Invite links: starsync:// opens the app and asks to join; one app per profile; real invite address; clipboard pick-up
- starsync://host:port/?name=&pw= (PROTOCOL.md "Invite link"); the password only when ticked, never in logs - Linux: launcher entry takes x-scheme-handler/starsync (%u, StartupNotify) and becomes its xdg-mime default; Windows: HKCU\Software\Classes\starsync -> starsync.exe - InstanceLock: lock file + loopback handoff per profile folder; the running app comes up with the second start's activation token (Wayland/X11) or AllowSetForegroundWindow (Windows) - Sharing looks up the internet address (HTTPS services with fallbacks); members card picks Internet / Home network / VPN and says when the port needs forwarding - JoinOffers: link and clipboard prompt with /health, joins through ConnectFlow; dismissed clipboard invites kept as fingerprints Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d48915dc47
commit
6048fbe7d1
35 files changed
+1943
-181
No files matched your search
@@ -184,4 +184,46 @@ class ConnectStore {
|
||||
];
|
||||
await saveQuietly();
|
||||
}
|
||||
|
||||
/// The saved password of a recent server at [host]:[port], '' when none.
|
||||
String secretFor(String host, int port) =>
|
||||
recents.peek().where((r) => r.key == '${host.toLowerCase()}:$port').firstOrNull?.secret ?? '';
|
||||
|
||||
/// Invites the join prompt already asked about (joined or dismissed),
|
||||
/// as fingerprints ([inviteFingerprint]), newest last; never the links.
|
||||
final _seenInvites = <String>[];
|
||||
|
||||
bool seenInvite(String fingerprint) {
|
||||
_loadSeen();
|
||||
return _seenInvites.contains(fingerprint);
|
||||
}
|
||||
|
||||
Future<void> rememberInvite(String fingerprint) async {
|
||||
_loadSeen();
|
||||
_seenInvites
|
||||
..remove(fingerprint)
|
||||
..add(fingerprint);
|
||||
while (_seenInvites.length > maxSeenInvites) {
|
||||
_seenInvites.removeAt(0);
|
||||
}
|
||||
try {
|
||||
final box = connectFormBoxIfOpen() ?? await openConnectFormBox();
|
||||
await box.put(connectKeySeenInvites, [..._seenInvites]);
|
||||
} catch (e) {
|
||||
debugPrint('save seen invites failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
bool _seenLoaded = false;
|
||||
void _loadSeen() {
|
||||
if (_seenLoaded) return;
|
||||
final box = connectFormBoxIfOpen();
|
||||
if (box == null) return;
|
||||
_seenLoaded = true;
|
||||
final raw = box.get(connectKeySeenInvites);
|
||||
if (raw is List) _seenInvites.insertAll(0, [for (final v in raw) if (v is String && !_seenInvites.contains(v)) v]);
|
||||
}
|
||||
}
|
||||
|
||||
const connectKeySeenInvites = 'seenInvites';
|
||||
const maxSeenInvites = 30;
|
||||
Reference in new issue
Block a user