diff --git a/app/actions/local/channel.ts b/app/actions/local/channel.ts index 403464445e..f5d01235d8 100644 --- a/app/actions/local/channel.ts +++ b/app/actions/local/channel.ts @@ -151,7 +151,7 @@ export const markChannelAsViewed = async (serverUrl: string, channelId: string, } member.prepareUpdate((m) => { - m.messageCount = 0; + m.isUnread = false; m.mentionsCount = 0; m.manuallyUnread = false; m.viewedAt = member.lastViewedAt; diff --git a/app/components/team_sidebar/team_list/team_item/index.ts b/app/components/team_sidebar/team_list/team_item/index.ts index 0809dfa231..77ac495d49 100644 --- a/app/components/team_sidebar/team_list/team_item/index.ts +++ b/app/components/team_sidebar/team_list/team_item/index.ts @@ -18,34 +18,30 @@ import type SystemModel from '@typings/database/models/servers/system'; const {SERVER: {SYSTEM, MY_CHANNEL, CHANNEL}} = MM_TABLES; -const withSystem = withObservables([], ({database}: WithDatabaseArgs) => { - const currentTeamId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap((t) => of$(t.value)), - ); - return { - currentTeamId, - }; -}); - type WithTeamsArgs = WithDatabaseArgs & { myTeam: MyTeamModel; } -const withTeams = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) => { - const myChannels = database.get(MY_CHANNEL).query(Q.on(CHANNEL, Q.and(Q.where('delete_at', Q.eq(0)), Q.where('team_id', Q.eq(myTeam.id))))).observeWithColumns(['mentions_count', 'message_count']); +const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) => { + const myChannels = database.get(MY_CHANNEL).query(Q.on(CHANNEL, Q.and(Q.where('delete_at', Q.eq(0)), Q.where('team_id', Q.eq(myTeam.id))))).observeWithColumns(['mentions_count', 'is_unread']); const mentionCount = myChannels.pipe( // eslint-disable-next-line max-nested-callbacks switchMap((val) => of$(val.reduce((acc, v) => acc + v.mentionsCount, 0))), ); const hasUnreads = myChannels.pipe( // eslint-disable-next-line max-nested-callbacks - switchMap((val) => of$(val.reduce((acc, v) => acc + v.messageCount, 0) > 0)), + switchMap((val) => of$(val.reduce((acc, v) => acc || v.isUnread, false))), ); + const currentTeamId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( + switchMap((t) => of$(t.value)), + ); + return { + currentTeamId, team: myTeam.team.observe(), mentionCount, hasUnreads, }; }); -export default withDatabase(withSystem(withTeams(TeamItem))); +export default withDatabase(enhance(TeamItem)); diff --git a/app/database/models/server/my_channel.ts b/app/database/models/server/my_channel.ts index 9925ca35f8..343ea2a65d 100644 --- a/app/database/models/server/my_channel.ts +++ b/app/database/models/server/my_channel.ts @@ -31,11 +31,14 @@ export default class MyChannelModel extends Model { /** manually_unread : Determine if the user marked a post as unread */ @field('manually_unread') manuallyUnread!: boolean; + /** message_count : The derived number of unread messages on this channel */ + @field('message_count') messageCount!: number; + /** mentions_count : The number of mentions on this channel */ @field('mentions_count') mentionsCount!: number; - /** message_count : The derived number of unread messages on this channel */ - @field('message_count') messageCount!: number; + /** is_unread : Whether the channel has unread messages */ + @field('is_unread') isUnread!: boolean; /** roles : The user's privileges on this channel */ @field('roles') roles!: string; diff --git a/app/database/operator/server_data_operator/handlers/channel.ts b/app/database/operator/server_data_operator/handlers/channel.ts index 6a561eca80..534a0b2cd7 100644 --- a/app/database/operator/server_data_operator/handlers/channel.ts +++ b/app/database/operator/server_data_operator/handlers/channel.ts @@ -140,7 +140,9 @@ const ChannelHandler = (superclass: any) => class extends superclass { myChannels.forEach((my) => { const channel = channels.find((c) => c.id === my.channel_id); if (channel) { - my.msg_count = Math.max(0, channel.total_msg_count - my.msg_count); + const msgCount = Math.max(0, channel.total_msg_count - my.msg_count); + my.msg_count = msgCount; + my.is_unread = msgCount > 0; } }); diff --git a/app/database/operator/server_data_operator/transformers/channel.ts b/app/database/operator/server_data_operator/transformers/channel.ts index 0a66c3b01d..c1303d2de8 100644 --- a/app/database/operator/server_data_operator/transformers/channel.ts +++ b/app/database/operator/server_data_operator/transformers/channel.ts @@ -125,6 +125,7 @@ export const transformMyChannelRecord = ({action, database, value}: TransformerA myChannel._raw.id = isCreateAction ? (raw.channel_id || myChannel.id) : record.id; myChannel.roles = raw.roles; myChannel.messageCount = raw.msg_count; + myChannel.isUnread = Boolean(raw.is_unread); myChannel.mentionsCount = raw.mention_count; myChannel.lastPostAt = raw.last_post_at || 0; myChannel.lastViewedAt = raw.last_viewed_at; diff --git a/app/database/schema/server/table_schemas/my_channel.ts b/app/database/schema/server/table_schemas/my_channel.ts index 89c7ffab33..17ddf505e0 100644 --- a/app/database/schema/server/table_schemas/my_channel.ts +++ b/app/database/schema/server/table_schemas/my_channel.ts @@ -15,6 +15,7 @@ export default tableSchema({ {name: 'manually_unread', type: 'boolean'}, {name: 'mentions_count', type: 'number'}, {name: 'message_count', type: 'number'}, + {name: 'is_unread', type: 'boolean'}, {name: 'roles', type: 'string'}, {name: 'viewed_at', type: 'number'}, ], diff --git a/app/database/schema/server/test.ts b/app/database/schema/server/test.ts index 53765dda58..172796ee22 100644 --- a/app/database/schema/server/test.ts +++ b/app/database/schema/server/test.ts @@ -116,6 +116,7 @@ describe('*** Test schema for SERVER database ***', () => { manually_unread: {name: 'manually_unread', type: 'boolean'}, mentions_count: {name: 'mentions_count', type: 'number'}, message_count: {name: 'message_count', type: 'number'}, + is_unread: {name: 'is_unread', type: 'boolean'}, roles: {name: 'roles', type: 'string'}, viewed_at: {name: 'viewed_at', type: 'number'}, }, @@ -125,6 +126,7 @@ describe('*** Test schema for SERVER database ***', () => { {name: 'manually_unread', type: 'boolean'}, {name: 'mentions_count', type: 'number'}, {name: 'message_count', type: 'number'}, + {name: 'is_unread', type: 'boolean'}, {name: 'roles', type: 'string'}, {name: 'viewed_at', type: 'number'}, ], diff --git a/app/screens/home/tab_bar/home.tsx b/app/screens/home/tab_bar/home.tsx index 459e0b8048..c20e40cda3 100644 --- a/app/screens/home/tab_bar/home.tsx +++ b/app/screens/home/tab_bar/home.tsx @@ -23,7 +23,7 @@ type Props = { type UnreadMessages = { mentions: number; - messages: number; + unread: boolean; }; type UnreadSubscription = UnreadMessages & { @@ -52,30 +52,30 @@ const style = StyleSheet.create({ const Home = ({isFocused, theme}: Props) => { const db = DatabaseManager.appDatabase?.database; - const [total, setTotal] = useState({mentions: 0, messages: 0}); + const [total, setTotal] = useState({mentions: 0, unread: false}); const updateTotal = () => { - let messages = 0; + let unread = false; let mentions = 0; subscriptions.forEach((value) => { - messages += value.messages; + unread = unread || value.unread; mentions += value.mentions; }); - setTotal({mentions, messages}); + setTotal({mentions, unread}); }; const unreadsSubscription = (serverUrl: string, myChannels: MyChannelModel[]) => { const unreads = subscriptions.get(serverUrl); if (unreads) { let mentions = 0; - let messages = 0; + let unread = false; myChannels.forEach((myChannel) => { mentions += myChannel.mentionsCount; - messages += myChannel.messageCount; + unread = unread || myChannel.isUnread; }); unreads.mentions = mentions; - unreads.messages = messages; + unreads.unread = unread; subscriptions.set(serverUrl, unreads); updateTotal(); } @@ -90,13 +90,13 @@ const Home = ({isFocused, theme}: Props) => { if (!subscriptions.has(serverUrl)) { const unreads: UnreadSubscription = { mentions: 0, - messages: 0, + unread: false, }; subscriptions.set(serverUrl, unreads); unreads.subscription = sdb.database. get(MY_CHANNEL). query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))). - observeWithColumns(['mentions_count', 'message_count']). + observeWithColumns(['mentions_count', 'has_unreads']). subscribe(unreadsSubscription.bind(undefined, serverUrl)); } } @@ -132,7 +132,7 @@ const Home = ({isFocused, theme}: Props) => { } else if (total.mentions > 99) { unreadStyle = style.mentionsThreeDigits; } - } else if (total.messages) { + } else if (total.unread) { unreadStyle = style.unread; } @@ -150,7 +150,7 @@ const Home = ({isFocused, theme}: Props) => { style={unreadStyle} visible={!isFocused && Boolean(unreadStyle)} type='Small' - value={total.mentions || (total.messages * -1)} + value={total.mentions || (total.unread ? -1 : 0)} /> ); diff --git a/types/api/channels.d.ts b/types/api/channels.d.ts index 5cb0ce6eca..417bc40e6f 100644 --- a/types/api/channels.d.ts +++ b/types/api/channels.d.ts @@ -60,6 +60,7 @@ type ChannelMembership = { scheme_user?: boolean; scheme_admin?: boolean; post_root_id?: string; + is_unread?: boolean; }; type ChannelUnread = { channel_id: string; diff --git a/types/database/models/servers/my_channel.d.ts b/types/database/models/servers/my_channel.d.ts index 1769339859..fd3270e6ae 100644 --- a/types/database/models/servers/my_channel.d.ts +++ b/types/database/models/servers/my_channel.d.ts @@ -26,6 +26,9 @@ export default class MyChannelModel extends Model { /** message_count : The derived number of unread messages on this channel */ messageCount: number; + /** is_unread : Whether the channel has unread posts */ + isUnread: boolean; + /** roles : The user's privileges on this channel */ roles: string;