diff --git a/app/components/team_sidebar/team_sidebar.tsx b/app/components/team_sidebar/team_sidebar.tsx index bab5239ee..a8f0ccd84 100644 --- a/app/components/team_sidebar/team_sidebar.tsx +++ b/app/components/team_sidebar/team_sidebar.tsx @@ -14,7 +14,7 @@ import TeamList from './team_list'; type Props = { iconPad?: boolean; canJoinOtherTeams: boolean; - teamsCount: number; + hasMoreThanOneTeam: boolean; } const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { @@ -36,8 +36,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }; }); -export default function TeamSidebar({iconPad, canJoinOtherTeams, teamsCount}: Props) { - const initialWidth = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0; +export default function TeamSidebar({iconPad, canJoinOtherTeams, hasMoreThanOneTeam}: Props) { + const initialWidth = hasMoreThanOneTeam ? TEAM_SIDEBAR_WIDTH : 0; const width = useSharedValue(initialWidth); const marginTop = useSharedValue(iconPad ? 44 : 0); const theme = useTheme(); @@ -58,8 +58,8 @@ export default function TeamSidebar({iconPad, canJoinOtherTeams, teamsCount}: Pr }, [iconPad]); useEffect(() => { - width.value = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0; - }, [teamsCount]); + width.value = hasMoreThanOneTeam ? TEAM_SIDEBAR_WIDTH : 0; + }, [hasMoreThanOneTeam]); return ( diff --git a/app/screens/home/channel_list/categories_list/categories/body/index.ts b/app/screens/home/channel_list/categories_list/categories/body/index.ts index f39675d21..cf3c8842c 100644 --- a/app/screens/home/channel_list/categories_list/categories/body/index.ts +++ b/app/screens/home/channel_list/categories_list/categories/body/index.ts @@ -3,7 +3,7 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; +import {of as of$, Observable} from 'rxjs'; import {switchMap, combineLatestWith, distinctUntilChanged} from 'rxjs/operators'; import {Preferences} from '@constants'; @@ -19,6 +19,7 @@ import CategoryBody from './category_body'; import type {WithDatabaseArgs} from '@typings/database/database'; import type CategoryModel from '@typings/database/models/servers/category'; import type ChannelModel from '@typings/database/models/servers/channel'; +import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type PreferenceModel from '@typings/database/models/servers/preference'; type EnhanceProps = { @@ -30,8 +31,7 @@ type EnhanceProps = { const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)})); -const observeCategoryChannels = (category: CategoryModel) => { - const myChannels = category.myChannels.observeWithColumns(['last_post_at', 'is_unread']); +const observeCategoryChannels = (category: CategoryModel, myChannels: Observable) => { const channels = category.channels.observeWithColumns(['create_at', 'display_name']); const manualSort = category.categoryChannelsBySortOrder.observeWithColumns(['sort_order']); return myChannels.pipe( @@ -57,7 +57,8 @@ const observeCategoryChannels = (category: CategoryModel) => { }; const enhanced = withObservables([], ({category, currentUserId, database, isTablet, locale}: EnhanceProps) => { - const channelsWithMyChannel = observeCategoryChannels(category); + const categoryMyChannels = category.myChannels.observeWithColumns(['last_post_at', 'is_unread']); + const channelsWithMyChannel = observeCategoryChannels(category, categoryMyChannels); const currentChannelId = isTablet ? observeCurrentChannelId(database) : of$(''); const lastUnreadId = isTablet ? observeLastUnreadChannelId(database) : of$(undefined); @@ -77,9 +78,9 @@ const enhanced = withObservables([], ({category, currentUserId, database, isTabl ); } - const notifyPropsPerChannel = channelsWithMyChannel.pipe( + const notifyPropsPerChannel = categoryMyChannels.pipe( // eslint-disable-next-line max-nested-callbacks - switchMap((cwms) => observeNotifyPropsByChannels(database, cwms.map((c) => c.myChannel))), + switchMap((mc) => observeNotifyPropsByChannels(database, mc)), ); const hiddenDmPrefs = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.DIRECT_CHANNEL_SHOW, undefined, 'false'). diff --git a/app/screens/home/channel_list/categories_list/header/index.ts b/app/screens/home/channel_list/categories_list/header/index.ts index 1aa6d23eb..e726d1491 100644 --- a/app/screens/home/channel_list/categories_list/header/index.ts +++ b/app/screens/home/channel_list/categories_list/header/index.ts @@ -4,7 +4,7 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {distinctUntilChanged, switchMap} from 'rxjs/operators'; import {Permissions} from '@constants'; import {observePermissionForTeam} from '@queries/servers/role'; @@ -25,6 +25,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const canJoinChannels = combineLatest([currentUser, team]).pipe( switchMap(([u, t]) => observePermissionForTeam(database, t, u, Permissions.JOIN_PUBLIC_CHANNELS, true)), + distinctUntilChanged(), ); const canCreatePublicChannels = combineLatest([currentUser, team]).pipe( @@ -37,6 +38,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe( switchMap(([open, priv]) => of$(open || priv)), + distinctUntilChanged(), ); const canAddUserToTeam = combineLatest([currentUser, team]).pipe( @@ -48,9 +50,11 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { canJoinChannels, canInvitePeople: combineLatest([enableOpenServer, canAddUserToTeam]).pipe( switchMap(([openServer, addUser]) => of$(openServer && addUser)), + distinctUntilChanged(), ), displayName: team.pipe( switchMap((t) => of$(t?.displayName)), + distinctUntilChanged(), ), pushProxyStatus: observePushVerificationStatus(database), }; diff --git a/app/screens/home/channel_list/categories_list/index.test.tsx b/app/screens/home/channel_list/categories_list/index.test.tsx index 77bfff155..670636551 100644 --- a/app/screens/home/channel_list/categories_list/index.test.tsx +++ b/app/screens/home/channel_list/categories_list/index.test.tsx @@ -33,8 +33,8 @@ describe('components/categories_list', () => { it('should render', () => { const wrapper = renderWithEverything( , {database}, ); @@ -46,8 +46,8 @@ describe('components/categories_list', () => { const wrapper = renderWithEverything( , {database}, ); @@ -67,8 +67,8 @@ describe('components/categories_list', () => { jest.useFakeTimers(); const wrapper = renderWithEverything( , {database}, ); @@ -89,8 +89,8 @@ describe('components/categories_list', () => { jest.useFakeTimers(); const wrapper = renderWithEverything( , {database}, ); diff --git a/app/screens/home/channel_list/categories_list/index.tsx b/app/screens/home/channel_list/categories_list/index.tsx index d406c82a4..7287ce050 100644 --- a/app/screens/home/channel_list/categories_list/index.tsx +++ b/app/screens/home/channel_list/categories_list/index.tsx @@ -27,28 +27,28 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ })); type ChannelListProps = { - channelsCount: number; + hasChannels: boolean; iconPad?: boolean; isCRTEnabled?: boolean; - teamsCount: number; + moreThanOneTeam: boolean; }; -const getTabletWidth = (teamsCount: number) => { - return TABLET_SIDEBAR_WIDTH - (teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0); +const getTabletWidth = (moreThanOneTeam: boolean) => { + return TABLET_SIDEBAR_WIDTH - (moreThanOneTeam ? TEAM_SIDEBAR_WIDTH : 0); }; -const CategoriesList = ({channelsCount, iconPad, isCRTEnabled, teamsCount}: ChannelListProps) => { +const CategoriesList = ({hasChannels, iconPad, isCRTEnabled, moreThanOneTeam}: ChannelListProps) => { const theme = useTheme(); const styles = getStyleSheet(theme); const {width} = useWindowDimensions(); const isTablet = useIsTablet(); - const tabletWidth = useSharedValue(isTablet ? getTabletWidth(teamsCount) : 0); + const tabletWidth = useSharedValue(isTablet ? getTabletWidth(moreThanOneTeam) : 0); useEffect(() => { if (isTablet) { - tabletWidth.value = getTabletWidth(teamsCount); + tabletWidth.value = getTabletWidth(moreThanOneTeam); } - }, [isTablet && teamsCount]); + }, [isTablet && moreThanOneTeam]); const tabletStyle = useAnimatedStyle(() => { if (!isTablet) { @@ -61,7 +61,7 @@ const CategoriesList = ({channelsCount, iconPad, isCRTEnabled, teamsCount}: Chan }, [isTablet, width]); const content = useMemo(() => { - if (channelsCount < 1) { + if (!hasChannels) { return (); } diff --git a/app/screens/home/channel_list/channel_list.tsx b/app/screens/home/channel_list/channel_list.tsx index ab521a5e8..ff3a9412b 100644 --- a/app/screens/home/channel_list/channel_list.tsx +++ b/app/screens/home/channel_list/channel_list.tsx @@ -29,9 +29,10 @@ import Servers from './servers'; import type {LaunchType} from '@typings/launch'; type ChannelProps = { - channelsCount: number; + hasChannels: boolean; isCRTEnabled: boolean; - teamsCount: number; + hasTeams: boolean; + hasMoreThanOneTeam: boolean; isLicensed: boolean; showToS: boolean; launchType: LaunchType; @@ -126,10 +127,10 @@ const ChannelListScreen = (props: ChannelProps) => { }, [theme, insets.top]); useEffect(() => { - if (!props.teamsCount) { + if (!props.hasTeams) { resetToTeams(); } - }, [Boolean(props.teamsCount)]); + }, [Boolean(props.hasTeams)]); useEffect(() => { const back = BackHandler.addEventListener('hardwareBackPress', handleBackPress); @@ -176,13 +177,13 @@ const ChannelListScreen = (props: ChannelProps) => { > {isTablet && diff --git a/app/screens/home/channel_list/index.ts b/app/screens/home/channel_list/index.ts index 27064044d..c1372a1bc 100644 --- a/app/screens/home/channel_list/index.ts +++ b/app/screens/home/channel_list/index.ts @@ -4,7 +4,7 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {distinctUntilChanged, switchMap} from 'rxjs/operators'; import {queryAllMyChannelsForTeam} from '@queries/servers/channel'; import {observeCurrentTeamId, observeLicense} from '@queries/servers/system'; @@ -21,11 +21,22 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { switchMap((lcs) => (lcs ? of$(lcs.IsLicensed === 'true') : of$(false))), ); + const teamsCount = queryMyTeams(database).observeCount(false); + return { isCRTEnabled: observeIsCRTEnabled(database), - teamsCount: queryMyTeams(database).observeCount(false), - channelsCount: observeCurrentTeamId(database).pipe( + hasTeams: teamsCount.pipe( + switchMap((v) => of$(v > 0)), + distinctUntilChanged(), + ), + hasMoreThanOneTeam: teamsCount.pipe( + switchMap((v) => of$(v > 1)), + distinctUntilChanged(), + ), + hasChannels: observeCurrentTeamId(database).pipe( switchMap((id) => (id ? queryAllMyChannelsForTeam(database, id).observeCount(false) : of$(0))), + switchMap((v) => of$(v > 0)), + distinctUntilChanged(), ), isLicensed, showToS: observeShowToS(database), diff --git a/app/utils/categories.ts b/app/utils/categories.ts index 2cd350f73..c871da9a7 100644 --- a/app/utils/categories.ts +++ b/app/utils/categories.ts @@ -3,7 +3,7 @@ import {General, Preferences} from '@constants'; import {DMS_CATEGORY} from '@constants/categories'; -import {getPreferenceAsBool, getPreferenceValue} from '@helpers/api/preference'; +import {getPreferenceAsBool} from '@helpers/api/preference'; import {isDMorGM} from '@utils/channel'; import {getUserIdFromChannelName} from '@utils/user'; @@ -42,15 +42,18 @@ export const filterAutoclosedDMs = ( // Only autoclose DMs that haven't been assigned to a category return channelsWithMyChannel; } - + const prefMap = preferences.reduce((acc, v) => { + const existing = acc.get(v.name); + acc.set(v.name, Math.max((v.value as unknown as number) || 0, existing || 0)); + return acc; + }, new Map()); const getLastViewedAt = (cwm: ChannelWithMyChannel) => { // The server only ever sets the last_viewed_at to the time of the last post in channel, so we may need // to use the preferences added for the previous version of autoclosing DMs. const id = cwm.channel.id; return Math.max( cwm.myChannel.lastViewedAt, - getPreferenceValue(preferences, Preferences.CATEGORIES.CHANNEL_APPROXIMATE_VIEW_TIME, id, 0), - getPreferenceValue(preferences, Preferences.CATEGORIES.CHANNEL_OPEN_TIME, id, 0), + prefMap.get(id) || 0, ); }; @@ -178,7 +181,7 @@ export const sortChannels = (sorting: CategorySorting, channelsWithMyChannel: Ch }).map((cwm) => cwm.channel); } else if (sorting === 'manual') { return channelsWithMyChannel.sort((cwmA, cwmB) => { - return cwmB.sortOrder - cwmA.sortOrder; + return cwmA.sortOrder - cwmB.sortOrder; }).map((cwm) => cwm.channel); }