From 47d70208e96108908f9957680c769f8a0ec82183 Mon Sep 17 00:00:00 2001 From: Divyanshu patil Date: Thu, 29 Jan 2026 13:20:32 +0530 Subject: [PATCH 1/2] feat: save quick replies to the userpref --- app/i18n/locales/en.json | 3 + app/lib/constants/keys.ts | 1 + app/lib/methods/userPreferences.ts | 13 ++++ app/stacks/InsideStack.tsx | 2 + app/stacks/types.ts | 1 + app/views/UserPreferencesView/index.tsx | 10 +++ .../UserWatchOSQuickRepliesView/index.tsx | 68 +++++++++++++++++++ 7 files changed, 98 insertions(+) create mode 100644 app/views/UserWatchOSQuickRepliesView/index.tsx diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index 20adc12eb90..13cee686a12 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -18,6 +18,7 @@ "abac_removed_user_from_the_room": "was removed by ABAC", "ABAC_room_attributes": "Room attributes", "accept": "Accept", + "WatchOS_Quick_Replies_Description":"Enter Quick Replies to Display on Apple Watch", "Accessibility": "Accessibility", "Accessibility_and_Appearance": "Accessibility & appearance", "Accessibility_statement": "Accessibility statement", @@ -33,6 +34,7 @@ "Add_server": "Add workspace", "Add_thread_reply": "Add thread reply", "Add_users": "Add users", + "Add_Quick_Reply":"Add Quick Reply", "added__roomName__to_this_team": "added #{{roomName}} to this team", "Added__username__to_this_team": "added @{{user_added}} to this team", "Admin_Panel": "Admin panel", @@ -977,6 +979,7 @@ "Waiting_for_answer": "Waiting for answer", "Waiting_for_network": "Waiting for network...", "Waiting_for_server_connection": "Waiting for server connection", + "WatchOS_Quick_Replies":"WatchOS Quick Replies", "Websocket_disabled": "Websocket is disabled for this workspace.\n{{contact}}", "What_are_you_doing_right_now": "What are you doing right now?", "Whats_the_password_for_your_certificate": "What's the password for your certificate?", diff --git a/app/lib/constants/keys.ts b/app/lib/constants/keys.ts index 8c6dc1da7d2..bfb8aa52561 100644 --- a/app/lib/constants/keys.ts +++ b/app/lib/constants/keys.ts @@ -25,3 +25,4 @@ export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY'; export const TOKEN_KEY = 'reactnativemeteor_usertoken'; export const CURRENT_SERVER = 'currentServer'; export const CERTIFICATE_KEY = 'RC_CERTIFICATE_KEY'; +export const WATCHOS_QUICKREPLIES = 'RC_WATCHOS_QUICKREPIES'; diff --git a/app/lib/methods/userPreferences.ts b/app/lib/methods/userPreferences.ts index 8e9a605ee3c..d8e9dbc0ebc 100644 --- a/app/lib/methods/userPreferences.ts +++ b/app/lib/methods/userPreferences.ts @@ -149,6 +149,19 @@ class UserPreferences { this.mmkv.set(key, value); } + getArray(key: string): T[] | null { + try { + const jsonString = this.mmkv.getString(key); + return jsonString ? (JSON.parse(jsonString) as T[]) : null; + } catch { + return null; + } + } + + setArray(key: string, value: T[]): void { + this.mmkv.set(key, JSON.stringify(value)); + } + getAllKeys(): string[] { return this.mmkv.getAllKeys(); } diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx index a314c801885..6ea856d2a3c 100644 --- a/app/stacks/InsideStack.tsx +++ b/app/stacks/InsideStack.tsx @@ -36,6 +36,7 @@ import CannedResponseDetail from '../views/CannedResponseDetail'; import ProfileView from '../views/ProfileView'; import UserPreferencesView from '../views/UserPreferencesView'; import UserNotificationPrefView from '../views/UserNotificationPreferencesView'; +import UserWatchOSQuickRepliesView from '../views/UserWatchOSQuickRepliesView'; import ChangePasswordView from '../views/ChangePasswordView'; // Display Preferences View import DisplayPrefsView from '../views/DisplayPrefsView'; @@ -171,6 +172,7 @@ const ProfileStackNavigator = () => { + diff --git a/app/stacks/types.ts b/app/stacks/types.ts index c8afff63863..2e1b96c20c1 100644 --- a/app/stacks/types.ts +++ b/app/stacks/types.ts @@ -191,6 +191,7 @@ export type ProfileStackParamList = { ProfileView: undefined; UserPreferencesView: undefined; UserNotificationPrefView: undefined; + UserWatchOSQuickRepliesView: undefined; PushTroubleshootView: undefined; ChangeAvatarView: { context: TChangeAvatarViewContext; diff --git a/app/views/UserPreferencesView/index.tsx b/app/views/UserPreferencesView/index.tsx index 9b37c39e57b..4c13784836e 100644 --- a/app/views/UserPreferencesView/index.tsx +++ b/app/views/UserPreferencesView/index.tsx @@ -116,6 +116,16 @@ const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Ele /> + + + navigateToScreen('UserWatchOSQuickRepliesView')} + showActionIndicator + testID='preferences-view-watchos-quickreplies' + /> + + ); diff --git a/app/views/UserWatchOSQuickRepliesView/index.tsx b/app/views/UserWatchOSQuickRepliesView/index.tsx new file mode 100644 index 00000000000..024dd02e635 --- /dev/null +++ b/app/views/UserWatchOSQuickRepliesView/index.tsx @@ -0,0 +1,68 @@ +import { type NativeStackNavigationProp } from '@react-navigation/native-stack'; +import React, { useEffect, useState } from 'react'; +import { ScrollView } from 'react-native-gesture-handler'; + +import I18n from '../../i18n'; +import SafeAreaView from '../../containers/SafeAreaView'; +import * as List from '../../containers/List'; +import { type ProfileStackParamList } from '../../stacks/types'; +import { FormTextInput } from '../../containers/TextInput'; +import Chip from '../../containers/Chip'; +import { useUserPreferences } from '../../lib/methods/userPreferences'; +import { WATCHOS_QUICKREPLIES } from '../../lib/constants/keys'; + +interface IUserPreferencesViewProps { + navigation: NativeStackNavigationProp; +} + +const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Element => { + const [quickreplies, setQuickreplies] = useUserPreferences(WATCHOS_QUICKREPLIES, []); + const [input, setInput] = useState(''); + + useEffect(() => { + navigation.setOptions({ + title: I18n.t('Preferences') + }); + }, [navigation]); + + const removeQuickReply = (reply: string) => { + const newReplies = quickreplies?.filter(quickreply => quickreply !== reply); + setQuickreplies(newReplies); + }; + + const addQuickReply = () => { + const value = input.trim(); + if (!value) return; + if (!quickreplies?.includes(input.trim())) setQuickreplies([...(quickreplies ?? []), value]); + setInput(''); + }; + + return ( + + + + <> + {quickreplies && quickreplies.length !== 0 && ( + + {quickreplies.map((reply, index) => ( + removeQuickReply(reply)} /> + ))} + + )} + + + setInput(text)} + placeholder={I18n.t('Add_Quick_Reply')} + onSubmitEditing={addQuickReply} + /> + + + + + + ); +}; + +export default UserPreferencesView; From 6e484579bf4cb579b011e6d38ecc986afb08acb9 Mon Sep 17 00:00:00 2001 From: Divyanshu patil Date: Fri, 30 Jan 2026 10:09:18 +0530 Subject: [PATCH 2/2] feat: sync app with watch --- .../WatchOSQuickReplies/getWatchStatus.ts | 9 +++ .../WatchOSQuickReplies/syncReplies.ts | 16 +++++ .../UserWatchOSQuickRepliesView/index.tsx | 12 ++++ ios/Podfile.lock | 6 +- .../Loaders/WatchSession.swift | 20 +++++- ios/RocketChat Watch App/Storage.swift | 1 + .../Views/MessageComposerView.swift | 52 ++++++++++++++- ios/RocketChatRN.xcodeproj/project.pbxproj | 20 ++++-- ios/WatchBridge.m | 16 +++++ ios/WatchBridge.swift | 66 +++++++++++++++++++ 10 files changed, 207 insertions(+), 11 deletions(-) create mode 100644 app/lib/methods/WatchOSQuickReplies/getWatchStatus.ts create mode 100644 app/lib/methods/WatchOSQuickReplies/syncReplies.ts create mode 100644 ios/WatchBridge.m create mode 100644 ios/WatchBridge.swift diff --git a/app/lib/methods/WatchOSQuickReplies/getWatchStatus.ts b/app/lib/methods/WatchOSQuickReplies/getWatchStatus.ts new file mode 100644 index 00000000000..4052231d54b --- /dev/null +++ b/app/lib/methods/WatchOSQuickReplies/getWatchStatus.ts @@ -0,0 +1,9 @@ +import { NativeModules } from 'react-native'; + +const { WatchBridge } = NativeModules; + +export async function checkWatch() { + const status = await WatchBridge.getWatchStatus(); + + return status; +} diff --git a/app/lib/methods/WatchOSQuickReplies/syncReplies.ts b/app/lib/methods/WatchOSQuickReplies/syncReplies.ts new file mode 100644 index 00000000000..e2a0ffa49c6 --- /dev/null +++ b/app/lib/methods/WatchOSQuickReplies/syncReplies.ts @@ -0,0 +1,16 @@ +import { NativeModules } from 'react-native'; + +const { WatchBridge } = NativeModules; + +export async function syncWatchOSQuickReplies(replies: string[]) { + if (!Array.isArray(replies)) return false; + + try { + const success: boolean = await WatchBridge.syncQuickReplies(replies); + + return success; + } catch (e) { + console.error('Failed to send quick replies', e); + return false; + } +} diff --git a/app/views/UserWatchOSQuickRepliesView/index.tsx b/app/views/UserWatchOSQuickRepliesView/index.tsx index 024dd02e635..396fa87b46e 100644 --- a/app/views/UserWatchOSQuickRepliesView/index.tsx +++ b/app/views/UserWatchOSQuickRepliesView/index.tsx @@ -10,6 +10,8 @@ import { FormTextInput } from '../../containers/TextInput'; import Chip from '../../containers/Chip'; import { useUserPreferences } from '../../lib/methods/userPreferences'; import { WATCHOS_QUICKREPLIES } from '../../lib/constants/keys'; +import { syncWatchOSQuickReplies } from '../../lib/methods/WatchOSQuickReplies/syncReplies'; +import { checkWatch } from '../../lib/methods/WatchOSQuickReplies/getWatchStatus'; interface IUserPreferencesViewProps { navigation: NativeStackNavigationProp; @@ -25,6 +27,16 @@ const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Ele }); }, [navigation]); + useEffect(() => { + const load = async () => { + const status = await checkWatch(); + console.log(status); + const result = await syncWatchOSQuickReplies(quickreplies ?? []); + console.log(result); + }; + load(); + }, [quickreplies]); + const removeQuickReply = (reply: string) => { const newReplies = quickreplies?.filter(quickreply => quickreply !== reply); setQuickreplies(newReplies); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 99a6053f60c..aa85921f9d2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -232,7 +232,7 @@ PODS: - libwebp/sharpyuv (1.5.0) - libwebp/webp (1.5.0): - libwebp/sharpyuv - - MobileCrypto (0.2.0): + - MobileCrypto (0.2.1): - DoubleConversion - glog - hermes-engine @@ -3082,7 +3082,7 @@ SPEC CHECKSUMS: libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 - MobileCrypto: 60a1e43e26a9d6851ae2aa7294b8041c9e9220b7 + MobileCrypto: a424494b2f45bec9dbe60e3f6d16a40aedefe7b7 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 @@ -3185,7 +3185,7 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 WatermelonDB: 4c846c8cb94eef3cba90fa034d15310163226703 - Yoga: dfabf1234ccd5ac41d1b1d43179f024366ae9831 + Yoga: 2a3a4c38a8441b6359d5e5914d35db7b2b67aebd ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 PODFILE CHECKSUM: 199f6fbbe6fb415c822cca992e6152000ac55b3e diff --git a/ios/RocketChat Watch App/Loaders/WatchSession.swift b/ios/RocketChat Watch App/Loaders/WatchSession.swift index 4989b7fa875..4310469954e 100644 --- a/ios/RocketChat Watch App/Loaders/WatchSession.swift +++ b/ios/RocketChat Watch App/Loaders/WatchSession.swift @@ -7,7 +7,10 @@ protocol WatchSessionProtocol { /// Default WatchSession protocol implementation. final class WatchSession: NSObject, WatchSessionProtocol, WCSessionDelegate { private let session: WCSession - + + @Storage(.quickReplies, defaultValue: []) + private var quickReplies: [String]? + init(session: WCSession) { self.session = session super.init() @@ -46,6 +49,21 @@ final class WatchSession: NSObject, WatchSessionProtocol, WCSessionDelegate { func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { } + + // quick replies + func session( + _ session: WCSession, + didReceiveApplicationContext applicationContext: [String : Any] + ) { + print(applicationContext) + + if let replies = applicationContext["quickReplies"] as? [String] { + quickReplies = replies + print("QUICK REPLIES STORED:", replies) + } else { + print("quickReplies key missing") + } + } } /// Retry decorator for WatchSession protocol. diff --git a/ios/RocketChat Watch App/Storage.swift b/ios/RocketChat Watch App/Storage.swift index e79a0277b22..ed42dee74dc 100644 --- a/ios/RocketChat Watch App/Storage.swift +++ b/ios/RocketChat Watch App/Storage.swift @@ -2,6 +2,7 @@ import Foundation enum StorageKey: String { case currentServer = "current_server" + case quickReplies = "quick_replies" } @propertyWrapper diff --git a/ios/RocketChat Watch App/Views/MessageComposerView.swift b/ios/RocketChat Watch App/Views/MessageComposerView.swift index f57aeeb0231..23837041020 100644 --- a/ios/RocketChat Watch App/Views/MessageComposerView.swift +++ b/ios/RocketChat Watch App/Views/MessageComposerView.swift @@ -5,6 +5,9 @@ struct MessageComposerView: View { let room: Room let onSend: (String) -> Void + + @Storage(.quickReplies, defaultValue: []) + private var quickReplies: [String]? var body: some View { if room.isReadOnly { @@ -17,9 +20,52 @@ struct MessageComposerView: View { Spacer() } } else { - TextField("Message", text: $message) - .submitLabel(.send) - .onSubmit(send) + VStack(alignment: .leading, spacing: 4) { + TextField("Message", text: $message) + .submitLabel(.send) + .onSubmit(send) + + if let replies = quickReplies, !replies.isEmpty { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 6) { + ForEach(replies, id: \.self) { reply in + if #available(watchOS 26.0, *) { + Button { + message = reply + send() + } label: { + Text(reply) + .font(.caption2) + .foregroundStyle(.primary) + .padding(.horizontal, 10) + .padding(.vertical, 4) + } + .buttonStyle(.plain) + .glassEffect(.regular) + .clipShape(Capsule()) + } else { + Button { + message = reply + send() + } label: { + Text(reply) + .font(.caption2) + .foregroundStyle(.primary) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background( + Capsule() + .fill(Color.white.opacity(0.12)) + ) + } + .buttonStyle(.borderedProminent) + } + } + } + } + } + } + } } diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 29cc9563241..990983ebadb 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -11,6 +11,10 @@ 05F9D701BF644C25192B8E79 /* Pods_defaults_RocketChatRN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A46AABC73B7E9703E69AF850 /* Pods_defaults_RocketChatRN.framework */; }; 0C6E2DE448364EA896869ADF /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B37C79D9BD0742CE936B6982 /* libc++.tbd */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 169F81322F2BDBB700DF8EA5 /* WatchBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 169F812E2F2BDBB700DF8EA5 /* WatchBridge.swift */; }; + 169F81372F2BFC4000DF8EA5 /* WatchBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 169F81362F2BFC4000DF8EA5 /* WatchBridge.m */; }; + 169F81382F2BFC4000DF8EA5 /* WatchBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 169F81362F2BFC4000DF8EA5 /* WatchBridge.m */; }; + 169F81392F2BFCE600DF8EA5 /* WatchBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 169F812E2F2BDBB700DF8EA5 /* WatchBridge.swift */; }; 1E01C81C2511208400FEF824 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C81B2511208400FEF824 /* URL+Extensions.swift */; }; 1E01C8212511301400FEF824 /* PushResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C8202511301400FEF824 /* PushResponse.swift */; }; 1E01C8252511303100FEF824 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C8242511303100FEF824 /* Notification.swift */; }; @@ -469,6 +473,8 @@ 13B07F961A680F5B00A75B9A /* Rocket.Chat Experimental.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rocket.Chat Experimental.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RocketChatRN/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RocketChatRN/Info.plist; sourceTree = ""; }; + 169F812E2F2BDBB700DF8EA5 /* WatchBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchBridge.swift; sourceTree = ""; }; + 169F81362F2BFC4000DF8EA5 /* WatchBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WatchBridge.m; sourceTree = ""; }; 194D9A8897F4A486C2C6F89A /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-NotificationService/ExpoModulesProvider.swift"; sourceTree = ""; }; 1E01C81B2511208400FEF824 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; 1E01C8202511301400FEF824 /* PushResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushResponse.swift; sourceTree = ""; }; @@ -728,8 +734,10 @@ 66C2701E2EBBCB780062725F /* SecureStorage.h */, 66C2701F2EBBCB780062725F /* SecureStorage.m */, 7ACFE7D82DDE48760090D9BC /* AppDelegate.swift */, + 169F812E2F2BDBB700DF8EA5 /* WatchBridge.swift */, A48B46D72D3FFBD200945489 /* A11yFlowModule.h */, A48B46D82D3FFBD200945489 /* A11yFlowModule.m */, + 169F81362F2BFC4000DF8EA5 /* WatchBridge.m */, 7A8B30742BCD9D3F00146A40 /* SSLPinning.h */, 7A8B30752BCD9D3F00146A40 /* SSLPinning.mm */, 65B9A7192AFC24190088956F /* ringtone.mp3 */, @@ -1825,7 +1833,7 @@ inputFileListPaths = ( ); inputPaths = ( - "$TARGET_BUILD_DIR/$INFOPLIST_PATH", + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -1845,7 +1853,7 @@ inputFileListPaths = ( ); inputPaths = ( - "$TARGET_BUILD_DIR/$INFOPLIST_PATH", + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -2051,10 +2059,12 @@ 1E0426E6251A5467008F022C /* RoomType.swift in Sources */, 1E76CBCD25152C2C0067298C /* MessageType.swift in Sources */, 1E76CBC925152C1F0067298C /* PushResponse.swift in Sources */, + 169F81382F2BFC4000DF8EA5 /* WatchBridge.m in Sources */, 1E76CBD325152C770067298C /* Encryption.swift in Sources */, 1E76CBC825152C070067298C /* RocketChat.swift in Sources */, 1E76CBD725152C840067298C /* HTTPMethod.swift in Sources */, 1E76CBC725152BFF0067298C /* Payload.swift in Sources */, + 169F81322F2BDBB700DF8EA5 /* WatchBridge.swift in Sources */, 1E76CBD225152C730067298C /* Data+Extensions.swift in Sources */, 1E76CBD125152C710067298C /* Date+Extensions.swift in Sources */, 1E76CBD425152C790067298C /* Database.swift in Sources */, @@ -2330,11 +2340,13 @@ 7AAB3E1F257E6A6E00707CF6 /* RocketChat.swift in Sources */, 7AAB3E20257E6A6E00707CF6 /* HTTPMethod.swift in Sources */, 7AAB3E21257E6A6E00707CF6 /* Payload.swift in Sources */, + 169F81392F2BFCE600DF8EA5 /* WatchBridge.swift in Sources */, 7AAB3E23257E6A6E00707CF6 /* Data+Extensions.swift in Sources */, 7AAB3E24257E6A6E00707CF6 /* Date+Extensions.swift in Sources */, 7AAB3E25257E6A6E00707CF6 /* Database.swift in Sources */, 7ACFE7D92DDE48760090D9BC /* AppDelegate.swift in Sources */, 1E9A71752B59F36E00477BA2 /* ClientSSL.swift in Sources */, + 169F81372F2BFC4000DF8EA5 /* WatchBridge.m in Sources */, A48B46DA2D3FFBD200945489 /* A11yFlowModule.m in Sources */, 3F56D232A9EBA1C9C749F160 /* MMKVBridge.mm in Sources */, 7AACF8AE2C94B28B0082844E /* DecryptedContent.swift in Sources */, @@ -2593,7 +2605,7 @@ "$(inherited)", "$(SRCROOT)/../node_modules/rn-extensions-share/ios/**", "$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**", - "$PODS_CONFIGURATION_BUILD_DIR/Firebase", + $PODS_CONFIGURATION_BUILD_DIR/Firebase, ); INFOPLIST_FILE = ShareRocketChatRN/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -2669,7 +2681,7 @@ "$(inherited)", "$(SRCROOT)/../node_modules/rn-extensions-share/ios/**", "$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**", - "$PODS_CONFIGURATION_BUILD_DIR/Firebase", + $PODS_CONFIGURATION_BUILD_DIR/Firebase, ); INFOPLIST_FILE = ShareRocketChatRN/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; diff --git a/ios/WatchBridge.m b/ios/WatchBridge.m new file mode 100644 index 00000000000..4fbde8a64c0 --- /dev/null +++ b/ios/WatchBridge.m @@ -0,0 +1,16 @@ +#import + +@interface RCT_EXTERN_MODULE(WatchBridge, NSObject) + +RCT_EXTERN_METHOD( + getWatchStatus:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject +) + +RCT_EXTERN_METHOD( + syncQuickReplies:(NSArray *)replies + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject +) + +@end diff --git a/ios/WatchBridge.swift b/ios/WatchBridge.swift new file mode 100644 index 00000000000..0c944cd02c7 --- /dev/null +++ b/ios/WatchBridge.swift @@ -0,0 +1,66 @@ +import Foundation +import React +import WatchConnectivity + +@objc(WatchBridge) +final class WatchBridge: NSObject { + + @objc(syncQuickReplies:resolver:rejecter:) + func syncQuickReplies( + _ replies: [String], + resolver resolve: RCTPromiseResolveBlock, + rejecter reject: RCTPromiseRejectBlock + ) { + print("WatchBridge.syncData called") + print("Value:", replies) + + guard WCSession.isSupported() else { + resolve(false) + return + } + + let session = WCSession.default + + guard session.isPaired, session.isWatchAppInstalled else { + resolve(false) + return + } + + do { + try session.updateApplicationContext([ + "quickReplies": replies + ]) + resolve(true) + } catch { + reject("SYNC_FAILED", error.localizedDescription, error) + } + } + + @objc(getWatchStatus:rejecter:) + func getWatchStatus( + resolver resolve: RCTPromiseResolveBlock, + rejecter reject: RCTPromiseRejectBlock + ) { + guard WCSession.isSupported() else { + resolve([ + "isSupported": false, + "isPaired": false, + "isWatchAppInstalled": false + ]) + return + } + + let session = WCSession.default + + resolve([ + "isSupported": true, + "isPaired": session.isPaired, + "isWatchAppInstalled": session.isWatchAppInstalled + ]) + } +} + +extension WatchBridge: RCTBridgeModule { + static func moduleName() -> String! { "WatchBridge" } + static func requiresMainQueueSetup() -> Bool { false } +}