Skip to content

Commit b3b67e4

Browse files
authored
Merge pull request #2080 from The-Commit-Company/develop
Merging develop into main
2 parents d05359d + 03f8722 commit b3b67e4

13 files changed

Lines changed: 163 additions & 104 deletions

File tree

apps/mobile/app.json

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@
88
"icon": "./assets/icon.png",
99
"userInterfaceStyle": "automatic",
1010
"newArchEnabled": true,
11-
"splash": {
12-
"image": "./assets/splash.png",
13-
"resizeMode": "contain",
14-
"backgroundColor": "#000000"
15-
},
1611
"ios": {
1712
"supportsTablet": false,
1813
"appleTeamId": "W6346TTZQX",
@@ -47,6 +42,19 @@
4742
[
4843
"expo-router"
4944
],
45+
[
46+
"expo-splash-screen",
47+
{
48+
"backgroundColor": "#ffffff",
49+
"image": "./assets/splash.png",
50+
"imageWidth": 400,
51+
"resizeMode": "contain",
52+
"dark": {
53+
"backgroundColor": "#121212",
54+
"image": "./assets/splash.png"
55+
}
56+
}
57+
],
5058
[
5159
"expo-secure-store"
5260
],
@@ -87,4 +95,4 @@
8795
},
8896
"owner": "the-commit-company"
8997
}
90-
}
98+
}

apps/mobile/app/_layout.tsx

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import 'expo-dev-client';
2-
import { router, Slot, usePathname } from 'expo-router';
2+
import { router, Slot } from 'expo-router';
33
import { ThemeProvider } from '@react-navigation/native';
44
import "../global.css";
5-
import { useEffect } from 'react';
5+
import { useEffect, useState } from 'react';
66
import { GestureHandlerRootView } from 'react-native-gesture-handler';
77
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
88
import { setNavigationBar, themeAtom } from '@hooks/useColorScheme';
@@ -23,6 +23,9 @@ import advancedFormat from 'dayjs/plugin/advancedFormat'
2323
import relativeTime from 'dayjs/plugin/relativeTime';
2424
import { useAtom } from 'jotai';
2525
import { useColorScheme } from 'nativewind';
26+
import * as SplashScreen from 'expo-splash-screen';
27+
import * as SystemUI from 'expo-system-ui';
28+
import { Appearance } from 'react-native';
2629

2730
dayjs.extend(utc)
2831
dayjs.extend(timezone)
@@ -40,46 +43,77 @@ if (__DEV__) {
4043
]);
4144
}
4245

46+
// Prevent splash from auto-hiding before app is ready
47+
SplashScreen.preventAutoHideAsync()
48+
49+
SplashScreen.setOptions({
50+
duration: 200,
51+
fade: true,
52+
});
53+
4354
const messaging = getMessaging()
4455

4556
export default function RootLayout() {
4657

47-
// const path = usePathname()
48-
// console.log(path)
4958

50-
const { getItem } = useAsyncStorage(`default-site`)
59+
const [appIsReady, setAppIsReady] = useState(false);
60+
const { getItem } = useAsyncStorage(`default-site`);
61+
const { colorScheme, setColorScheme } = useColorScheme();
62+
const isDarkColorScheme = colorScheme === 'dark';
63+
const [theme] = useAtom(themeAtom);
64+
65+
// Set system UI background color early to prevent flash
66+
useEffect(() => {
67+
const setSystemBackground = async () => {
68+
const scheme = Appearance.getColorScheme();
69+
const bgColor = scheme === 'dark' ? '#121212' : '#ffffff';
70+
await SystemUI.setBackgroundColorAsync(bgColor);
71+
};
72+
setSystemBackground();
73+
}, []);
5174

5275

5376
useEffect(() => {
5477

5578
const onMount = async () => {
56-
// Get the defualt site from the async storage
57-
// Also check if the app was started by a notification
58-
const initialNotification = await messaging.getInitialNotification();
59-
60-
if (initialNotification) {
61-
if (initialNotification.data?.channel_id && initialNotification.data?.sitename) {
62-
setDefaultSite(initialNotification.data.sitename as string)
63-
let path = 'chat'
64-
if (initialNotification.data.is_thread) {
65-
path = 'thread'
79+
80+
try {
81+
// Get the defualt site from the async storage
82+
// Also check if the app was started by a notification
83+
const initialNotification = await messaging.getInitialNotification();
84+
85+
if (initialNotification) {
86+
if (initialNotification.data?.channel_id && initialNotification.data?.sitename) {
87+
setDefaultSite(initialNotification.data.sitename as string)
88+
let path = 'chat'
89+
if (initialNotification.data.is_thread) {
90+
path = 'thread'
91+
}
92+
router.navigate(`/${initialNotification.data.sitename}/${path}/${initialNotification.data.channel_id}`, {
93+
withAnchor: true
94+
})
95+
96+
return
6697
}
67-
router.navigate(`/${initialNotification.data.sitename}/${path}/${initialNotification.data.channel_id}`, {
68-
withAnchor: true
69-
})
98+
}
7099

71-
return
100+
// If not started by notification
101+
// On load, check if the user has a site set
102+
const defaultSite = await getItem()
103+
if (defaultSite) {
104+
router.replace(`/${defaultSite}`)
105+
} else {
106+
router.replace('/landing')
72107
}
73-
}
74108

75-
// If not started by notification
76-
// On load, check if the user has a site set
77-
const defaultSite = await getItem()
78-
if (defaultSite) {
79-
router.replace(`/${defaultSite}`)
80-
} else {
81-
router.replace('/landing')
109+
} catch (error) {
110+
console.warn('Error during app initialization:', error);
111+
router.replace('/landing');
112+
} finally {
113+
// mark app as ready regardless of success or failure
114+
setAppIsReady(true);
82115
}
116+
83117
}
84118

85119
// Handle notification open when app is in background
@@ -104,11 +138,12 @@ export default function RootLayout() {
104138
};
105139
}, []);
106140

107-
const { colorScheme, setColorScheme } = useColorScheme();
108-
109-
const isDarkColorScheme = colorScheme === 'dark'
110-
111-
const [theme] = useAtom(themeAtom);
141+
// Hide splash screen when app is ready
142+
useEffect(() => {
143+
if (appIsReady) {
144+
SplashScreen.hide();
145+
}
146+
}, [appIsReady]);
112147

113148
useEffect(() => {
114149
if (theme.state === 'hasData') {
@@ -157,4 +192,4 @@ export default function RootLayout() {
157192
</GestureHandlerRootView>
158193
</>
159194
)
160-
}
195+
}

apps/mobile/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"expo-router": "~4.0.20",
5656
"expo-secure-store": "~14.0.1",
5757
"expo-sharing": "~13.0.1",
58+
"expo-splash-screen": "~0.29.24",
5859
"expo-status-bar": "~2.0.1",
5960
"expo-system-ui": "~4.0.9",
6061
"expo-video": "~2.0.5",

frontend/src/components/feature/chat/ChatMessage/Renderers/PollMessage.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ const PollMessageBox = ({ data, messageID }: { data: Poll, messageID: string })
6060
{data.poll.is_anonymous ? <Badge color='blue' className={'w-fit'}>Anonymous</Badge> : null}
6161
</Flex>
6262
{data.poll.is_disabled ? <Text color="gray" size='1'>
63-
<IoLockClosed />
6463
Poll is now closed. No more votes will be accepted.
6564
</Text> : null}
6665
{data.poll.end_date && !data.poll.is_disabled && (

frontend/src/components/feature/polls/ViewPollVotes.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const ViewPollVotes = ({ poll }: ViewPollVotesProps) => {
5555
<Button variant='ghost' size={'1'} className='bg-transparent hover:text-accent-10 w-full'>{buttonText}</Button>
5656
</DrawerTrigger>
5757
<DrawerContent>
58-
<div className='h-[80vh]'>
58+
<div className='h-[80vh] overflow-y-auto'>
5959
<ViewPollVotesModalContent
6060
onClose={onClose}
6161
poll={poll} />
@@ -91,6 +91,7 @@ const ViewPollVotesModalContent = ({ onClose, poll }: ViewPollVotesModalContentP
9191
}
9292

9393
const VotesBlock = ({ votesData, poll }: { votesData: PollVotesResponse, poll: Poll }) => {
94+
9495
return (
9596
<Flex direction={'column'}>
9697
<Dialog.Title>
@@ -101,11 +102,10 @@ const VotesBlock = ({ votesData, poll }: { votesData: PollVotesResponse, poll: P
101102
</Dialog.Title>
102103

103104
<Separator className='w-full' />
104-
<ScrollArea className='h-[76vh]' type='scroll'>
105105
<Flex direction={'column'} className='py-4 pr-3' gap={'2'}>
106106
<Text size={'3'} weight={'bold'}>{poll.poll.question}</Text>
107-
{votesData && Object.keys(votesData).map((opt) => {
108-
const option = votesData[opt]
107+
{votesData && Object.keys(votesData).map((opt) => {
108+
const option = votesData[opt]
109109
const optionName = poll.poll.options.find(o => o.name === opt)?.option
110110
return (
111111
<div>
@@ -127,7 +127,6 @@ const VotesBlock = ({ votesData, poll }: { votesData: PollVotesResponse, poll: P
127127
)
128128
})}
129129
</Flex>
130-
</ScrollArea>
131130
</Flex>
132131
)
133132
}
@@ -142,4 +141,4 @@ const UserVote = ({ user_id }: { user_id: string }) => {
142141
<Text as='span' className='block' weight='medium' size='2'>{user?.full_name}</Text>
143142
</div>
144143
</Flex>
145-
}
144+
}

frontend/src/components/feature/threads/ThreadDrawer/ThreadHeader.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import useFetchChannelMembers, { Member } from "@/hooks/fetchers/useFetchChannel
1010
import { useContext, useMemo } from "react"
1111
import useIsPushNotificationEnabled from "@/hooks/fetchers/useIsPushNotificationEnabled"
1212
import { UserContext } from "@/utils/auth/UserProvider"
13+
import { LuExternalLink } from "react-icons/lu"
1314

14-
export const ThreadHeader = () => {
15+
export const ThreadHeader = ({ channelID }: { channelID?: string }) => {
1516

1617
const navigate = useNavigate()
1718

@@ -28,6 +29,10 @@ export const ThreadHeader = () => {
2829
return null
2930
}, [user, channelMembers])
3031

32+
const onViewMessageInChannel = () => {
33+
navigate(`../../${channelID}?message_id=${threadID}`)
34+
}
35+
3136
return (
3237
<header className='dark:bg-gray-2 bg-white fixed top-0 px-3 sm:w-[calc((100vw-var(--sidebar-width)-var(--space-8))/2)] w-screen' style={{ zIndex: 999 }}>
3338
<Flex direction={'column'} gap='2' className='pt-3'>
@@ -42,6 +47,12 @@ export const ThreadHeader = () => {
4247
</IconButton>
4348
</DropdownMenu.Trigger>
4449
<DropdownMenu.Content className='min-w-48'>
50+
{channelID && <DropdownMenu.Item onClick={onViewMessageInChannel}>
51+
<Flex gap='2' align='center'>
52+
<LuExternalLink size={'16'} />
53+
View Message
54+
</Flex>
55+
</DropdownMenu.Item>}
4556
<ToggleNotificationButton channelMember={channelMember} />
4657
<LeaveThreadButton />
4758
{channelMembers[currentUser].is_admin === 1 && <DeleteThreadButton />}

frontend/src/components/feature/threads/ThreadManager/ViewThread.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const ViewThread = () => {
2626
return (
2727
<div>
2828
<Flex direction='column' gap='0' className='w-full h-screen'>
29-
<ThreadHeader />
29+
<ThreadHeader channelID={data?.channel_id} />
3030
{isLoading && <FullPageLoader />}
3131
{error && <Box p='4'><ErrorBanner error={error} /></Box>}
3232
{data && <ThreadMessages threadMessage={data} key={threadID} />}

frontend/src/pages/settings/PushNotifications.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,7 @@ const PushNotifications = () => {
215215
<RegisterSiteButton mutate={mutate} ravenSettings={ravenSettings} />}
216216

217217
{isRavenCloud && ravenSettings?.push_notification_service === "Raven"
218-
&& ravenSettings?.push_notification_server_url && ravenSettings?.vapid_public_key && <Button
219-
onClick={() => call.post('raven.api.notification.sync_user_tokens_to_raven_cloud').then(() => {
220-
toast.success('Data syncing to Raven Cloud...')
221-
})}
222-
type='button'
223-
variant='soft'
224-
className='not-cal'>
225-
Sync Data to Raven Cloud
226-
</Button>}
218+
&& ravenSettings?.push_notification_server_url && ravenSettings?.vapid_public_key && <SyncDataButton />}
227219

228220
{!isRavenCloud && <Button
229221
asChild
@@ -270,4 +262,25 @@ const RegisterSiteButton = ({ mutate, ravenSettings }: { mutate: VoidFunction, r
270262

271263
}
272264

265+
const SyncDataButton = () => {
266+
const { call, loading } = useFrappePostCall('raven.api.notification.sync_user_tokens_to_raven_cloud')
267+
268+
const syncData = () => {
269+
toast.promise(call({}), {
270+
loading: 'Syncing data to Raven Cloud...',
271+
success: 'Data synced to Raven Cloud.',
272+
error: (error) => 'Failed to sync data to Raven Cloud. ' + (getErrorMessage(error))
273+
})
274+
}
275+
276+
return <Button
277+
onClick={syncData}
278+
disabled={loading}
279+
variant='soft'
280+
type='button'
281+
className='not-cal'>
282+
{loading ? "Syncing Data to Raven Cloud..." : "Sync Data to Raven Cloud"}
283+
</Button>
284+
}
285+
273286
export const Component = PushNotifications

raven/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "2.8.6"
1+
__version__ = "2.8.7"
22

33
from raven.raven_integrations.doctype.raven_incoming_webhook.raven_incoming_webhook import ( # noqa
44
handle_incoming_webhook as webhook,

raven/api/notification.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ def unsubscribe(fcm_token: str) -> None:
104104
Remove the FCM token from the database
105105
"""
106106

107-
frappe.db.delete("Raven Push Token", {"fcm_token": fcm_token, "user": frappe.session.user})
107+
# Check if the FCM token exists
108+
token_name = frappe.db.exists(
109+
"Raven Push Token", {"fcm_token": fcm_token, "user": frappe.session.user}
110+
)
111+
if not token_name:
112+
frappe.throw(_("FCM token not found"))
113+
114+
# Delete the FCM token from the database using delete_doc to ensure that on_trash method gets called to delete the token from RC/FCP.
115+
frappe.delete_doc("Raven Push Token", token_name)
108116

109117
return "Unsubscribed"

0 commit comments

Comments
 (0)