diff options
author | Foysal Ahamed <foysal@blueskyweb.xyz> | 2023-07-28 18:04:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-28 11:04:27 -0500 |
commit | eec300d77241925e6b42e5e7e51894f2cba50e18 (patch) | |
tree | 0fb74f898687a6331cba3a3f0cffe3354596f4ab /src | |
parent | 38d78e16bffc9a25a45a4ad41caeef2c075daa26 (diff) | |
download | voidsky-eec300d77241925e6b42e5e7e51894f2cba50e18.tar.zst |
List cleanup on remove (#1069)
* :lipstick: Hide Add to List option on own profile * :sparkles: Remove Lists tab when last list is removed * :sparkles: Add listener to list delete on profile screen * :sparkles: Only show save changes in list modal when changes are made
Diffstat (limited to 'src')
-rw-r--r-- | src/state/models/content/list.ts | 2 | ||||
-rw-r--r-- | src/state/models/lists/lists-list.ts | 15 | ||||
-rw-r--r-- | src/state/models/root-store.ts | 8 | ||||
-rw-r--r-- | src/state/models/ui/profile.ts | 5 | ||||
-rw-r--r-- | src/view/com/modals/ListAddRemoveUser.tsx | 34 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 8 |
6 files changed, 60 insertions, 12 deletions
diff --git a/src/state/models/content/list.ts b/src/state/models/content/list.ts index c5ac72e49..2498cf581 100644 --- a/src/state/models/content/list.ts +++ b/src/state/models/content/list.ts @@ -217,6 +217,8 @@ export class ListModel { records.map(record => createDel(record.uri)), ), }) + + this.rootStore.emitListDeleted(this.uri) } async subscribe() { diff --git a/src/state/models/lists/lists-list.ts b/src/state/models/lists/lists-list.ts index 6618c3bf6..54e2f5fde 100644 --- a/src/state/models/lists/lists-list.ts +++ b/src/state/models/lists/lists-list.ts @@ -48,9 +48,24 @@ export class ListsListModel { return this.hasLoaded && !this.hasContent } + /** + * Removes posts from the feed upon deletion. + */ + onListDeleted(uri: string) { + this.lists = this.lists.filter(l => l.uri !== uri) + } + // public api // = + /** + * Register any event listeners. Returns a cleanup function. + */ + registerListeners() { + const sub = this.rootStore.onListDeleted(this.onListDeleted.bind(this)) + return () => sub.remove() + } + async refresh() { return this.loadMore(true) } diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts index 389ce86d8..d76ea07c9 100644 --- a/src/state/models/root-store.ts +++ b/src/state/models/root-store.ts @@ -188,6 +188,14 @@ export class RootStoreModel { DeviceEventEmitter.emit('post-deleted', uri) } + // a list was deleted by the local user + onListDeleted(handler: (uri: string) => void): EmitterSubscription { + return DeviceEventEmitter.addListener('list-deleted', handler) + } + emitListDeleted(uri: string) { + DeviceEventEmitter.emit('list-deleted', uri) + } + // the session has started and been fully hydrated onSessionLoaded(handler: () => void): EmitterSubscription { return DeviceEventEmitter.addListener('session-loaded', handler) diff --git a/src/state/models/ui/profile.ts b/src/state/models/ui/profile.ts index 81daf797f..a0249d768 100644 --- a/src/state/models/ui/profile.ts +++ b/src/state/models/ui/profile.ts @@ -87,7 +87,10 @@ export class ProfileUiModel { } get selectedView() { - return this.selectorItems[this.selectedViewIndex] + // If, for whatever reason, the selected view index is not available, default back to posts + // This can happen when the user was focused on a view but performed an action that caused + // the view to disappear (e.g. deleting the last list in their list of lists https://imgflip.com/i/7txu1y) + return this.selectorItems[this.selectedViewIndex] || Sections.Posts } get uiItems() { diff --git a/src/view/com/modals/ListAddRemoveUser.tsx b/src/view/com/modals/ListAddRemoveUser.tsx index 49f46e741..0f001f911 100644 --- a/src/view/com/modals/ListAddRemoveUser.tsx +++ b/src/view/com/modals/ListAddRemoveUser.tsx @@ -20,6 +20,7 @@ import {sanitizeHandle} from 'lib/strings/handles' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {isDesktopWeb, isAndroid} from 'platform/detection' +import isEqual from 'lodash.isequal' export const snapPoints = ['fullscreen'] @@ -37,6 +38,9 @@ export const Component = observer( const pal = usePalette('default') const palPrimary = usePalette('primary') const palInverted = usePalette('inverted') + const [originalSelections, setOriginalSelections] = React.useState< + string[] + >([]) const [selected, setSelected] = React.useState<string[]>([]) const listsList: ListsListModel = React.useMemo( @@ -51,7 +55,9 @@ export const Component = observer( listsList.refresh() memberships.fetch().then( () => { - setSelected(memberships.memberships.map(m => m.value.list)) + const ids = memberships.memberships.map(m => m.value.list) + setOriginalSelections(ids) + setSelected(ids) }, err => { store.log.error('Failed to fetch memberships', {err}) @@ -156,6 +162,10 @@ export const Component = observer( ) }, [onPressNewMuteList]) + // Only show changes button if there are some items on the list to choose from AND user has made changes in selection + const canSaveChanges = + !listsList.isEmpty && !isEqual(selected, originalSelections) + return ( <View testID="listAddRemoveUserModal" style={s.hContentRegion}> <Text style={[styles.title, pal.text]}>Add {displayName} to Lists</Text> @@ -178,16 +188,18 @@ export const Component = observer( onAccessibilityEscape={onPressCancel} label="Cancel" /> - <Button - testID="saveBtn" - type="primary" - onPress={onPressSave} - style={styles.footerBtn} - accessibilityLabel="Save changes" - accessibilityHint="" - onAccessibilityEscape={onPressSave} - label="Save Changes" - /> + {canSaveChanges && ( + <Button + testID="saveBtn" + type="primary" + onPress={onPressSave} + style={styles.footerBtn} + accessibilityLabel="Save changes" + accessibilityHint="" + onAccessibilityEscape={onPressSave} + label="Save Changes" + /> + )} </View> </View> ) diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 390266440..f00585336 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -56,6 +56,13 @@ export const ProfileScreen = withAuthRequired( setHasSetup(false) }, [route.params.name]) + // We don't need this to be reactive, so we can just register the listeners once + useEffect(() => { + const listCleanup = uiState.lists.registerListeners() + return () => listCleanup() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + useFocusEffect( React.useCallback(() => { const softResetSub = store.onScreenSoftReset(onSoftReset) @@ -126,6 +133,7 @@ export const ProfileScreen = withAuthRequired( /> ) }, [uiState, onRefresh, route.params.hideBackButton]) + const Footer = React.useMemo(() => { return uiState.showLoadingMoreFooter ? LoadingMoreFooter : undefined }, [uiState.showLoadingMoreFooter]) |