Skip to content

Commit fae8efd

Browse files
authored
iOS: Add universal links for private session invites (#232)
### Summary Adds iOS universal link support so `https://pastepoint.com/private/<code>` invite links open PastePoint and join the matching private session directly. This also serves the Apple App Site Association file from the web/nginx stack and bumps client version policy for the new release. ### What changed **Universal links** - Add the Apple App Site Association file for `/private/*` links. - Include `.well-known/apple-app-site-association` in the Angular build assets. - Copy the AASA file into the nginx image. - Serve the AASA file from nginx with `application/json`. - Add the iOS Associated Domains entitlement for `applinks:pastepoint.com`. **iOS** - Handle incoming universal links with `.onOpenURL`. - Parse and validate private session invite URLs before joining. - Join the private session from a universal link using the same connection flow as manual joins. - Ignore links for the already-active private session. - Close settings and refresh rooms/user state after joining from a link. - Skip a redundant reconnect on the first network path monitor update. **Nginx / static serving** - Add an explicit route for `/.well-known/apple-app-site-association`. - Simplify static `try_files` behavior for `robots.txt` and `sitemap.xml`. **Version policy / bumps** - iOS → `0.10.0` - web → `0.21.0` - server policy minimum/latest: - iOS → `0.10.0` - web → `0.21.0` - Set the iOS update gate URL to the real App Store URL.
2 parents c3188f0 + 335dfbb commit fae8efd

15 files changed

Lines changed: 112 additions & 25 deletions

File tree

client/ios/PastePoint.xcodeproj/project.pbxproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@
435435
buildSettings = {
436436
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
437437
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "";
438+
CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/PastePoint/Resources/PastePoint.entitlements";
438439
CODE_SIGN_STYLE = Automatic;
439440
CURRENT_PROJECT_VERSION = 1;
440441
DEVELOPMENT_TEAM = KH5NHCFH44;
@@ -451,7 +452,7 @@
451452
"$(inherited)",
452453
"@executable_path/Frameworks",
453454
);
454-
MARKETING_VERSION = 0.9.0;
455+
MARKETING_VERSION = 0.10.0;
455456
PRODUCT_BUNDLE_IDENTIFIER = com.pastepoint.ios;
456457
PRODUCT_NAME = "$(TARGET_NAME)";
457458
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -473,6 +474,7 @@
473474
buildSettings = {
474475
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
475476
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "";
477+
CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/PastePoint/Resources/PastePoint.entitlements";
476478
CODE_SIGN_STYLE = Automatic;
477479
CURRENT_PROJECT_VERSION = 1;
478480
DEVELOPMENT_TEAM = KH5NHCFH44;
@@ -489,7 +491,7 @@
489491
"$(inherited)",
490492
"@executable_path/Frameworks",
491493
);
492-
MARKETING_VERSION = 0.9.0;
494+
MARKETING_VERSION = 0.10.0;
493495
PRODUCT_BUNDLE_IDENTIFIER = com.pastepoint.ios;
494496
PRODUCT_NAME = "$(TARGET_NAME)";
495497
STRING_CATALOG_GENERATE_SYMBOLS = YES;

client/ios/PastePoint/App/ContentView+Events.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,17 @@ private struct ChatEventHandlers: ViewModifier {
1818
let owner: ContentView
1919

2020
func body(content: Content) -> some View {
21-
connectionHandlers(fileTransferHandlers(messageHandlers(content)))
21+
deepLinkHandlers(connectionHandlers(fileTransferHandlers(messageHandlers(content))))
22+
}
23+
24+
// MARK: - Deep links
25+
26+
/// Universal links (`https://pastepoint.com/private/<code>`) join the invited session.
27+
private func deepLinkHandlers(_ content: some View) -> some View {
28+
content
29+
.onOpenURL { url in
30+
owner.handleIncomingURL(url)
31+
}
2232
}
2333

2434
// MARK: - Incoming messages

client/ios/PastePoint/App/ContentView+Handlers.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: GPL-3.0-only
44
//
55

6+
import Logging
67
import SwiftUI
78

89
// MARK: - Handlers
@@ -61,6 +62,35 @@ extension ContentView {
6162
return true
6263
}
6364

65+
/// Universal-link entry: validates an invite URL and joins its private session,
66+
/// mirroring the manual join flow in `SettingsJoinPrivateView`.
67+
func handleIncomingURL(_ url: URL) {
68+
guard
69+
let code = AppEnvironment.privateSessionCode(from: url.absoluteString),
70+
SessionService.isValidSessionCode(code)
71+
else {
72+
logger.warning("Ignoring invalid incoming URL")
73+
toast.show(.error(.invalidSessionCode))
74+
return
75+
}
76+
77+
// Already in this session
78+
guard services.wsService.currentSessionCode != code else { return }
79+
80+
logger.info("Joining private session from universal link")
81+
Task {
82+
await services.wsService.setupPrivateSession(code)
83+
guard await services.connectIfPermitted() else {
84+
toast.show(.error(.localNetworkOffJoin))
85+
return
86+
}
87+
setSettingsVisible(false)
88+
pendingPrivateJoin = true
89+
await services.roomService.listRooms()
90+
await services.userService.getUsername()
91+
}
92+
}
93+
6494
func handleAcceptFile(fromUser: String, fileId: String) {
6595
Task {
6696
let result = await services.fileTransferService.acceptFileOffer(fromUser: fromUser, fileId: fileId)

client/ios/PastePoint/App/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ struct ContentView: View {
193193
// MARK: - Settings presentation
194194

195195
/// Shows/hides settings inside one animation transaction (gear toggle + panel close).
196-
private func setSettingsVisible(_ visible: Bool) {
196+
func setSettingsVisible(_ visible: Bool) {
197197
withAnimation(.easeInOut(duration: 0.25)) {
198198
showSettings = visible
199199
}

client/ios/PastePoint/Core/Services/App/AppServices.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final class AppServices: ObservableObject {
3131
private var isInBackground = false
3232
private var isForegroundHandling = false
3333
private let networkMonitor = NWPathMonitor()
34-
private var lastPathStatus: NWPath.Status?
34+
private var lastPathStatus: NWPath.Status = .satisfied
3535
private var cancellables = Set<AnyCancellable>()
3636

3737
private init() {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.developer.associated-domains</key>
6+
<array>
7+
<string>applinks:pastepoint.com</string>
8+
</array>
9+
</dict>
10+
</plist>

client/web/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ RUN apk add --no-cache gettext=0.24.1-r1
6464
# Copy only the specific static files needed
6565
COPY --from=builder /usr/src/app/dist/web/browser/robots.txt /usr/share/nginx/html/browser/robots.txt
6666
COPY --from=builder /usr/src/app/dist/web/browser/sitemap.xml /usr/share/nginx/html/browser/sitemap.xml
67+
COPY --from=builder /usr/src/app/dist/web/browser/.well-known/apple-app-site-association /usr/share/nginx/html/browser/.well-known/apple-app-site-association
6768

6869
# Copy nginx config files
6970
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf.template

client/web/angular.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
{
3333
"glob": "**/*",
3434
"input": "public"
35+
},
36+
{
37+
"glob": "**/*",
38+
"input": "public/.well-known",
39+
"output": "/.well-known"
3540
}
3641
],
3742
"styles": ["src/styles.css", "node_modules/@ngxpert/hot-toast/src/styles/styles.css"],

client/web/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "web",
3-
"version": "0.20.0",
3+
"version": "0.21.0",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",

0 commit comments

Comments
 (0)