List cleanup on remove (#1069)
* 💄 Hide Add to List option on own profile * ✨ Remove Lists tab when last list is removed * ✨ Add listener to list delete on profile screen * ✨ Only show save changes in list modal when changes are madezio/stable
parent
38d78e16bf
commit
eec300d772
|
@ -217,6 +217,8 @@ export class ListModel {
|
||||||
records.map(record => createDel(record.uri)),
|
records.map(record => createDel(record.uri)),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.rootStore.emitListDeleted(this.uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
async subscribe() {
|
async subscribe() {
|
||||||
|
|
|
@ -48,9 +48,24 @@ export class ListsListModel {
|
||||||
return this.hasLoaded && !this.hasContent
|
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
|
// 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() {
|
async refresh() {
|
||||||
return this.loadMore(true)
|
return this.loadMore(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,14 @@ export class RootStoreModel {
|
||||||
DeviceEventEmitter.emit('post-deleted', uri)
|
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
|
// the session has started and been fully hydrated
|
||||||
onSessionLoaded(handler: () => void): EmitterSubscription {
|
onSessionLoaded(handler: () => void): EmitterSubscription {
|
||||||
return DeviceEventEmitter.addListener('session-loaded', handler)
|
return DeviceEventEmitter.addListener('session-loaded', handler)
|
||||||
|
|
|
@ -87,7 +87,10 @@ export class ProfileUiModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get selectedView() {
|
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() {
|
get uiItems() {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {isDesktopWeb, isAndroid} from 'platform/detection'
|
import {isDesktopWeb, isAndroid} from 'platform/detection'
|
||||||
|
import isEqual from 'lodash.isequal'
|
||||||
|
|
||||||
export const snapPoints = ['fullscreen']
|
export const snapPoints = ['fullscreen']
|
||||||
|
|
||||||
|
@ -37,6 +38,9 @@ export const Component = observer(
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const palPrimary = usePalette('primary')
|
const palPrimary = usePalette('primary')
|
||||||
const palInverted = usePalette('inverted')
|
const palInverted = usePalette('inverted')
|
||||||
|
const [originalSelections, setOriginalSelections] = React.useState<
|
||||||
|
string[]
|
||||||
|
>([])
|
||||||
const [selected, setSelected] = React.useState<string[]>([])
|
const [selected, setSelected] = React.useState<string[]>([])
|
||||||
|
|
||||||
const listsList: ListsListModel = React.useMemo(
|
const listsList: ListsListModel = React.useMemo(
|
||||||
|
@ -51,7 +55,9 @@ export const Component = observer(
|
||||||
listsList.refresh()
|
listsList.refresh()
|
||||||
memberships.fetch().then(
|
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 => {
|
err => {
|
||||||
store.log.error('Failed to fetch memberships', {err})
|
store.log.error('Failed to fetch memberships', {err})
|
||||||
|
@ -156,6 +162,10 @@ export const Component = observer(
|
||||||
)
|
)
|
||||||
}, [onPressNewMuteList])
|
}, [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 (
|
return (
|
||||||
<View testID="listAddRemoveUserModal" style={s.hContentRegion}>
|
<View testID="listAddRemoveUserModal" style={s.hContentRegion}>
|
||||||
<Text style={[styles.title, pal.text]}>Add {displayName} to Lists</Text>
|
<Text style={[styles.title, pal.text]}>Add {displayName} to Lists</Text>
|
||||||
|
@ -178,16 +188,18 @@ export const Component = observer(
|
||||||
onAccessibilityEscape={onPressCancel}
|
onAccessibilityEscape={onPressCancel}
|
||||||
label="Cancel"
|
label="Cancel"
|
||||||
/>
|
/>
|
||||||
<Button
|
{canSaveChanges && (
|
||||||
testID="saveBtn"
|
<Button
|
||||||
type="primary"
|
testID="saveBtn"
|
||||||
onPress={onPressSave}
|
type="primary"
|
||||||
style={styles.footerBtn}
|
onPress={onPressSave}
|
||||||
accessibilityLabel="Save changes"
|
style={styles.footerBtn}
|
||||||
accessibilityHint=""
|
accessibilityLabel="Save changes"
|
||||||
onAccessibilityEscape={onPressSave}
|
accessibilityHint=""
|
||||||
label="Save Changes"
|
onAccessibilityEscape={onPressSave}
|
||||||
/>
|
label="Save Changes"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
@ -56,6 +56,13 @@ export const ProfileScreen = withAuthRequired(
|
||||||
setHasSetup(false)
|
setHasSetup(false)
|
||||||
}, [route.params.name])
|
}, [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(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
const softResetSub = store.onScreenSoftReset(onSoftReset)
|
const softResetSub = store.onScreenSoftReset(onSoftReset)
|
||||||
|
@ -126,6 +133,7 @@ export const ProfileScreen = withAuthRequired(
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, [uiState, onRefresh, route.params.hideBackButton])
|
}, [uiState, onRefresh, route.params.hideBackButton])
|
||||||
|
|
||||||
const Footer = React.useMemo(() => {
|
const Footer = React.useMemo(() => {
|
||||||
return uiState.showLoadingMoreFooter ? LoadingMoreFooter : undefined
|
return uiState.showLoadingMoreFooter ? LoadingMoreFooter : undefined
|
||||||
}, [uiState.showLoadingMoreFooter])
|
}, [uiState.showLoadingMoreFooter])
|
||||||
|
|
Loading…
Reference in New Issue