From d3707f30e30bb717e95b27cc83a1121815b475b5 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Thu, 10 Nov 2022 16:30:14 -0600 Subject: Implement scene invitation and membership controls --- src/view/com/modals/InviteToScene.tsx | 238 ++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 src/view/com/modals/InviteToScene.tsx (limited to 'src/view/com/modals/InviteToScene.tsx') diff --git a/src/view/com/modals/InviteToScene.tsx b/src/view/com/modals/InviteToScene.tsx new file mode 100644 index 000000000..f1c9d3386 --- /dev/null +++ b/src/view/com/modals/InviteToScene.tsx @@ -0,0 +1,238 @@ +import React, {useState, useEffect, useMemo} from 'react' +import Toast from '../util/Toast' +import { + ActivityIndicator, + FlatList, + StyleSheet, + Text, + useWindowDimensions, + View, +} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import { + TabView, + SceneMap, + Route, + TabBar, + TabBarProps, +} from 'react-native-tab-view' +import _omit from 'lodash.omit' +import {AtUri} from '../../../third-party/uri' +import {ProfileCard} from '../profile/ProfileCard' +import {ErrorMessage} from '../util/ErrorMessage' +import {useStores} from '../../../state' +import * as apilib from '../../../state/lib/api' +import {ProfileViewModel} from '../../../state/models/profile-view' +import {SceneInviteSuggestions} from '../../../state/models/scene-invite-suggestions' +import {FollowItem} from '../../../state/models/user-follows-view' +import {s, colors} from '../../lib/styles' + +export const snapPoints = ['70%'] + +export function Component({profileView}: {profileView: ProfileViewModel}) { + const store = useStores() + const layout = useWindowDimensions() + const [index, setIndex] = useState(0) + const tabRoutes = [ + {key: 'suggestions', title: 'Suggestions'}, + {key: 'pending', title: 'Pending Invites'}, + ] + const [hasSetup, setHasSetup] = useState(false) + const [error, setError] = useState('') + const suggestions = useMemo( + () => new SceneInviteSuggestions(store, {sceneDid: profileView.did}), + [profileView.did], + ) + const [createdInvites, setCreatedInvites] = useState>( + {}, + ) + + useEffect(() => { + let aborted = false + if (hasSetup) { + return + } + suggestions.setup().then(() => { + if (aborted) return + setHasSetup(true) + }) + return () => { + aborted = true + } + }, [profileView.did]) + + const onPressInvite = async (follow: FollowItem) => { + setError('') + try { + const assertionUri = await apilib.inviteToScene( + store, + profileView.did, + follow.did, + follow.declaration.cid, + ) + setCreatedInvites({[follow.did]: assertionUri, ...createdInvites}) + Toast.show('Invite sent', { + duration: Toast.durations.LONG, + position: Toast.positions.TOP, + }) + } catch (e) { + setError('There was an issue with the invite. Please try again.') + console.error(e) + } + } + const onPressUndo = async (subjectDid: string, assertionUri: string) => { + setError('') + const urip = new AtUri(assertionUri) + try { + await store.api.app.bsky.graph.assertion.delete({ + did: profileView.did, + rkey: urip.rkey, + }) + setCreatedInvites(_omit(createdInvites, [subjectDid])) + } catch (e) { + setError('There was an issue with the invite. Please try again.') + console.error(e) + } + } + + const renderSuggestionItem = ({item}: {item: FollowItem}) => { + const createdInvite = createdInvites[item.did] + return ( + + !createdInvite ? ( + <> + + Invite + + ) : ( + <> + + Undo invite + + ) + } + onPressButton={() => + !createdInvite + ? onPressInvite(item) + : onPressUndo(item.did, createdInvite) + } + /> + ) + } + + const Suggestions = () => ( + + {hasSetup ? ( + + + + User search is still being implemented. For now, you can pick from + your follows below. + + + {!suggestions.hasContent ? ( + + {suggestions.myFollowsView.follows.length + ? 'Sorry! You dont follow anybody for us to suggest.' + : 'Sorry! All of the users you follow are members already.'} + + ) : ( + item._reactKey} + renderItem={renderSuggestionItem} + style={s.flex1} + /> + )} + + ) : !error ? ( + + ) : undefined} + + ) + + const PendingInvites = () => ( + + + + Pending invites are still being implemented. Check back soon! + + + + ) + + const renderScene = SceneMap({ + suggestions: Suggestions, + pending: PendingInvites, + }) + + const renderTabBar = (props: TabBarProps) => ( + + ) + + return ( + + + Invite to {profileView.displayName || profileView.handle} + + {error !== '' ? ( + + + + ) : undefined} + + + ) +} + +const styles = StyleSheet.create({ + title: { + textAlign: 'center', + fontWeight: 'bold', + fontSize: 18, + marginBottom: 4, + }, + todoContainer: { + backgroundColor: colors.pink1, + margin: 10, + padding: 10, + borderRadius: 6, + }, + todoLabel: { + color: colors.pink5, + textAlign: 'center', + }, + + tabBar: { + flexDirection: 'row', + }, + tabItem: { + alignItems: 'center', + padding: 16, + flex: 1, + }, +}) -- cgit 1.4.1