@@ -15,6 +15,10 @@ import SiteSwitcher from '../auth/SiteSwitcher'
1515import { getSiteNameFromUrl } from '@raven/lib/utils/operations'
1616import ServerIcon from '@assets/icons/ServerIcon.svg'
1717import AddSite from '../auth/AddSite'
18+ import { FrappeError , useFrappePostCall , useSWRConfig } from 'frappe-react-sdk'
19+ import { toast } from 'sonner-native'
20+ import { getErrorMessage } from '@components/common/ErrorBanner'
21+ import { ActivityIndicator } from '@components/nativewindui/ActivityIndicator'
1822
1923const WorkspaceSwitcher = ( { workspace, setWorkspace } : { workspace : string , setWorkspace : ( workspace : string ) => Promise < void > } ) => {
2024
@@ -108,6 +112,9 @@ interface SelectWorkspaceSheetProps {
108112
109113const SelectWorkspaceSheet = ( { selectedWorkspace, workspaces, setWorkspace } : SelectWorkspaceSheetProps ) => {
110114
115+ const { call : joinWorkspace , loading : joiningWorkspace } = useFrappePostCall ( 'raven.api.workspaces.join_workspace' )
116+ const { mutate } = useSWRConfig ( )
117+
111118 const { myWorkspaces, otherWorkspaces } = useMemo ( ( ) => {
112119 const myWorkspaces : Workspace [ ] = [ ]
113120 const otherWorkspaces : Workspace [ ] = [ ]
@@ -127,6 +134,26 @@ const SelectWorkspaceSheet = ({ selectedWorkspace, workspaces, setWorkspace }: S
127134 return { myWorkspaces, otherWorkspaces }
128135 } , [ workspaces , selectedWorkspace ] )
129136
137+ const handleJoinOtherWorkspace = useCallback (
138+ ( workspaceName : string ) => {
139+ const displayName =
140+ workspaces . find ( ( w ) => w . name === workspaceName ) ?. workspace_name ?? workspaceName
141+
142+ joinWorkspace ( { workspace : workspaceName } )
143+ . then ( ( ) => Promise . all ( [ mutate ( 'workspaces_list' ) , mutate ( 'channel_list' ) ] ) )
144+ . then ( ( ) => setWorkspace ( workspaceName ) )
145+ . then ( ( ) => {
146+ toast . success ( `You have joined ${ displayName } .` )
147+ } )
148+ . catch ( ( error : unknown ) => {
149+ toast . error (
150+ getErrorMessage ( error as FrappeError ) || 'Failed to join the workspace.'
151+ )
152+ } )
153+ } ,
154+ [ joinWorkspace , setWorkspace , workspaces ]
155+ )
156+
130157 const siteInfo = useSiteContext ( )
131158
132159 const urlWithoutProtocol = useMemo ( ( ) => {
@@ -163,6 +190,8 @@ const SelectWorkspaceSheet = ({ selectedWorkspace, workspaces, setWorkspace }: S
163190 workspace = { workspace }
164191 setWorkspace = { setWorkspace }
165192 isOtherWorkspace
193+ isJoining = { joiningWorkspace }
194+ onJoinOtherWorkspace = { handleJoinOtherWorkspace }
166195 isLast = { index === otherWorkspaces . length - 1 }
167196 />
168197 ) ) }
@@ -173,18 +202,45 @@ const SelectWorkspaceSheet = ({ selectedWorkspace, workspaces, setWorkspace }: S
173202 )
174203}
175204
176- const WorkspaceRow = ( { workspace, isLast, setWorkspace, isOtherWorkspace = false } : { workspace : Workspace , isLast : boolean , setWorkspace : ( workspace : string ) => Promise < void > , isOtherWorkspace ?: boolean } ) => {
205+ type WorkspaceRowProps = {
206+ workspace : Workspace
207+ isLast : boolean
208+ setWorkspace : ( workspace : string ) => Promise < void >
209+ isOtherWorkspace ?: boolean
210+ /** True while this row's join request is in flight */
211+ isJoining ?: boolean
212+ onJoinOtherWorkspace ?: ( workspaceName : string ) => void
213+ }
214+
215+ const WorkspaceRow = ( {
216+ workspace,
217+ isLast,
218+ setWorkspace,
219+ isOtherWorkspace = false ,
220+ isJoining = false ,
221+ onJoinOtherWorkspace,
222+ } : WorkspaceRowProps ) => {
177223 const { colors } = useColorScheme ( )
178224
179- const onClick = ( ) => {
225+ const onPress = ( ) => {
226+ if ( isOtherWorkspace && onJoinOtherWorkspace ) {
227+ void onJoinOtherWorkspace ( workspace . name )
228+ return
229+ }
180230 if ( ! isOtherWorkspace ) {
181- setWorkspace ( workspace . name )
231+ void setWorkspace ( workspace . name )
182232 }
183233 }
184234
235+ const disabled = isOtherWorkspace && isJoining
236+
185237 return (
186- < Pressable className = 'flex flex-col gap-2' onPress = { onClick } >
187- < View className = 'flex-row items-center gap-2' >
238+ < Pressable
239+ className = 'flex flex-col gap-2'
240+ onPress = { onPress }
241+ disabled = { disabled }
242+ >
243+ < View className = { `flex-row items-center gap-2 ${ disabled ? 'opacity-60' : '' } ` } >
188244 < UserAvatar
189245 alt = { workspace . workspace_name }
190246 src = { getLogo ( workspace ) }
@@ -194,11 +250,13 @@ const WorkspaceRow = ({ workspace, isLast, setWorkspace, isOtherWorkspace = fals
194250 < Text className = 'text-sm font-semibold' > { workspace . workspace_name } </ Text >
195251 < Text className = 'text-sm text-gray-500' > { workspace . type } </ Text >
196252 </ View >
197- { workspace . isSelected &&
253+ { isJoining ? (
254+ < ActivityIndicator size = "small" color = { colors . primary } />
255+ ) : workspace . isSelected ? (
198256 < View className = 'mr-2' >
199257 < CheckFilledIcon fill = { colors . primary } height = { 20 } width = { 20 } />
200258 </ View >
201- }
259+ ) : null }
202260 </ View >
203261 { ! isLast && < Divider className = 'mx-0' /> }
204262 </ Pressable >
@@ -216,4 +274,4 @@ const getLogo = (workspace: WorkspaceFields) => {
216274 return logo
217275}
218276
219- export default WorkspaceSwitcher
277+ export default WorkspaceSwitcher
0 commit comments