Private
Public Access
1
0

401 Commits

Author SHA1 Message Date
64d7394ffa gtk: fix attachment download race condition 2026-02-22 00:36:15 -08:00
69892a4d08 fix rpmspec 2026-02-22 00:24:36 -08:00
a4bd28b22c gtk: image sizing fix 2026-02-22 00:19:43 -08:00
335ded4752 gtk: auto versioning from git tags 2026-02-22 00:02:33 -08:00
d40eb1886e cargo spec fix for rpm 2026-02-21 23:55:50 -08:00
de06e449be make rpm: bandaid for old cargo-generate-rpm 2026-02-21 23:54:27 -08:00
a51ff2a7c2 daemon: Cargo: add deb copyright 2026-02-21 23:45:37 -08:00
7cf2724a75 gtk: implement get_attachment_fd and texture/attachment cache, viewport loading 2026-02-21 23:28:21 -08:00
f0ec6b8cb4 core: implement get_attachment_fd event for dbus, message limit for get_messages 2026-02-21 23:26:00 -08:00
f38702bc95 dbus: Smaller GetMessages buffers, avoid encoding attachment paths. 2026-02-21 22:22:09 -08:00
a52c2e0909 eds contact resolver: cache when no contact found 2026-02-21 21:40:12 -08:00
9765994f14 fix for transcript being cut off 2026-02-19 18:11:56 -08:00
5fd94489af kptui: simplify title 2026-02-13 16:58:35 -08:00
e807528466 kptui: get rid of unread count 2026-02-12 13:08:58 -08:00
7c117eb52e kptui: Unread indicator, highlight vs selected state 2026-02-12 13:06:01 -08:00
1febd91c2c kptui: handle some emacs like keyboard shortcuts 2026-02-11 14:48:00 -08:00
9a3c808095 kptui: message entry should scroll horizontally 2026-02-11 14:43:04 -08:00
6ccef24512 gtk: flatpak manifest 2025-12-15 01:02:17 -08:00
61c1b690ba Adds deb building scripts 2025-12-15 00:56:24 -08:00
2f58283e26 Adds cross-compiling support for arm/raspi 2025-12-15 00:32:46 -08:00
be2e3ea9d9 gtk: add split view navigation stack support 2025-12-14 18:49:38 -08:00
fc69c387c5 kptui: organize client code into kordophoned-client 2025-12-14 18:06:14 -08:00
68bb94aa0b kptui: initial commit 2025-12-14 17:50:37 -08:00
f4402292a1 gitignore: add target 2025-12-14 17:10:25 -08:00
e650cffde7 osx: mark as read when hovering over window 2025-09-25 00:20:19 -07:00
cbd9dccf1a README: trim 2025-09-12 18:26:57 -07:00
1a5f13f2b8 osx: implements quicklook 2025-09-12 18:17:58 -07:00
87e986707d osx: update kpd 2025-09-12 16:07:31 -07:00
b5ba0b1f7a Merge branch 'wip/local_ids'
* wip/local_ids:
  first attempt at trying to keep track of locally send id
2025-09-12 15:58:50 -07:00
bc51bf03a1 osx: better scroll view management 2025-09-12 15:58:34 -07:00
8304b68a64 first attempt at trying to keep track of locally send id 2025-09-12 12:04:31 -07:00
6261351598 osx: wiring for opening a new window, but not connected to gesture yet
when I add `.tapGesture(count: 2)` to list items, this seems to block
single clicks because SwiftUI sucks. Need to find a better way to invoke
this.
2025-09-11 15:33:56 -07:00
955ff95520 osx: name app "Kordophone" instead of kordophone2 2025-09-11 15:33:31 -07:00
754ad3282d Merge branch 'wip/attachment_mime'
* wip/attachment_mime:
  core: attachment store: limit concurrent downloads
  core: attachment mime: prefer jpg instead of jfif
  wip: attachment MIME
2025-09-10 14:41:36 -07:00
f901077067 osx: some minor fixes 2025-09-10 14:41:24 -07:00
778d4b6650 core: attachment store: limit concurrent downloads 2025-09-10 14:23:02 -07:00
e8256a9e57 core: attachment mime: prefer jpg instead of jfif 2025-09-10 14:06:54 -07:00
4e8b161d26 wip: attachment MIME 2025-09-10 13:48:27 -07:00
74d1a7f54b osx: try badging icon for unread 2025-09-09 18:54:14 -07:00
4b497aaabc osx: linkify text, enable selection 2025-09-09 15:45:50 -07:00
6caf008a39 osx: update kordophoned binary 2025-09-09 13:40:43 -07:00
d20afef370 kpcli: updates: print error on error 2025-09-09 13:36:35 -07:00
357be5cdf4 core: HTTPClient: update socket should just automatically retry on subsqeuent auth success 2025-09-09 13:33:13 -07:00
4db28222a6 core: HTTPClient: event stream should just automatically retry after auth token 2025-09-09 13:30:53 -07:00
469fd8fa13 gtk: add Makefile for making rpm 2025-09-07 18:32:57 -07:00
f09f45a66f gtk: rpmspec update 2025-09-07 18:17:52 -07:00
481ac7357c gtk: attempt to fix rpmspec 2025-09-07 18:10:39 -07:00
27c6ac1c47 Remove API references from READMEs 2025-09-06 19:55:04 -07:00
acbcf2f992 AI generated READMEs 2025-09-06 19:52:37 -07:00
577e8491c9 Add 'osx/' from commit '46755a07ef2e7aa9852d74c30e2c12f9fe8f2278'
git-subtree-dir: osx
git-subtree-mainline: 034026e88a
git-subtree-split: 46755a07ef
2025-09-06 19:38:26 -07:00
034026e88a Add 'android/' from commit '5d26ea956906cd31a6cc37e79b0a4cac77b3118b'
git-subtree-dir: android
git-subtree-mainline: 7fe2701272
git-subtree-split: 5d26ea9569
2025-09-06 19:37:14 -07:00
7fe2701272 Add 'server/' from commit '800090542d91beae40bc81fc41b67ba61c47da77'
git-subtree-dir: server
git-subtree-mainline: 6a4054c15a
git-subtree-split: 800090542d
2025-09-06 19:36:27 -07:00
6a4054c15a Add 'mock/' from commit '2041d3ce6377da091eca17cf9d8ad176a3024616'
git-subtree-dir: mock
git-subtree-mainline: 8216d7c706
git-subtree-split: 2041d3ce63
2025-09-06 19:35:49 -07:00
8216d7c706 Add 'gtk/' from commit '7d0dfb455aa86245231b383a92e79b3c08a12d5e'
git-subtree-dir: gtk
git-subtree-mainline: c710c6e053
git-subtree-split: 7d0dfb455a
2025-09-06 19:34:30 -07:00
c710c6e053 Add 'core/' from commit 'b0dfc4146ca0da535a87f8509aec68817fb2ab14'
git-subtree-dir: core
git-subtree-mainline: a07f3dcd23
git-subtree-split: b0dfc4146c
2025-09-06 19:33:33 -07:00
a07f3dcd23 Seed commit 2025-09-06 19:33:27 -07:00
46755a07ef Implements attachment uploading 2025-09-03 22:38:49 -07:00
b0dfc4146c Add TLS support 2025-09-03 22:23:45 -07:00
b2f8abfbff Reduce animations in transcript 2025-09-03 17:08:54 -07:00
7675894ba7 daemon update 2025-09-03 17:08:35 -07:00
fc02d86a68 UI support for uploading image attachments 2025-08-30 21:52:30 -06:00
236070ccc9 Better attachment placeholders 2025-08-30 00:39:35 -06:00
0595fbc651 This ended up being pebkac 2025-08-29 23:19:14 -06:00
44fa638b1c snoozer: try another auth method 2025-08-29 23:09:57 -06:00
8fcc7609b9 snoozer: fix auth 2025-08-29 23:08:37 -06:00
54f7f3a4db adds utilities > snoozer 2025-08-29 22:44:09 -06:00
8257b8dbd6 Handle xpc connection interruptions 2025-08-29 22:30:37 -06:00
92d5b99853 kordophone: better handling of url decoding errors 2025-08-29 22:08:56 -06:00
7992c03fb6 App icon, group member annotations, variable spacing 2025-08-29 21:47:51 -06:00
5f37f82a33 Fix multi-window, turn off sandboxing 2025-08-29 19:59:11 -06:00
41c5776d98 Embed mach service 2025-08-29 19:45:27 -06:00
54df338ce0 Some minor changes 2025-08-29 18:49:00 -06:00
0128723765 xpc: Fixes file handle explosion - drop fd after its copied via xpc 2025-08-29 18:48:16 -06:00
5da92a90d4 Adds keychain support 2025-08-29 15:12:54 -06:00
eb4426e473 UpdateMonitor: dont leak convo in log 2025-08-29 15:12:23 -06:00
c1507e9ee1 Merge branch 'wip/macos-xpc'
* wip/macos-xpc: (23 commits)
  auth: try switching to platform agnostic auth store
  daemon: fix crash when misconfigured
  xpc: better file descriptor handling
  xpc: adds OpenAttachmentFd
  xpc: full attachment data
  sync policy: only ignore empty bodies if there are no attachments
  xpc: include attachment guids
  cargo fmt
  xpc: Some cleanup
  xpc: refactor -- separate rpc impl and xpc glue
  xpc: refactor, less chatty logging
  xpc: Use reply port when replying to RPC messages
  cargo fmt
  xpc: implement signals
  xpc: implement rest of methods in kpcli except signals.
  cargo fmt
  xpc: Better type unpacking
  xpc: implement GetConversations
  xpc: kpcli: clean up client interface
  xpc: generic interface for dispatching methods
  ...
2025-08-25 01:01:56 -07:00
c30330a444 auth: try switching to platform agnostic auth store 2025-08-25 00:56:03 -07:00
402b5a5f80 autosyncing, appearance tweaks 2025-08-25 00:37:48 -07:00
f0fd738935 Settings, no password yet 2025-08-25 00:13:55 -07:00
f82123a454 daemon: fix crash when misconfigured 2025-08-25 00:09:57 -07:00
f0029d02e1 Implements attachment previewing 2025-08-24 23:38:35 -07:00
cc59fe4996 xpc: better file descriptor handling 2025-08-24 23:38:14 -07:00
eaa5966e99 xpc: adds OpenAttachmentFd 2025-08-24 23:20:25 -07:00
f2353461b3 xpc: full attachment data 2025-08-24 23:01:11 -07:00
f277fcd341 sync policy: only ignore empty bodies if there are no attachments 2025-08-24 19:46:28 -07:00
ee32a0398f xpc: include attachment guids 2025-08-24 19:46:16 -07:00
126a4cc55f Get swifty 2025-08-24 18:54:50 -07:00
b38df68eb2 Implements signals 2025-08-24 18:41:42 -07:00
3ee94a3bea Adds getting/sending messages 2025-08-24 17:58:37 -07:00
b5a2f318b4 initial commit 2025-08-24 16:24:21 -07:00
f239d1de19 cargo fmt 2025-08-24 16:20:14 -07:00
28738a1e92 xpc: Some cleanup 2025-08-24 16:19:56 -07:00
00bbc3b330 xpc: refactor -- separate rpc impl and xpc glue 2025-08-24 15:49:55 -07:00
73508bea9e xpc: refactor, less chatty logging 2025-08-24 15:34:05 -07:00
a93a773071 xpc: Use reply port when replying to RPC messages 2025-08-24 15:28:33 -07:00
fc62f0533d Initial Commit 2025-08-24 11:16:20 -07:00
06b27c041a cargo fmt 2025-08-24 11:04:41 -07:00
da813806bb xpc: implement signals 2025-08-24 10:36:39 -07:00
16db2caacc xpc: implement rest of methods in kpcli except signals. 2025-08-23 20:13:33 -07:00
0b7b35b301 cargo fmt 2025-08-23 20:02:54 -07:00
6f90e1c749 xpc: Better type unpacking 2025-08-23 20:01:13 -07:00
b7fabd6c05 xpc: implement GetConversations 2025-08-23 19:48:49 -07:00
885c96172d xpc: kpcli: clean up client interface 2025-08-23 19:41:12 -07:00
8ff95f4bf9 xpc: generic interface for dispatching methods 2025-08-23 19:24:42 -07:00
e51fa3abeb kordophoned becomes a lib 2025-08-20 23:12:31 -07:00
e9bda39d8a xpc: hacky implementation of GetVersion 2025-08-10 21:48:44 -07:00
7d0dfb455a Dockerfile: build with fedora 40 for adwaita 2025-08-08 15:47:45 -07:00
201982170f Adds makefile and dockerfile for building rpms 2025-08-08 15:42:21 -07:00
54b76109c2 Fixes rpm build 2025-08-08 13:47:21 -07:00
8cdcb049cf rpm packaging, includes systemd service 2025-08-08 11:56:14 -07:00
James Magahern
911454aafb first pass at xpc impl 2025-08-01 12:26:17 -07:00
43b668e9a2 Fix linux build 2025-07-31 19:40:03 -07:00
James Magahern
c7d620c1b5 kpcli: finish separation of daemon interface 2025-07-31 19:30:54 -07:00
James Magahern
0e034898b2 kpcli fix stage 1 2025-07-31 19:19:29 -07:00
James Magahern
8115f94121 kordophoned sans kpcli building on macos 2025-07-31 19:16:44 -07:00
5fa6c86a17 adds readme 2025-07-15 19:00:11 -07:00
356a1b85b9 version bump 2025-07-15 18:59:03 -07:00
3e43bd1434 repository: auto start service if not already running 2025-07-15 18:58:13 -07:00
c878141e61 rpmspec: these may not be necessary. 2025-07-15 18:43:53 -07:00
44bc7c0cb4 v1.0.0 2025-07-15 18:42:28 -07:00
349a644b0e Desktop/icon files, rpm dist 2025-07-15 18:42:05 -07:00
742703cb8e Version: 1.0.0 2025-07-15 18:04:11 -07:00
3197814098 Implement hybrid versioning approach 2025-07-15 16:39:57 -07:00
21703b9f8e AttachmentStore: less chatty logging 2025-06-27 00:54:40 -07:00
6e14585a12 EDS: Found the issue where address book sometimes doesn't load -v
The wrong source was getting selected. Not sure if this one is always a
decoy, there might be others that we aren't supposed to use. Happy that
it's working now though.
2025-06-27 00:48:20 -07:00
b043ff6f08 eds: still not able to resolve sometimes, some AI generated attempts at solving 2025-06-26 20:44:24 -07:00
9e3e6dc66f ContactResolver: implement in-memory cache for positive results 2025-06-26 18:50:58 -07:00
bb74604a74 transcript: show sender annotation if interrupted by date annotation 2025-06-26 18:48:52 -07:00
e73cf321c0 Add normalization for eds resolver 2025-06-26 18:37:23 -07:00
5a399cc6ca weird: need to filter out bidi control characters from sender handles from server 2025-06-26 18:33:08 -07:00
f6bb1a9b57 Don't overwrite already resolved participants, better naming of keys 2025-06-26 18:23:15 -07:00
bb19db17cd Started working on contact resolution 2025-06-26 16:23:53 -07:00
9f84969ff5 Multi-window support! 2025-06-18 18:21:11 -07:00
3379198940 Add double click gesture on image bubbles to open 2025-06-18 18:07:59 -07:00
0dece34012 Adds link clicking support 2025-06-18 17:36:32 -07:00
4ebd310b7a fix bug with clearing locked selection bubble 2025-06-18 17:01:01 -07:00
ccfea2883c Enables selection of bubbles using an invisible text view 2025-06-18 16:50:14 -07:00
3b6666cfc2 Mark conversation as read on movement 2025-06-18 15:32:37 -07:00
3b30cb77c8 Implements mark as read 2025-06-18 15:02:04 -07:00
a70adbb7f1 Implements marking conversations as read when clicked on 2025-06-18 15:00:54 -07:00
4170f13092 fixes crash when trying to copy image 2025-06-18 01:49:33 -07:00
d33b50cfb5 transcript: add copy right click action 2025-06-18 01:36:29 -07:00
fa6c7c50b7 Refactor: serverimpl -> dbus::agent, clean up main.rs 2025-06-18 01:03:14 -07:00
f0b7cff226 Remove this check: attachments could have no body 2025-06-17 20:52:21 -07:00
e1c579d23b Text size: this really should just read the default 2025-06-17 00:57:22 -07:00
16102f9f94 Fix animation clamping issues 2025-06-17 00:53:37 -07:00
54ca001892 Adds incoming bubble animations 2025-06-17 00:47:03 -07:00
2041d3ce63 Fix websocket 2025-06-17 00:39:50 -07:00
c70ae00d5b transcriptview perf: only draw the items that are actually visible. 2025-06-16 20:09:56 -07:00
032573d23b cargo fmt 2025-06-16 19:26:13 -07:00
75fe4d4608 fix all warnings 2025-06-16 19:25:24 -07:00
800090542d updates: should really return 401 for bad auth instead of 404 2025-06-16 19:18:14 -07:00
9d591dffc5 Try to resolve daemon hang when changing settings 2025-06-16 19:06:35 -07:00
45aaf55804 dbus: filter attachment characters here. not ideal... 2025-06-16 18:52:58 -07:00
2db0e3136e Some metrics tweaks for my laptop 2025-06-14 00:14:58 -07:00
31eeb8659a fix update reconnect notification when waking from sleep 2025-06-13 19:01:00 -07:00
4d466f0d26 re-fix the issue of accumulating message list models 2025-06-13 17:48:50 -07:00
b2049fb432 Workaround for empty server messages (typing indicator) 2025-06-13 17:47:29 -07:00
bb04bc4352 Remove this install step now that we static link 2025-06-13 17:42:55 -07:00
aace2a8dfc messagelist: actually implement before/after properly 2025-06-13 17:42:05 -07:00
9c013c3702 Strip space before sending messages 2025-06-13 17:14:23 -07:00
741932c67d Implement resync after update monitor reconnect 2025-06-13 17:13:04 -07:00
45d873907f bugfixes, better handling of server url changes 2025-06-13 17:11:29 -07:00
dece6f1abc daemon: update monitor: implements ping/pong (required server changes) 2025-06-13 16:45:28 -07:00
7cceb5b92d remove logging from pingpong 2025-06-13 14:56:40 -07:00
3f03937ca4 guessing at this point 2025-06-13 14:54:51 -07:00
d706435103 pingpong prevent superclass from handling rest of ping frame 2025-06-13 14:50:13 -07:00
e1ec237053 now actually handle pong 2025-06-13 14:47:47 -07:00
a9c2f5d93e pingpong: need to handle the first part of the frame first 2025-06-13 14:44:03 -07:00
1dc6f0ec1b add logging 2025-06-13 14:39:24 -07:00
e97edc10b7 Apparently need -ObjC for loading categories from a static library. 2025-06-13 14:36:00 -07:00
283e6c2218 Revert "static link categories"
This reverts commit f040b95827.
2025-06-13 14:35:26 -07:00
f040b95827 static link categories 2025-06-13 13:57:06 -07:00
7352efbcfd Switch to statically linking CocoaHTTPServer 2025-06-13 13:35:42 -07:00
78eb946109 prototype of ping pong websocket (ai generated) 2025-06-13 13:26:15 -07:00
1a5bb874dc rpath manipulation: This may not be necessary anymore 2025-06-13 13:17:31 -07:00
1420d96a20 TranscriptView: ellipsize title 2025-06-13 12:26:57 -07:00
4f40be205d Adds CONTENT_LENGTH workaround for CocoaHTTPServer bug 2025-06-12 21:19:47 -07:00
269271835f bug fixes 2025-06-12 20:47:36 -07:00
ff03e73758 plumb attachment guids for sendmessage 2025-06-12 20:36:40 -07:00
2d43b87839 add CLAUDE.md 2025-06-12 20:36:18 -07:00
6fb88c3a0d Switch from Entry to TextView for multiline, paste support for attachments 2025-06-12 20:35:56 -07:00
137da5b3d1 Finish daemon support for uploaded attachments + sending 2025-06-12 19:46:53 -07:00
f3e59b9951 Adds ui support for attachments, results not yet connected to daemon 2025-06-12 19:26:49 -07:00
8dbe36fde1 Repository: add support for attachment uploading 2025-06-12 18:13:59 -07:00
930f905efc Perf optimizations, recommended by o3 2025-06-12 18:09:58 -07:00
2f4e9b7c07 Implements attachment uploading 2025-06-12 17:58:03 -07:00
501bd3f604 Add back message list watching, support attachments without metadata 2025-06-12 17:54:09 -07:00
54790d1d70 Implements attachments display in transcript 2025-06-06 20:03:02 -07:00
4ddc0dca39 Notify when attachment download succeeds, fix deadlock in attachment store 2025-06-06 20:02:09 -07:00
1d3b2f25ba cargo fmt 2025-06-06 16:39:31 -07:00
8cd72d9417 cargo fix 2025-06-06 16:35:51 -07:00
9e8c976a0e remove some unused builder code in daemon::models::message 2025-06-06 16:30:22 -07:00
77e1078d6a plumb all known attachments via dbus if known 2025-06-06 16:28:29 -07:00
1a2dad08a5 adds image bubble layout for attachments 2025-06-06 14:33:40 -07:00
2e55f3ac9e dbus: remove some signals I wont implement 2025-06-05 20:21:30 -07:00
cbc7679f58 AttachmentStore now has its own runloop, can download attachments 2025-06-05 20:19:34 -07:00
595c7a764b adds CLAUDE hints 2025-05-28 14:57:12 -07:00
e55b29eb4d plub through attachment guids via messages 2025-05-26 16:52:38 -07:00
2b5df53cc3 better d-bus interface for attachments 2025-05-26 16:19:26 -07:00
831e490eb4 Started to factor out DbusRegistry from Endpoint 2025-05-26 15:49:29 -07:00
c02d4ecdf3 broken: started working on attachment dbus object, but order of endpoint creation seems to matter, need to reuse more parts 2025-05-25 18:52:18 -07:00
0d4c2e5104 Started working on attachment store 2025-05-15 20:11:10 -07:00
77177e07aa kpcli: fix for update data structure changes 2025-05-14 17:43:28 -07:00
83eb97fd9c websocket: automatically reconnect if not heard from for a while 2025-05-14 17:39:23 -07:00
1ed7f5bda3 Fix retain cycles 2025-05-14 17:37:23 -07:00
4ad9613827 temporary solution for infinite sync: just remember the times 2025-05-12 20:46:26 -07:00
7339b49759 Encoding: need to include associated chat item guids.
Beginning to understand associated chat items (reactions, etc).
2025-05-09 23:39:16 -07:00
95c2e855dd some better logging around websocket connections 2025-05-09 22:25:57 -07:00
f377bbb7f9 Change unread indicator from number to icon 2025-05-04 00:49:21 -07:00
4aa6e53e3a adjust date attribution time a bit 2025-05-04 00:17:50 -07:00
819b852c1f Fixes bug where updates can cause a sync loop 2025-05-04 00:15:13 -07:00
f38e2a9798 Nicer app menu 2025-05-04 00:14:00 -07:00
d4cc3358b7 reorg: message-list-view -> transcript-view 2025-05-04 00:13:47 -07:00
3e9e8fb3d0 transcriptview: reset scroll position when model changes 2025-05-03 23:39:21 -07:00
7ccdbced30 Show conversation display name in title 2025-05-03 23:26:53 -07:00
786d982ce0 Add sender annotations 2025-05-03 23:19:15 -07:00
dd91746310 reorg: message-list -> transcript 2025-05-03 22:47:56 -07:00
d3dfffd652 show dates in transcript 2025-05-03 22:41:51 -07:00
8e87c2bce2 Less chattier log when syncing 2025-05-03 22:13:03 -07:00
21c926456d reorg: message layout becomes interface for other types of chat items (like date) 2025-05-03 22:12:26 -07:00
d843127c6d daemon: maintain outgoing message reference so model is consistent 2025-05-03 21:45:53 -07:00
518608a04e attempt to resolve chatter problems 2025-05-03 21:45:17 -07:00
0d61b6f2d7 daemon: adds conversation list limit, fixes auth saving in db auth store 2025-05-03 18:19:48 -07:00
e44120712f fixes for very large conversation lists 2025-05-03 18:19:17 -07:00
0f565756df adds setting screen 2025-05-03 01:11:26 -07:00
26d54f91d5 implements authentication/token retrieval/keyring 2025-05-03 01:06:50 -07:00
ecf66131e9 server: enqueue message update after sending. real server does this 2025-05-02 15:52:01 -07:00
ef0312ccbd ~buzzert/Kordophone#9: gtk v2: Conversation selected state lost when reloading 2025-05-02 15:51:43 -07:00
461c37bd20 daemon: updatemonitor: dont sync convo list on conversation update, only message sync 2025-05-02 15:46:33 -07:00
410182eab8 implements sending 2025-05-02 15:09:12 -07:00
2519bc05ad daemon: implements post office 2025-05-02 14:22:43 -07:00
07b55f8615 client: implements send_message 2025-05-02 12:03:56 -07:00
05b4beb2fb fix bug where mock server crashes when sending more than one update 2025-05-01 20:48:43 -07:00
2106bce755 daemon: reorg 2025-05-01 20:45:20 -07:00
2314713bb4 daemon: incorporate update monitor in daemon activities 2025-05-01 20:36:43 -07:00
1c2f09e81b clippy 2025-05-01 18:08:04 -07:00
f6ac3b5a58 client: implements event/updates websocket 2025-05-01 18:07:18 -07:00
13a78ccd47 adds the ability to clear db 2025-05-01 01:08:24 -07:00
fd4c43d585 client: actually do authentication properly 2025-05-01 01:02:36 -07:00
f80d1a609b attempt to resolve scaling issues on 2x displays 2025-04-30 21:19:24 -07:00
a7e88bd3c3 wire up message loading 2025-04-30 19:50:36 -07:00
bdf76ca725 generators: sometimes generate messages from me as well 2025-04-30 19:46:09 -07:00
4c7c31ab8d implement bubble view 2025-04-30 19:12:00 -07:00
e976b3db4c initial scaffolding for inverted, custom message list 2025-04-30 15:58:47 -07:00
3e1fa63fdf reorg: separate dbus code out of conversation list model and into repository 2025-04-30 15:19:44 -07:00
56fba9b72c Use generated dbus interface rather than editing it every time 2025-04-30 14:53:17 -07:00
59cfc8008b dbus: remove duplicate property for credential item 2025-04-30 14:51:49 -07:00
907a69385d reorg 2025-04-30 14:24:33 -07:00
101694ddbc Some fixups for the badge 2025-04-28 18:40:16 -07:00
7200ae54e4 Adds the ability to sync just one conversation 2025-04-28 18:39:52 -07:00
a1250c8ebe adds dbus messaging for getting conversations. needs org 2025-04-28 18:21:02 -07:00
4eff88a51b initial commit: barebones 2025-04-28 17:29:32 -07:00
e7d837d68c cargo clippy/fix 2025-04-28 16:06:51 -07:00
c189e5f9e3 daemon: add support for getting messages from db 2025-04-28 16:00:04 -07:00
9c245a5b52 client: Started working on ability to sync messages after last known message 2025-04-28 15:17:58 -07:00
6375284d9e daemon: copy audit, cleanup 2025-04-27 23:27:21 -07:00
1e9b570993 devises a strategy for signals 2025-04-27 22:44:05 -07:00
cecfd7cd76 implements settings, conversation dbus encoding 2025-04-27 18:07:58 -07:00
49f8b81b9c daemon: Token store 2025-04-27 14:01:19 -07:00
84f782cc03 daemon: implement solution for background sync 2025-04-27 13:40:59 -07:00
22554a7644 daemon: reorg: use channels for comms instead of copying daemon arc/mutex 2025-04-27 12:53:45 -07:00
ef74df9f28 daemon: start working on events. notes:
Probably need to make the locking mechanism more granular. Only lock the
database during db writes, see if we can do multiple readers and a
single writer. Otherwise, the daemon will not be able to service
requests while an event is being handled, which is not good.
2025-04-25 21:43:36 -07:00
82192ffbe5 daemon: setting foundation for client creation 2025-04-25 20:02:18 -07:00
fe32efef2c daemon: scaffolding for settings / sync 2025-04-25 18:02:54 -07:00
0c6b55fa38 kordophoned: better daemon bootstrapping 2025-04-25 16:54:37 -07:00
b1f171136a refactor: with_repository/with_settings 2025-04-25 16:35:10 -07:00
89c9ffc187 cleanup 2025-04-25 15:48:50 -07:00
f7d094fcd6 reorg: split repo / database so settings can use db connection as well 2025-04-25 15:42:46 -07:00
James Magahern
d0e1f51b6b adds some clues about how to implement tapbacks 2025-04-07 18:59:53 -07:00
dd9025cc10 daemon: main reorg 2025-02-12 00:32:44 -08:00
68ff158d6c kordophoned: reorg: server impl in separate file, skeleton for conversations 2025-02-12 00:26:32 -08:00
6a7d376aa9 kpcli: add daemon messaging support 2025-02-12 00:10:33 -08:00
fddc45c62a Adds kordophoned, basic dbus interface 2025-02-11 23:15:24 -08:00
16c202734c kpcli: db: add support for printing messages table 2025-01-20 22:23:18 -08:00
bfc6fdddc1 proj: Fix warnings 2025-01-20 22:13:44 -08:00
5d3d2f194a kpcli: adds support for querying messages 2025-01-20 22:05:53 -08:00
146fac2759 kordophone-db: adds support for the Messages table 2025-01-20 22:05:34 -08:00
a8104c379c kordophone: add support for /messages 2025-01-20 19:43:21 -08:00
793faab721 kpcli: adds 'db' subcommand for interacting with the database 2025-01-08 13:32:55 -08:00
5d26ea9569 Background UpdateMonitorService: specify intent to Android OS 2024-12-30 19:21:23 -08:00
89f8d21ebb clippy/cleanup 2024-12-21 18:01:00 -08:00
53d4604b63 remove unused Date model 2024-12-21 17:52:11 -08:00
ab44a169e6 reorg: move tests to separate module 2024-12-21 17:50:47 -08:00
8f523fd7fc reorg: separate db::records from insertable models 2024-12-21 17:09:37 -08:00
c4c6e4245d chatdatabase: implements all_conversations with participant zippering 2024-12-21 16:34:47 -08:00
f79cbbbc85 kordophone-db: switch to diesel for more features 2024-12-14 19:03:27 -08:00
86601b027a kordophone-db: participants, but still need to "upsert" these
Might move to SeaORM instead of trying to do this with microrm
2024-12-14 12:53:44 -08:00
fac9b1ffe6 adds kordophone-db 2024-12-08 21:12:17 -08:00
75d4767009 kpcli: reorg subcommands 2024-12-08 15:08:15 -08:00
1eb08ba464 Makefile for muscle memory 2024-12-08 15:06:35 -08:00
0e8b8f339a kpcli: client: adds printing of conversations 2024-11-10 19:40:39 -08:00
6b9f528cbf start working on kpcli 2024-11-09 17:36:25 -08:00
9007b4503f JSON encoding error for super long messages 2024-10-24 22:27:19 -07:00
030e86e205 uploadAttachment: [Security] sanitize incoming filename 2024-10-24 21:28:31 -07:00
James Magahern
b7312bccb9 adds /resolveHandle for resolving handles 2024-10-01 19:52:21 -07:00
a11dd27ef8 Vanity domain update: drop v2 2024-09-06 01:12:11 -07:00
c1fef50c0c Update module with vanity domain 2024-09-06 01:10:45 -07:00
da36d9da91 Clippy, warnings fix 2024-06-14 20:26:56 -07:00
cabd3b502a Retry auth automatically, remove tower dep 2024-06-14 20:23:44 -07:00
0dde0b9c53 clippy 2024-06-01 18:17:57 -07:00
a2caa2ddca prepare for tower middleware adoption 2024-06-01 18:16:25 -07:00
cf4195858e Started work on http server 2024-04-24 23:41:42 -07:00
48dcf9daca Fix tests 2024-04-21 23:09:37 -07:00
3e878ced4e reorg 2024-04-21 15:14:16 -07:00
0b2811dc9f Initial commit 2024-04-20 18:17:55 -07:00
634540a703 ~buzzert/Kordophone#8: android: Show spinner in full screen attachment viewer 2024-04-15 22:21:24 -07:00
d2afecafcf Try only synchronizing messages after last GUID 2024-04-15 19:38:40 -07:00
50e9971694 Adds app icon 2024-04-15 18:41:39 -07:00
b47132fd05 attachments: handle sending messages with attachments 2024-04-07 21:50:08 -07:00
95b358e66e attachments: Should be /attachment, not /fetchAttachment 2024-04-07 21:25:59 -07:00
a1349eff1b attachments: Add a generated attachment conversation 2024-04-07 21:06:03 -07:00
fa76c7eac1 Adds attachment fetching/uploading 2024-04-07 20:22:38 -07:00
63876104aa server: enforce auth for updates 2024-04-04 22:59:59 -07:00
58c84f6472 MBIMFetchAttachmentOperation: Add cache headers 2024-03-23 16:49:33 -07:00
c666083e4b Fix tests: Need to write proper date unmarshalling also 2024-03-01 23:01:57 -08:00
413fe338ca Logging: these need to be public oslogs 2024-01-07 18:14:50 -08:00
72527088cc logging: Use os log if running in launchd 2024-01-07 18:12:17 -08:00
c65803845b FetchAttachment: need to check for some edge cases wrt preview generation 2024-01-07 18:11:59 -08:00
James Magahern
831636216d Ensure all dates returned are ISO8601 2024-01-05 16:26:19 -08:00
a8043e53b3 Update SendMessage for new protocol 2024-01-03 23:46:10 -08:00
James Magahern
147dc15d1d SendOperation: Return whole message, not just GUID. 2024-01-02 18:14:31 -08:00
d8ca07f92a Actually check authentication 2023-12-10 19:51:18 -08:00
416949095c Update sendMessage to return inserted guid for new protocol 2023-12-10 17:45:40 -08:00
James Magahern
87d65e294b sendMessage: return guid after sending. This guid is stable 2023-12-05 15:41:07 -08:00
f222078a9d Identify ourselves as KordophoneMock 2023-12-02 22:23:41 -08:00
a0e8e049f0 Updates: need to have one channel per observer 2023-08-24 00:30:57 -07:00
7895a3a2e0 Fix bug where generated messages have wrong guids 2023-08-23 21:29:13 -07:00
b92d1a2892 server: implements /updates websocket 2023-08-13 00:33:46 -07:00
e9a9d1c064 prompt: adds 'version' command 2023-08-10 00:42:49 -07:00
56ad3a49ea Kordophone-2.6: lastMessage in /conversations 2023-08-10 00:42:21 -07:00
b92c683a0e /conversations: encode last message also 2023-08-10 00:28:20 -07:00
f13a809f1e PerformMessageQuery: Add comment regarding sort assumption 2023-08-08 21:39:43 -07:00
6a0b7ca225 Implement beforeMessageGUID/afterMessageGUID/beforeDate 2023-08-08 21:38:04 -07:00
5c9f580dff Cool ASCII art welcome message 2023-08-08 21:37:51 -07:00
b77020e23c server: implements /markConversation 2023-07-19 12:10:25 -06:00
1fce2c7cb3 Implements pollUpdates 2023-07-19 11:58:13 -06:00
7611bedef7 conversation: adds Equal() 2023-07-19 10:56:49 -06:00
James Magahern
37ff0b375f send: must be performed on main queue 2023-07-06 15:27:31 -07:00
James Magahern
d071e68a56 Security: adds authentication to updates websocket operation 2023-07-06 15:27:12 -07:00
ac27ac2d14 prompt: Implements receive message 2023-06-23 00:56:06 -07:00
943f52ac45 server: Implements sendMessage 2023-06-23 00:44:25 -07:00
06046ac266 prompt: adds ls, help, mark 2023-06-23 00:32:17 -07:00
191cffd4cf web/server: stub /pollUpdates 2023-06-22 23:55:46 -07:00
732e9b9667 generator: Generate names deterministically 2023-06-22 23:45:43 -07:00
James Magahern
a4c25df183 Prompt: adds interactive prompt (that does nothing right now) 2023-06-22 12:03:37 -07:00
James Magahern
27de41ddb2 Retab 2023-06-22 11:06:18 -07:00
James Magahern
84dbb7f006 Switch to zerolog 2023-06-22 11:03:00 -07:00
3613aac4c1 Log API endpoints 2023-06-19 20:34:06 -07:00
64c5169542 server: messages: add TODO regarding optional params 2023-06-19 18:32:36 -07:00
2d415a1170 messages: Implements /messages API 2023-06-19 18:31:05 -07:00
cdf3d922f7 Better generated names and messages 2023-06-19 12:57:21 -07:00
63ac783e18 conversation: DisplayName 2023-06-18 13:12:06 -07:00
6bbcf8cc63 Authentication: Implements authentication 2023-06-18 13:11:51 -07:00
53870e25a9 server: Return proper version 2023-06-16 23:38:48 -07:00
a2b14c88ea Initial commit: conversaions, status, version 2023-06-16 23:35:41 -07:00
James Magahern
2f5d50188b Adds websocket updates via the /updates endpoint 2023-01-17 16:16:23 -08:00
James Magahern
56ae7982c6 Last message preview uses imagent provided description 2023-01-12 17:00:18 -08:00
James Magahern
bc9e4f52b4 PreviewURL is nil on old macOS for some reason 2022-12-20 16:43:45 -08:00
James Magahern
3082c4ab19 Adds support for image previews
Just need to append ?preview=1 to attachment fetch operation.
2022-12-20 16:29:26 -08:00
ba8f76f4bd versionoperation: don't require auth 2022-08-03 19:55:04 -07:00
James Magahern
e5b78d62f0 Adds "status" operation 2022-08-03 17:28:23 -07:00
James Magahern
3ca9abcccd Adds "version" operation 2022-08-03 17:27:15 -07:00
James Magahern
cad3425327 MessagesList: Also adds support for afterMessageGUID 2022-08-03 17:13:01 -07:00
James Magahern
83ba072a9d RestrictedEntitlements: Maybe okay for "Debug" to make it easier during development 2022-08-03 16:58:05 -07:00
James Magahern
bd01480ad6 Don't build using restricted entitlements by default. 2022-08-03 16:56:53 -07:00
James Magahern
c7087a394e MessagesList: Add support for beforeMessageGUID and beforeDate 2022-08-03 16:52:39 -07:00
ebad248c1c Adds conversation delete option 2022-05-25 22:34:19 -07:00
e161eedef3 CORS 2022-05-25 21:27:13 -07:00
7a3303da06 Auth: Use Set-Cookie for auth token 2021-07-06 23:41:51 -07:00
641e4c53fa Add Makefile for ez installing 2021-07-06 23:41:39 -07:00
James Magahern
4d51ba7dd2 Auth: adds JWT bearer auth via /authenticate.
Works in addition to digest auth
2021-07-06 22:52:33 -07:00
James Magahern
f64ffcb8cc Add port number option 2021-06-14 21:40:55 -07:00
James Magahern
3c99b647d2 GPG is too much trouble for the access file 2021-06-12 17:44:31 -07:00
bb169c3e1c Adds launchd services 2021-06-06 23:39:24 -07:00
James Magahern
61384cff26 Remove more internal references 2019-12-16 17:35:19 -08:00
James Magahern
f006af9863 Upgrade to recommended settings 2019-12-16 17:30:47 -08:00
James Magahern
4f7a6d1b87 Move off of macosxinternal sdk
This moves kordophone off of using the internal SDK and switches to using class dumped headers instead.
2019-12-16 17:29:53 -08:00
6c089f737b Not sure why, but mimeType is not always populated 2019-03-03 14:23:54 -08:00
James Magahern
de852a926d Basic authentication support 2019-01-23 20:31:43 -08:00
James Magahern
e6314b0f80 Addendum to README regarding self-signed ssl certs 2019-01-22 23:32:35 -08:00
James Magahern
0cb907e2ad Experimental SSL support 2019-01-22 23:31:36 -08:00
James Magahern
90775ebbba Attachment uploading support 2019-01-16 14:17:31 -08:00
James Magahern
6aaa2ff5b3 Plumb guid down for deduplication on the conversation level 2019-01-04 13:08:28 -08:00
James Magahern
dba4910a82 Batch updates, and fixing bug where daemon would crash when accessing IMCore stuff from different threads 2018-11-21 15:51:51 -07:00
James Magahern
3186f1948a Attachments support! 2018-11-21 01:38:43 -07:00
James Magahern
4f5cd058c5 Updates to sending and message sequences 2018-11-20 19:57:35 -07:00
James Magahern
ddec4be8d6 Supports polling for updates 2018-11-17 01:07:55 -08:00
James Magahern
464e9fe22c Remove copy files phase for CocoaWebServer framework 2018-11-16 18:52:23 -08:00
1b12faddf8 Project cleanup around new submodule 2018-11-16 01:40:02 -08:00
182e240291 Removed GCDWebServer submodule 2018-11-16 01:32:09 -08:00
baffa7b035 Switches from GCDWebServer to CocoaHTTPServer so we can have HTTPS eventually 2018-11-16 01:30:38 -08:00
James Magahern
5ced6151c2 Some additional headers needed to decrypt the payload 2018-11-15 16:30:25 -08:00
James Magahern
6c9996dfa1 Encrypted server response 2018-11-15 15:08:39 -08:00
James Magahern
b92860b011 Some basic crypto laid down and tests 2018-11-15 14:50:40 -08:00
e08e9d738c Message preview and participant list 2018-11-14 23:12:41 -08:00
James Magahern
f462ee68ca Try to not use private entitlements 2018-11-13 22:39:18 -08:00
James Magahern
ce7e6e7dd8 Organize everything into operations 2018-11-13 12:29:15 -08:00
James Magahern
7a3dee7073 Bit of cleanup 2018-11-12 22:15:50 -08:00
James Magahern
2d0fd5b290 Moved stuff around and you can see convos, messages, and send messages! 2018-11-12 22:11:36 -08:00
James Magahern
58777807bc Initial commit
Message receiving works, sending sort of works
2018-11-12 20:10:46 -08:00
381 changed files with 38021 additions and 220 deletions

18
.gitignore vendored
View File

@@ -1,17 +1 @@
*.iml target/
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
.idea/

66
README.md Normal file
View File

@@ -0,0 +1,66 @@
# Kordophone Monorepo
Kordophone is an iMessage bridge: a lightweight server runs on a Mac and exposes an HTTP API so nonApple devices can send/receive iMessages. A set of clients (Android, Linux GTK, macOS Cocoa) talk to that API. A shared Rust library powers most clients, and a mock server helps local testing.
> Important: Interfacing with iMessage on macOS involves private APIs and restricted entitlements. See `server/README.md` for details and safety notes.
## Repository Layout
Toplevel projects in this monorepo:
- `server/` — macOS daemon that bridges to iMessage and exposes an HTTP REST API. Written in ObjectiveC/Cocoa. See `server/README.md`.
- `core/` — Rust workspace with the shared Kordophone client library and related tooling. Used by `gtk/` and `osx/`.
- `gtk/` — GTK4/Libadwaita Linux desktop client built on the Rust `core` library.
- `osx/` — macOS Cocoa client that uses the Rust `core` library and talks to the local Kordophone client daemon via XPC.
- `android/` — Android client. Currently implements its own API client (does not use the Rust library yet).
- `mock/` — Gobased mock server that emulates a Mac running the Kordophone server, for local development and tests.
Quick links:
- Server (mac daemon): `server/`
- Core (Rust workspace): `core/`
- GTK client (Linux): `gtk/`
- macOS client (Cocoa): `osx/`
- Android client: `android/`
- Mock server (Go): `mock/`
## How It Works
1. The macOS Kordophone Server (`server/`) runs on a Mac with iMessage and exposes an HTTP/JSON API and a WebSocket/updates channel.
2. Clients connect to that server to list conversations, fetch/send messages, upload/download attachments, and receive live updates.
3. A Rust library in `core/kordophone` implements the wire protocol and client behaviors. Linux/macOS clients use this library directly; Android currently ships a native Kotlin client.
4. The Rust client daemon (`core/kordophoned`) provides local caching and IPC (DBus on Linux, XPC on macOS) for GUI apps.
5. The `mock/` server simulates the server API for local development without a Mac.
## Getting Started
You can try the clients quickly against the mock server.
1) Run the mock server (no auth):
```bash
cd mock
go run ./... # or: make; ./kordophone-mock
```
The mock server listens on `http://localhost:5738`.
2) Point a client at the mock server:
- Android: open Settings in the app and set the server host to `http://10.0.2.2:5738` (Android emulator) or `http://<your-host-ip>:5738` if running on device. Disable auth unless you started the mock with `--auth`.
- GTK (Linux): build and run the GTK app from `gtk/` (see its README) and configure it to use the local daemon backed by the server URL.
- macOS (Cocoa): build the app in `osx/` (see `osx/README.md`).
To use a real Mac server instead, set your clients server URL to the host running `server/` with the correct scheme/port and authentication.
## Building
Below are brief notes. Each subprojects README has more detail.
- Server (macOS): open `server/MessagesBridge.xcodeproj` in Xcode. See `server/README.md` for entitlements, SSL, and running.
- Core (Rust): install Rust (stable) and run `cargo build` inside `core/`. See `core/README.md`.
- GTK (Linux): see `gtk/README.md` for RPM build via `rpmbuild -ba dist/rpm/kordophone.spec`.
- macOS (Cocoa): open `osx/kordophone2.xcodeproj` in Xcode. See `osx/README.md`.
- Android: open `android/` in Android Studio and build. See `android/README.md` for configuration.
- Mock server (Go): `cd mock && go run ./...` or `make`.

17
android/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
.idea/

View File

46
android/README.md Normal file
View File

@@ -0,0 +1,46 @@
# Kordophone Android Client
Android client for the Kordophone iMessage bridge. This app connects to a running Kordophone server over HTTP/JSON and streams updates.
Note: This client currently implements its own API layer in Kotlin and does not yet use the shared Rust `core` library.
## Build & Run
Requirements:
- Android Studio (AGP 8.x)
- JDK 8+ toolchain (project uses Java 8 bytecode)
Steps:
1. Open `android/` in Android Studio.
2. Sync Gradle and build the project.
3. Run the `app` configuration on an emulator or device.
The app targets `minSdk 30`, `targetSdk 33` and uses Jetpack Compose.
## Configure Server
Set the server host and optional Basic Auth from the apps Settings screen.
- Server URL for local mock (emulator): `http://10.0.2.2:5738`
- Server URL for local mock (device): `http://<your-host-ip>:5738`
- Credentials: match the server if auth is enabled (mock supports `--auth`).
Settings are persisted using `EncryptedSharedPreferences`.
## Modules
- `app/` — UI (Compose), DI (Hilt), app wiring
- `backend/` — Kotlin API client, DB/cache (Realm), models, tests
## Testing with the Mock Server
Start the mock server in the repo root:
```bash
cd mock
go run ./... # or: make; ./kordophone-mock
```
Set the apps server URL to the mock address and run. The mock implements the Kordophone server API; see centralized API documentation (planned under `api/`).

View File

@@ -40,7 +40,10 @@
android:value="" /> android:value="" />
</activity> </activity>
<service android:name=".UpdateMonitorService" /> <service
android:name=".UpdateMonitorService"
android:foregroundServiceType="dataSync"
android:exported="false" />
</application> </application>
</manifest> </manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,6 +1,5 @@
package net.buzzert.kordophonedroid.ui.attachments package net.buzzert.kordophonedroid.ui.attachments
import androidx.compose.foundation.Indication
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -8,16 +7,15 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Scaffold import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel import coil.compose.SubcomposeAsyncImage
import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import net.buzzert.kordophonedroid.ui.LocalNavController import net.buzzert.kordophonedroid.ui.LocalNavController
import net.buzzert.kordophonedroid.ui.theme.KordophoneTopAppBar import net.buzzert.kordophonedroid.ui.theme.KordophoneTopAppBar
@@ -41,12 +39,19 @@ fun AttachmentViewer(attachmentGuid: String) {
Column(modifier = Modifier.padding(padding)) { Column(modifier = Modifier.padding(padding)) {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
AsyncImage( SubcomposeAsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)
.data(data) .data(data)
.crossfade(true) .crossfade(true)
.build(), .build(),
contentDescription = "", contentDescription = "",
loading = {
Box {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.Center)
)
}
},
modifier = Modifier modifier = Modifier
.zoomable(zoomState) .zoomable(zoomState)
.fillMaxWidth() .fillMaxWidth()

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:fillColor="@color/black"
>
<gradient
android:startColor="#000"
android:endColor="#333"
android:angle="1.0"
/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 659 KiB

After

Width:  |  Height:  |  Size: 659 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#3D3D3D</color>
</resources>

Binary file not shown.

View File

@@ -97,6 +97,7 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
date = conversation.date date = conversation.date
unreadCount = conversation.unreadCount unreadCount = conversation.unreadCount
lastMessagePreview = conversation.lastMessagePreview lastMessagePreview = conversation.lastMessagePreview
lastMessageGUID = conversation.lastMessageGUID
} }
} catch (e: NoSuchElementException) { } catch (e: NoSuchElementException) {
// Conversation does not exist. Copy it to the realm. // Conversation does not exist. Copy it to the realm.
@@ -121,6 +122,10 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
} }
fun writeMessages(messages: List<ModelMessage>, conversation: ModelConversation, outgoing: Boolean = false) { fun writeMessages(messages: List<ModelMessage>, conversation: ModelConversation, outgoing: Boolean = false) {
if (messages.isEmpty()) {
return
}
val dbConversation = getManagedConversationByGuid(conversation.guid) val dbConversation = getManagedConversationByGuid(conversation.guid)
realm.writeBlocking { realm.writeBlocking {
messages messages
@@ -128,8 +133,17 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
.map { copyToRealm(it, updatePolicy = UpdatePolicy.ALL) } .map { copyToRealm(it, updatePolicy = UpdatePolicy.ALL) }
findLatest(dbConversation)?.let { findLatest(dbConversation)?.let {
it.lastMessagePreview = messages.last().displayText val lastMessage = messages.maxByOrNull { it.date }!!
it.date = messages.last().date.toInstant().toRealmInstant()
val lastMessageDate = lastMessage.date.toInstant().toRealmInstant()
if (lastMessageDate > it.date) {
it.lastMessageGUID = lastMessage.guid
it.lastMessagePreview = lastMessage.displayText
// This will cause sort order to change. I think this ends
// up getting updated whenever we get conversation changes anyway.
// it.date = lastMessageDate
}
} }
} }
} }

View File

@@ -1,17 +1,13 @@
package net.buzzert.kordophone.backend.db.model package net.buzzert.kordophone.backend.db.model
import io.realm.kotlin.Realm
import io.realm.kotlin.ext.realmListOf import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.ext.realmSetOf
import io.realm.kotlin.ext.toRealmList import io.realm.kotlin.ext.toRealmList
import io.realm.kotlin.types.RealmInstant import io.realm.kotlin.types.RealmInstant
import io.realm.kotlin.types.RealmList import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.RealmSet
import io.realm.kotlin.types.annotations.PrimaryKey import io.realm.kotlin.types.annotations.PrimaryKey
import net.buzzert.kordophone.backend.model.GUID import net.buzzert.kordophone.backend.model.GUID
import org.mongodb.kbson.ObjectId import org.mongodb.kbson.ObjectId
import java.time.Instant
import net.buzzert.kordophone.backend.model.Conversation as ModelConversation import net.buzzert.kordophone.backend.model.Conversation as ModelConversation
import java.util.Date import java.util.Date
@@ -24,6 +20,7 @@ open class Conversation(
var date: RealmInstant, var date: RealmInstant,
var unreadCount: Int, var unreadCount: Int,
var lastMessageGUID: String?,
var lastMessagePreview: String?, var lastMessagePreview: String?,
): RealmObject ): RealmObject
{ {
@@ -35,10 +32,11 @@ open class Conversation(
date = RealmInstant.now(), date = RealmInstant.now(),
unreadCount = 0, unreadCount = 0,
lastMessagePreview = null, lastMessagePreview = null,
lastMessageGUID = null,
) )
fun toConversation(): ModelConversation { fun toConversation(): ModelConversation {
val conversation = ModelConversation( return ModelConversation(
displayName = displayName, displayName = displayName,
participants = participants.toList(), participants = participants.toList(),
date = Date.from(date.toInstant()), date = Date.from(date.toInstant()),
@@ -46,9 +44,8 @@ open class Conversation(
guid = guid, guid = guid,
lastMessagePreview = lastMessagePreview, lastMessagePreview = lastMessagePreview,
lastMessage = null, lastMessage = null,
lastFetchedMessageGUID = lastMessageGUID
) )
return conversation
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -57,6 +54,10 @@ open class Conversation(
val o = other as Conversation val o = other as Conversation
return guid == o.guid return guid == o.guid
} }
override fun hashCode(): Int {
return guid.hashCode()
}
} }
fun ModelConversation.toDatabaseConversation(): Conversation { fun ModelConversation.toDatabaseConversation(): Conversation {
@@ -67,6 +68,7 @@ fun ModelConversation.toDatabaseConversation(): Conversation {
date = from.date.toInstant().toRealmInstant() date = from.date.toInstant().toRealmInstant()
unreadCount = from.unreadCount unreadCount = from.unreadCount
lastMessagePreview = from.lastMessagePreview lastMessagePreview = from.lastMessagePreview
lastMessageGUID = from.lastFetchedMessageGUID
guid = from.guid guid = from.guid
} }
} }

View File

@@ -27,6 +27,8 @@ data class Conversation(
@SerializedName("lastMessage") @SerializedName("lastMessage")
var lastMessage: Message?, var lastMessage: Message?,
var lastFetchedMessageGUID: String?,
) { ) {
companion object { companion object {
fun generate(): Conversation { fun generate(): Conversation {
@@ -38,6 +40,7 @@ data class Conversation(
unreadCount = 0, unreadCount = 0,
lastMessagePreview = null, lastMessagePreview = null,
lastMessage = null, lastMessage = null,
lastFetchedMessageGUID = null,
) )
} }
} }
@@ -59,7 +62,7 @@ data class Conversation(
participants == o.participants && participants == o.participants &&
displayName == o.displayName && displayName == o.displayName &&
unreadCount == o.unreadCount && unreadCount == o.unreadCount &&
lastMessagePreview == o.lastMessagePreview lastFetchedMessageGUID == o.lastFetchedMessageGUID
) )
} }
@@ -69,8 +72,8 @@ data class Conversation(
result = 31 * result + participants.hashCode() result = 31 * result + participants.hashCode()
result = 31 * result + (displayName?.hashCode() ?: 0) result = 31 * result + (displayName?.hashCode() ?: 0)
result = 31 * result + unreadCount result = 31 * result + unreadCount
result = 31 * result + (lastMessagePreview?.hashCode() ?: 0)
result = 31 * result + (lastMessage?.hashCode() ?: 0) result = 31 * result + (lastMessage?.hashCode() ?: 0)
result = 31 * result + (lastFetchedMessageGUID?.hashCode() ?: 0)
return result return result
} }
} }

View File

@@ -182,9 +182,7 @@ class ChatRepository(
} }
suspend fun synchronizeConversation(conversation: Conversation, limit: Int = 15) = withErrorChannelHandling { suspend fun synchronizeConversation(conversation: Conversation, limit: Int = 15) = withErrorChannelHandling {
// TODO: Should only fetch messages after the last GUID we know about. val messages = fetchMessages(conversation, limit = limit, afterGUID = conversation.lastFetchedMessageGUID)
// But keep in mind that outgoing message GUIDs are fake...
val messages = fetchMessages(conversation, limit = limit)
database.writeMessages(messages, conversation) database.writeMessages(messages, conversation)
} }
@@ -233,10 +231,10 @@ class ChatRepository(
private suspend fun fetchMessages( private suspend fun fetchMessages(
conversation: Conversation, conversation: Conversation,
limit: Int? = null, limit: Int? = null,
before: Message? = null, beforeGUID: String? = null,
after: Message? = null, afterGUID: String? = null,
): List<Message> { ): List<Message> {
return apiInterface.getMessages(conversation.guid, limit, before?.guid, after?.guid) return apiInterface.getMessages(conversation.guid, limit, beforeGUID, afterGUID)
.bodyOnSuccessOrThrow() .bodyOnSuccessOrThrow()
.onEach { it.conversation = conversation } .onEach { it.conversation = conversation }
} }

View File

@@ -9,6 +9,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.buzzert.kordophone.backend.db.CachedChatDatabase import net.buzzert.kordophone.backend.db.CachedChatDatabase
import net.buzzert.kordophone.backend.model.Message import net.buzzert.kordophone.backend.model.Message
import net.buzzert.kordophone.backend.model.OutgoingMessage
import net.buzzert.kordophone.backend.server.APIClient import net.buzzert.kordophone.backend.server.APIClient
import net.buzzert.kordophone.backend.server.APIInterface import net.buzzert.kordophone.backend.server.APIInterface
import net.buzzert.kordophone.backend.server.Authentication import net.buzzert.kordophone.backend.server.Authentication
@@ -85,13 +86,18 @@ class BackendTests {
val (repository, mockServer) = mockRepository() val (repository, mockServer) = mockRepository()
val conversation = mockServer.addTestConversations(1).first() val conversation = mockServer.addTestConversations(1).first()
val outgoingMessage = MockServer.generateMessage(conversation) val generatedMessage = MockServer.generateMessage(conversation)
val outgoingMessage = OutgoingMessage(
body = generatedMessage.text,
conversation = conversation,
attachmentUris = setOf(),
attachmentDataSource = { null },
)
val guid = repository.enqueueOutgoingMessage(outgoingMessage, conversation) repository.enqueueOutgoingMessage(outgoingMessage)
val event = repository.messageDeliveredChannel.first() val event = repository.messageDeliveredChannel.first()
assertEquals(event.requestGuid, guid) assertEquals(event.message.text, outgoingMessage.body)
assertEquals(event.message.text, outgoingMessage.text)
repository.close() repository.close()
} }

View File

@@ -18,18 +18,19 @@ import net.buzzert.kordophone.backend.server.AuthenticationRequest
import net.buzzert.kordophone.backend.server.AuthenticationResponse import net.buzzert.kordophone.backend.server.AuthenticationResponse
import net.buzzert.kordophone.backend.server.SendMessageRequest import net.buzzert.kordophone.backend.server.SendMessageRequest
import net.buzzert.kordophone.backend.server.SendMessageResponse import net.buzzert.kordophone.backend.server.SendMessageResponse
import net.buzzert.kordophone.backend.server.UploadAttachmentResponse
import net.buzzert.kordophone.backend.server.authenticatedWebSocketURL import net.buzzert.kordophone.backend.server.authenticatedWebSocketURL
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.ResponseBody import okhttp3.ResponseBody
import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.WebSocket import okhttp3.WebSocket
import okhttp3.WebSocketListener import okhttp3.WebSocketListener
import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.MockWebServer
import retrofit2.Call
import retrofit2.Response import retrofit2.Response
import java.util.Date import java.util.Date
import java.util.UUID import java.util.UUID
@@ -66,7 +67,8 @@ class MockServer {
unreadCount = 0, unreadCount = 0,
lastMessagePreview = null, lastMessagePreview = null,
lastMessage = null, lastMessage = null,
guid = UUID.randomUUID().toString() guid = UUID.randomUUID().toString(),
lastFetchedMessageGUID = null,
) )
} }
} }
@@ -166,6 +168,8 @@ class MockServerClient(private val server: MockServer): APIClient, WebSocketList
private var updateWatchJob: Job? = null private var updateWatchJob: Job? = null
private val gson: Gson = Gson() private val gson: Gson = Gson()
override val isConfigured: Boolean = true
override fun getAPIInterface(): APIInterface { override fun getAPIInterface(): APIInterface {
return MockServerInterface(server) return MockServerInterface(server)
} }
@@ -268,6 +272,13 @@ class MockServerInterface(private val server: MockServer): APIInterface {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override suspend fun uploadAttachment(
filename: String,
body: RequestBody
): Response<UploadAttachmentResponse> {
TODO("Not yet implemented")
}
override suspend fun authenticate(request: AuthenticationRequest): Response<AuthenticationResponse> { override suspend fun authenticate(request: AuthenticationRequest): Response<AuthenticationResponse> {
// Anything goes! // Anything goes!
val response = AuthenticationResponse( val response = AuthenticationResponse(

Some files were not shown because too many files have changed in this diff Show More