From c7758c33d19d27358ffcecee2e9df40feaf90856 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Mon, 16 Jan 2023 13:57:44 -0600 Subject: [PATCH] Remove scene-related modals --- src/state/models/shell-ui.ts | 18 -- src/state/models/suggested-invites-view.ts | 142 ---------- src/view/com/modals/CreateScene.tsx | 243 ---------------- src/view/com/modals/InviteToScene.tsx | 308 --------------------- src/view/com/modals/Modal.tsx | 12 - src/view/screens/Profile.tsx | 80 +----- 6 files changed, 4 insertions(+), 799 deletions(-) delete mode 100644 src/state/models/suggested-invites-view.ts delete mode 100644 src/view/com/modals/CreateScene.tsx delete mode 100644 src/view/com/modals/InviteToScene.tsx diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts index 52d0816804..1af74f56aa 100644 --- a/src/state/models/shell-ui.ts +++ b/src/state/models/shell-ui.ts @@ -25,22 +25,6 @@ export class EditProfileModal { } } -export class CreateSceneModal { - name = 'create-scene' - - constructor() { - makeAutoObservable(this) - } -} - -export class InviteToSceneModal { - name = 'invite-to-scene' - - constructor(public profileView: ProfileViewModel) { - makeAutoObservable(this) - } -} - export class ServerInputModal { name = 'server-input' @@ -143,7 +127,6 @@ export class ShellUiModel { activeModal: | ConfirmModal | EditProfileModal - | CreateSceneModal | ServerInputModal | ReportPostModal | ReportAccountModal @@ -191,7 +174,6 @@ export class ShellUiModel { modal: | ConfirmModal | EditProfileModal - | CreateSceneModal | ServerInputModal | ReportPostModal | ReportAccountModal, diff --git a/src/state/models/suggested-invites-view.ts b/src/state/models/suggested-invites-view.ts deleted file mode 100644 index fadc956ccf..0000000000 --- a/src/state/models/suggested-invites-view.ts +++ /dev/null @@ -1,142 +0,0 @@ -import {makeAutoObservable, runInAction} from 'mobx' -import {RootStoreModel} from './root-store' -import {UserFollowsViewModel, FollowItem} from './user-follows-view' -import {GetAssertionsView} from './get-assertions-view' -import {APP_BSKY_SYSTEM, APP_BSKY_GRAPH} from '@atproto/api' - -export interface SuggestedInvitesViewParams { - sceneDid: string -} - -export class SuggestedInvitesView { - // state - isLoading = false - isRefreshing = false - hasLoaded = false - error = '' - params: SuggestedInvitesViewParams - sceneAssertionsView: GetAssertionsView - myFollowsView: UserFollowsViewModel - - // data - suggestions: FollowItem[] = [] - - constructor( - public rootStore: RootStoreModel, - params: SuggestedInvitesViewParams, - ) { - makeAutoObservable( - this, - { - rootStore: false, - params: false, - }, - {autoBind: true}, - ) - this.params = params - this.sceneAssertionsView = new GetAssertionsView(rootStore, { - author: params.sceneDid, - assertion: APP_BSKY_GRAPH.AssertMember, - }) - this.myFollowsView = new UserFollowsViewModel(rootStore, { - user: rootStore.me.did || '', - }) - } - - get hasContent() { - return this.suggestions.length > 0 - } - - get hasError() { - return this.error !== '' - } - - get isEmpty() { - return this.hasLoaded && !this.hasContent - } - - get unconfirmed() { - return this.sceneAssertionsView.unconfirmed - } - - // public api - // = - - async setup() { - await this._fetch(false) - } - - async refresh() { - await this._fetch(true) - } - - async loadMore() { - // TODO - } - - // state transitions - // = - - private _xLoading(isRefreshing = false) { - this.isLoading = true - this.isRefreshing = isRefreshing - this.error = '' - } - - private _xIdle(err?: any) { - this.isLoading = false - this.isRefreshing = false - this.hasLoaded = true - this.error = err ? err.toString() : '' - if (err) { - this.rootStore.log.error('Failed to fetch suggested invites', err) - } - } - - // loader functions - // = - - private async _fetch(isRefreshing = false) { - this._xLoading(isRefreshing) - try { - // TODO need to fetch all! - await this.sceneAssertionsView.setup() - } catch (e: any) { - this.rootStore.log.error( - 'Failed to fetch current scene members in suggested invites', - e, - ) - this._xIdle( - 'Failed to fetch the current scene members. Check your internet connection and try again.', - ) - return - } - try { - await this.myFollowsView.setup() - } catch (e: any) { - this.rootStore.log.error( - 'Failed to fetch current followers in suggested invites', - e, - ) - this._xIdle( - 'Failed to fetch the your current followers. Check your internet connection and try again.', - ) - return - } - - // collect all followed users that arent already in the scene - const newSuggestions: FollowItem[] = [] - for (const follow of this.myFollowsView.follows) { - if (follow.declaration.actorType !== APP_BSKY_SYSTEM.ActorUser) { - continue - } - if (!this.sceneAssertionsView.getBySubject(follow.did)) { - newSuggestions.push(follow) - } - } - runInAction(() => { - this.suggestions = newSuggestions - }) - this._xIdle() - } -} diff --git a/src/view/com/modals/CreateScene.tsx b/src/view/com/modals/CreateScene.tsx deleted file mode 100644 index d92fe2e315..0000000000 --- a/src/view/com/modals/CreateScene.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import React, {useState} from 'react' -import * as Toast from '../util/Toast' -import { - ActivityIndicator, - StyleSheet, - TouchableOpacity, - View, -} from 'react-native' -import LinearGradient from 'react-native-linear-gradient' -import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet' -import {AppBskyActorCreateScene} from '@atproto/api' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {Text} from '../util/text/Text' -import {useStores} from '../../../state' -import {s, colors, gradients} from '../../lib/styles' -import { - makeValidHandle, - createFullHandle, - enforceLen, - MAX_DISPLAY_NAME, - MAX_DESCRIPTION, -} from '../../../lib/strings' - -export const snapPoints = ['60%'] - -export function Component({}: {}) { - const store = useStores() - const [error, setError] = useState('') - const [isProcessing, setIsProcessing] = useState(false) - const [handle, setHandle] = useState('') - const [displayName, setDisplayName] = useState('') - const [description, setDescription] = useState('') - const onPressSave = async () => { - setIsProcessing(true) - if (error) { - setError('') - } - try { - if (!store.me.did) { - return - } - const desc = await store.api.com.atproto.server.getAccountsConfig() - const fullHandle = createFullHandle( - handle, - desc.data.availableUserDomains[0], - ) - // create scene actor - const createSceneRes = await store.api.app.bsky.actor.createScene({ - handle: fullHandle, - }) - // set the scene profile - await store.api.app.bsky.actor - .updateProfile({ - did: createSceneRes.data.did, - displayName, - description, - }) - .catch(e => - // an error here is not critical - store.log.error('Failed to update scene profile during creation', e), - ) - // follow the scene - await store.api.app.bsky.graph.follow - .create( - { - did: store.me.did, - }, - { - subject: { - did: createSceneRes.data.did, - declarationCid: createSceneRes.data.declaration.cid, - }, - createdAt: new Date().toISOString(), - }, - ) - .catch(e => - // an error here is not critical - store.log.error('Failed to follow scene after creation', e), - ) - Toast.show('Scene created') - store.shell.closeModal() - store.nav.navigate(`/profile/${fullHandle}`) - } catch (e: any) { - if (e instanceof AppBskyActorCreateScene.InvalidHandleError) { - setError( - 'The handle can only contain letters, numbers, and dashes, and must start with a letter.', - ) - } else if (e instanceof AppBskyActorCreateScene.HandleNotAvailableError) { - setError(`The handle "${handle}" is not available.`) - } else { - store.log.error('Failed to create scene', e) - setError( - 'Failed to create the scene. Check your internet connection and try again.', - ) - } - setIsProcessing(false) - } - } - const onPressCancel = () => { - store.shell.closeModal() - } - - return ( - - - Create a scene - - Scenes are invite-only groups which aggregate what's popular with - members. - - - - Scene Handle - setHandle(makeValidHandle(str))} - /> - - - Scene Display Name - - setDisplayName(enforceLen(v, MAX_DISPLAY_NAME)) - } - /> - - - Scene Description - setDescription(enforceLen(v, MAX_DESCRIPTION))} - /> - - {error !== '' && ( - - - - )} - {handle.length >= 2 && !isProcessing ? ( - - - Create Scene - - - ) : ( - - - {isProcessing ? ( - - ) : ( - Create Scene - )} - - - )} - - - Cancel - - - - - - ) -} - -const styles = StyleSheet.create({ - outer: { - flex: 1, - // paddingTop: 20, - }, - title: { - textAlign: 'center', - fontWeight: 'bold', - fontSize: 24, - marginBottom: 12, - }, - description: { - textAlign: 'center', - fontSize: 17, - paddingHorizontal: 22, - color: colors.gray5, - marginBottom: 10, - }, - inner: { - padding: 14, - height: 350, - }, - group: { - marginBottom: 10, - }, - label: { - fontSize: 16, - fontWeight: 'bold', - paddingHorizontal: 4, - paddingBottom: 4, - }, - textInput: { - borderWidth: 1, - borderColor: colors.gray3, - borderRadius: 6, - paddingHorizontal: 14, - paddingVertical: 10, - fontSize: 16, - color: colors.black, - }, - textArea: { - borderWidth: 1, - borderColor: colors.gray3, - borderRadius: 6, - paddingHorizontal: 12, - paddingTop: 10, - fontSize: 16, - color: colors.black, - height: 70, - textAlignVertical: 'top', - }, - btn: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - width: '100%', - borderRadius: 32, - padding: 14, - backgroundColor: colors.gray1, - }, -}) diff --git a/src/view/com/modals/InviteToScene.tsx b/src/view/com/modals/InviteToScene.tsx deleted file mode 100644 index 2b4d0ac291..0000000000 --- a/src/view/com/modals/InviteToScene.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import React, {useState, useEffect, useMemo} from 'react' -import {observer} from 'mobx-react-lite' -import * as Toast from '../util/Toast' -import { - ActivityIndicator, - FlatList, - StyleSheet, - 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/error/ErrorMessage' -import {Text} from '../util/text/Text' -import {useStores} from '../../../state' -import * as apilib from '../../../state/lib/api' -import {ProfileViewModel} from '../../../state/models/profile-view' -import {SuggestedInvitesView} from '../../../state/models/suggested-invites-view' -import {Assertion} from '../../../state/models/get-assertions-view' -import {FollowItem} from '../../../state/models/user-follows-view' -import {s, colors} from '../../lib/styles' - -export const snapPoints = ['70%'] - -export const Component = observer(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 SuggestedInvitesView(store, {sceneDid: profileView.did}), - [profileView.did], - ) - const [createdInvites, setCreatedInvites] = useState>( - {}, - ) - // TODO: it would be much better if we just used the suggestions view for the deleted pending invites - // but mobx isnt picking up on the state change in suggestions.unconfirmed and I dont have - // time to debug that right now -prf - const [deletedPendingInvites, setDeletedPendingInvites] = useState< - Record - >({}) - - 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') - } catch (e: any) { - setError('There was an issue with the invite. Please try again.') - store.log.error('Failed to invite user to scene', 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: any) { - setError('There was an issue with the invite. Please try again.') - store.log.error('Failed to delete a scene invite', e) - } - } - - const onPressDeleteInvite = async (assertion: Assertion) => { - setError('') - const urip = new AtUri(assertion.uri) - try { - await store.api.app.bsky.graph.assertion.delete({ - did: profileView.did, - rkey: urip.rkey, - }) - setDeletedPendingInvites({ - [assertion.uri]: true, - ...deletedPendingInvites, - }) - Toast.show('Invite removed') - } catch (e: any) { - setError('There was an issue with the invite. Please try again.') - store.log.error('Failed to delete an invite', 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 renderPendingInviteItem = ({item}: {item: Assertion}) => { - const wasDeleted = deletedPendingInvites[item.uri] - if (wasDeleted) { - return - } - return ( - ( - <> - - Undo invite - - )} - onPressButton={() => onPressDeleteInvite(item)} - /> - ) - } - - 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 = () => ( - - {suggestions.sceneAssertionsView.isLoading ? ( - - ) : undefined} - - {!suggestions.unconfirmed.length ? ( - - No pending invites. - - ) : ( - item._reactKey} - renderItem={renderPendingInviteItem} - style={s.flex1} - /> - )} - - - ) - - 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, - }, -}) diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 43271c9648..e0e18d54ae 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -9,8 +9,6 @@ import * as models from '../../../state/models/shell-ui' import * as ConfirmModal from './Confirm' import * as EditProfileModal from './EditProfile' -import * as CreateSceneModal from './CreateScene' -import * as InviteToSceneModal from './InviteToScene' import * as ServerInputModal from './ServerInput' import * as ReportPostModal from './ReportPost' import * as ReportAccountModal from './ReportAccount' @@ -55,16 +53,6 @@ export const Modal = observer(function Modal() { {...(store.shell.activeModal as models.EditProfileModal)} /> ) - } else if (store.shell.activeModal?.name === 'create-scene') { - snapPoints = CreateSceneModal.snapPoints - element = - } else if (store.shell.activeModal?.name === 'invite-to-scene') { - snapPoints = InviteToSceneModal.snapPoints - element = ( - - ) } else if (store.shell.activeModal?.name === 'server-input') { snapPoints = ServerInputModal.snapPoints element = ( diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 0fb30162cf..6d5f79e3c6 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -92,9 +92,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => { // rendering // = - const isSceneCreator = - uiState.isScene && store.me.did === uiState.profile.creator - const renderHeader = () => { if (!uiState) { return @@ -126,8 +123,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => { } else { if ( uiState.selectedView === Sections.Posts || - uiState.selectedView === Sections.PostsWithReplies || - uiState.selectedView === Sections.Trending + uiState.selectedView === Sections.PostsWithReplies ) { if (uiState.feed.hasContent) { if (uiState.selectedView === Sections.Posts) { @@ -147,80 +143,12 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => { return } } else if (uiState.feed.isEmpty) { - items = items.concat([EMPTY_ITEM]) - if (uiState.profile.isScene) { - renderItem = () => ( - - ) - } else { - renderItem = () => ( - - ) - } - } - } else if (uiState.selectedView === Sections.Scenes) { - if (uiState.memberships.hasContent) { - items = uiState.memberships.memberships.slice() - renderItem = (item: any) => { - return ( - - ) - } - } else if (uiState.memberships.isEmpty) { items = items.concat([EMPTY_ITEM]) renderItem = () => ( - ) - } - } else if (uiState.selectedView === Sections.Members) { - if (uiState.members.hasContent) { - items = uiState.members.members.slice() - renderItem = (item: any) => { - const shouldAdmin = isSceneCreator && item.did !== store.me.did - const renderButton = shouldAdmin - ? () => ( - <> - - Remove - - ) - : undefined - return ( - onPressRemoveMember(item)} - /> - ) - } - } else if (uiState.members.isEmpty) { - items = items.concat([EMPTY_ITEM]) - renderItem = () => ( - ) }