Live video on the AT Protocol
at eli/multitesting 202 lines 6.0 kB view raw
1import messaging from "@react-native-firebase/messaging"; 2import { openAuthSessionAsync } from "expo-web-browser"; 3import { oauthCallback } from "features/bluesky/blueskySlice"; 4import { BlueskyState } from "features/bluesky/blueskyTypes"; 5import { PermissionsAndroid, Platform } from "react-native"; 6import { createAppSlice } from "../../hooks/createSlice"; 7import { 8 initialState, 9 PlatformState, 10 RegisterNotificationTokenBody, 11} from "./shared"; 12 13const checkApplicationPermission = async () => { 14 if (Platform.OS === "android") { 15 try { 16 await PermissionsAndroid.request( 17 PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS, 18 ); 19 } catch (error) { 20 console.log("error getting notifications ", error); 21 } 22 } 23}; 24 25export const platformSlice = createAppSlice({ 26 name: "platform", 27 initialState, 28 reducers: (create) => ({ 29 handleNotification: create.reducer( 30 ( 31 state, 32 action: { payload: { [key: string]: string | object } | undefined }, 33 ) => { 34 if (!action.payload) { 35 return state; 36 } 37 if (typeof action.payload.path !== "string") { 38 return state; 39 } 40 return { 41 ...state, 42 notificationDestination: action.payload.path, 43 }; 44 }, 45 ), 46 clearNotification: create.reducer((state) => { 47 return { 48 ...state, 49 notificationDestination: null, 50 }; 51 }), 52 openLoginLink: create.asyncThunk( 53 async (url: string, thunkAPI) => { 54 const res = await openAuthSessionAsync(url); 55 if (res.type === "success") { 56 thunkAPI.dispatch(oauthCallback(res.url)); 57 } 58 }, 59 { 60 pending: (state) => { 61 state.status = "loading"; 62 }, 63 fulfilled: (state) => { 64 state.status = "idle"; 65 }, 66 rejected: (state, { error }) => { 67 state.status = "failed"; 68 console.error(error); 69 }, 70 }, 71 ), 72 73 initPushNotifications: create.asyncThunk( 74 async (_, thunkAPI) => { 75 const msg = messaging(); 76 messaging().setBackgroundMessageHandler(async (remoteMessage) => { 77 console.log("Message handled in the background!", remoteMessage); 78 }); 79 await checkApplicationPermission(); 80 const authorizationStatus = await msg.requestPermission(); 81 82 let perms = ""; 83 84 if (authorizationStatus === messaging.AuthorizationStatus.AUTHORIZED) { 85 console.log("User has notification permissions enabled."); 86 perms += "authorized"; 87 } else if ( 88 authorizationStatus === messaging.AuthorizationStatus.PROVISIONAL 89 ) { 90 console.log("User has provisional notification permissions."); 91 perms += "provisional"; 92 } else { 93 console.log("User has notification permissions disabled"); 94 perms += "disabled"; 95 } 96 97 const token = await msg.getToken(); 98 99 messaging() 100 .subscribeToTopic("live") 101 .then(() => console.log("Subscribed to live!")); 102 103 messaging().onMessage((remoteMessage) => { 104 console.log("Foreground message:", remoteMessage); 105 // Display the notification to the user 106 }); 107 messaging().onNotificationOpenedApp((remoteMessage) => { 108 console.log( 109 "App opened by notification while in foreground:", 110 remoteMessage, 111 ); 112 thunkAPI.dispatch(handleNotification(remoteMessage.data)); 113 // Handle notification interaction when the app is in the foreground 114 }); 115 messaging() 116 .getInitialNotification() 117 .then((remoteMessage) => { 118 if (!remoteMessage) { 119 return; 120 } 121 console.log( 122 "App opened by notification from closed state:", 123 remoteMessage, 124 ); 125 thunkAPI.dispatch(handleNotification(remoteMessage.data)); 126 }); 127 128 return { token }; 129 }, 130 { 131 pending: (state) => {}, 132 fulfilled: (state, { payload }) => { 133 return { 134 ...state, 135 notificationToken: payload.token, 136 }; 137 }, 138 rejected: (state) => {}, 139 }, 140 ), 141 142 registerNotificationToken: create.asyncThunk( 143 async (_, thunkAPI) => { 144 if (typeof process.env.EXPO_PUBLIC_STREAMPLACE_URL !== "string") { 145 console.log("process.env.EXPO_PUBLIC_STREAMPLACE_URL undefined!"); 146 return; 147 } 148 const { platform, bluesky } = thunkAPI.getState() as { 149 platform: PlatformState; 150 bluesky: BlueskyState; 151 }; 152 if (!platform.notificationToken) { 153 throw new Error("No notification token"); 154 } 155 const body: RegisterNotificationTokenBody = { 156 token: platform.notificationToken, 157 }; 158 const did = bluesky.oauthSession?.did; 159 if (did) { 160 body.repoDID = did; 161 } 162 try { 163 const res = await fetch( 164 `${process.env.EXPO_PUBLIC_STREAMPLACE_URL}/api/notification`, 165 { 166 method: "POST", 167 headers: { 168 "content-type": "application/json", 169 }, 170 body: JSON.stringify(body), 171 }, 172 ); 173 console.log({ status: res.status }); 174 } catch (e) { 175 console.log(e); 176 } 177 }, 178 { 179 pending: (state) => {}, 180 fulfilled: (state) => {}, 181 rejected: (state) => {}, 182 }, 183 ), 184 }), 185 186 selectors: { 187 selectNotificationToken: (platform) => platform.notificationToken, 188 selectNotificationDestination: (platform) => 189 platform.notificationDestination, 190 }, 191}); 192 193export const { 194 openLoginLink, 195 initPushNotifications, 196 registerNotificationToken, 197 handleNotification, 198 clearNotification, 199} = platformSlice.actions; 200 201export const { selectNotificationToken, selectNotificationDestination } = 202 platformSlice.selectors;