diff --git a/patches/react-native+0.68.7.patch b/patches/react-native+0.68.7.patch
index 024a73247cc..fe4cc5f9760 100644
--- a/patches/react-native+0.68.7.patch
+++ b/patches/react-native+0.68.7.patch
@@ -1,3 +1,76 @@
+diff --git a/node_modules/react-native/jest/setup.js b/node_modules/react-native/jest/setup.js
+index 5bc654475..e61bbf49c 100644
+--- a/node_modules/react-native/jest/setup.js
++++ b/node_modules/react-native/jest/setup.js
+@@ -15,22 +15,52 @@ const mockComponent = jest.requireActual('./mockComponent');
+ jest.requireActual('@react-native/polyfills/Object.es8');
+ jest.requireActual('@react-native/polyfills/error-guard');
+
+-global.__DEV__ = true;
+-
+-global.performance = {
+- now: jest.fn(Date.now),
+-};
+-
+-global.Promise = jest.requireActual('promise');
+-global.regeneratorRuntime = jest.requireActual('regenerator-runtime/runtime');
+-global.window = global;
+-
+-global.requestAnimationFrame = function (callback) {
+- return setTimeout(callback, 0);
+-};
+-global.cancelAnimationFrame = function (id) {
+- clearTimeout(id);
+-};
++Object.defineProperties(global, {
++ __DEV__: {
++ configurable: true,
++ enumerable: true,
++ value: true,
++ writable: true,
++ },
++ Promise: {
++ configurable: true,
++ enumerable: true,
++ value: jest.requireActual('promise'),
++ writable: true,
++ },
++ cancelAnimationFrame: {
++ configurable: true,
++ enumerable: true,
++ value: id => clearTimeout(id),
++ writable: true,
++ },
++ performance: {
++ configurable: true,
++ enumerable: true,
++ value: {
++ now: jest.fn(Date.now),
++ },
++ writable: true,
++ },
++ regeneratorRuntime: {
++ configurable: true,
++ enumerable: true,
++ value: jest.requireActual('regenerator-runtime/runtime'),
++ writable: true,
++ },
++ requestAnimationFrame: {
++ configurable: true,
++ enumerable: true,
++ value: callback => setTimeout(callback, 0),
++ writable: true,
++ },
++ window: {
++ configurable: true,
++ enumerable: true,
++ value: global,
++ writable: true,
++ },
++});
+
+ // there's a __mock__ for it.
+ jest.setMock(
diff --git a/node_modules/react-native/scripts/react_native_pods.rb b/node_modules/react-native/scripts/react_native_pods.rb
index f2ceeda..c618f77 100644
--- a/node_modules/react-native/scripts/react_native_pods.rb
diff --git a/src/__tests__/lib/exampleData.js b/src/__tests__/lib/exampleData.js
index 32424d3a5a5..abc22401009 100644
--- a/src/__tests__/lib/exampleData.js
+++ b/src/__tests__/lib/exampleData.js
@@ -212,8 +212,8 @@ export const userStatusEmojiRealm: UserStatus['status_emoji'] = deepFreeze({
export const realm: URL = new URL('https://zulip.example.org');
/** These may be raised but should not be lowered. */
-export const recentZulipVersion: ZulipVersion = new ZulipVersion('6.0-dev-2191-gf56ce7a159');
-export const recentZulipFeatureLevel = 153;
+export const recentZulipVersion: ZulipVersion = new ZulipVersion('8.0-dev-2894-g86100cdb4e');
+export const recentZulipFeatureLevel = 226;
export const makeAccount = (
args: {|
diff --git a/src/__tests__/metatests.js b/src/__tests__/test-test.js
similarity index 100%
rename from src/__tests__/metatests.js
rename to src/__tests__/test-test.js
diff --git a/src/action-sheets/__tests__/action-sheet-test.js b/src/action-sheets/__tests__/action-sheet-test.js
index 8a8ab876222..3b396ae44b9 100644
--- a/src/action-sheets/__tests__/action-sheet-test.js
+++ b/src/action-sheets/__tests__/action-sheet-test.js
@@ -98,6 +98,21 @@ describe('constructTopicActionButtons', () => {
expect(titles({ ...eg.plusBackgroundData, mute })).toContain('Mute topic');
});
+ test('show followTopic on muted topic', () => {
+ const mute = makeMuteState([[eg.stream, topic]]);
+ expect(titles({ ...eg.plusBackgroundData, mute })).toContain('Follow topic');
+ });
+
+ test('show followTopic', () => {
+ const mute = makeMuteState([]);
+ expect(titles({ ...eg.plusBackgroundData, mute })).toContain('Follow topic');
+ });
+
+ test('show unfollowTopic', () => {
+ const mute = makeMuteState([[eg.stream, topic, UserTopicVisibilityPolicy.Followed]]);
+ expect(titles({ ...eg.plusBackgroundData, mute })).toContain('Unfollow topic');
+ });
+
test('show resolveTopic', () => {
expect(titles({ ...eg.plusBackgroundData })).toContain('Resolve topic');
});
diff --git a/src/action-sheets/index.js b/src/action-sheets/index.js
index b70255d83f1..65b5d5d17dc 100644
--- a/src/action-sheets/index.js
+++ b/src/action-sheets/index.js
@@ -319,6 +319,24 @@ const muteTopic = {
},
};
+const followTopic = {
+ title: 'Follow topic',
+ errorMessage: 'Failed to follow topic',
+ action: async ({ auth, streamId, topic, zulipFeatureLevel }) => {
+ invariant(zulipFeatureLevel >= 219, 'Should only attempt to follow topic on FL 219+');
+ await api.updateUserTopic(auth, streamId, topic, UserTopicVisibilityPolicy.Followed);
+ },
+};
+
+const unfollowTopic = {
+ title: 'Unfollow topic',
+ errorMessage: 'Failed to unfollow topic',
+ action: async ({ auth, streamId, topic, zulipFeatureLevel }) => {
+ invariant(zulipFeatureLevel >= 219, 'Should only attempt to unfollow topic on FL 219+');
+ await api.updateUserTopic(auth, streamId, topic, UserTopicVisibilityPolicy.None);
+ },
+};
+
const copyLinkToTopic = {
title: 'Copy link to topic',
errorMessage: 'Failed to copy topic link',
@@ -651,6 +669,11 @@ export const constructTopicActionButtons = (args: {|
const sub = subscriptions.get(streamId);
const streamMuted = !!sub && !sub.in_home_view;
+ // TODO(server-7.0): Simplify this condition away.
+ const supportsUnmutingTopics = zulipFeatureLevel >= 170;
+ // TODO(server-8.0): Simplify this condition away.
+ const supportsFollowingTopics = zulipFeatureLevel >= 219;
+
const buttons = [];
const unreadCount = getUnreadCountForTopic(unread, streamId, topic);
if (unreadCount > 0) {
@@ -661,25 +684,46 @@ export const constructTopicActionButtons = (args: {|
switch (getTopicVisibilityPolicy(mute, streamId, topic)) {
case UserTopicVisibilityPolicy.Muted:
buttons.push(unmuteTopic);
+ if (supportsFollowingTopics) {
+ buttons.push(followTopic);
+ }
break;
case UserTopicVisibilityPolicy.None:
case UserTopicVisibilityPolicy.Unmuted:
+ buttons.push(muteTopic);
+ if (supportsFollowingTopics) {
+ buttons.push(followTopic);
+ }
+ break;
case UserTopicVisibilityPolicy.Followed:
buttons.push(muteTopic);
+ if (supportsFollowingTopics) {
+ buttons.push(unfollowTopic);
+ }
break;
}
} else if (sub && streamMuted) {
// Muted stream.
- // TODO(server-7.0): Simplify this condition away.
- if (zulipFeatureLevel >= 170) {
+ if (supportsUnmutingTopics) {
switch (getTopicVisibilityPolicy(mute, streamId, topic)) {
case UserTopicVisibilityPolicy.None:
case UserTopicVisibilityPolicy.Muted:
buttons.push(unmuteTopicInMutedStream);
+ if (supportsFollowingTopics) {
+ buttons.push(followTopic);
+ }
break;
case UserTopicVisibilityPolicy.Unmuted:
+ buttons.push(muteTopic);
+ if (supportsFollowingTopics) {
+ buttons.push(followTopic);
+ }
+ break;
case UserTopicVisibilityPolicy.Followed:
buttons.push(muteTopic);
+ if (supportsFollowingTopics) {
+ buttons.push(unfollowTopic);
+ }
break;
}
}
diff --git a/src/common/Icons.js b/src/common/Icons.js
index 8b2aef67920..1afd96793ee 100644
--- a/src/common/Icons.js
+++ b/src/common/Icons.js
@@ -117,5 +117,6 @@ export const IconAlertTriangle: SpecificIconType = makeIcon(Feather, 'alert-tria
// WildcardMentionItem depends on this being square.
export const IconWildcardMention: SpecificIconType = makeIcon(FontAwesome, 'bullhorn');
-// eslint-disable-next-line react/function-component-definition
+/* eslint-disable react/function-component-definition */
export const IconWebPublic: SpecificIconType = props => ;
+export const IconFollow: SpecificIconType = props => ;
diff --git a/src/mute/muteModel.js b/src/mute/muteModel.js
index 9e0b846832e..82aade0293e 100644
--- a/src/mute/muteModel.js
+++ b/src/mute/muteModel.js
@@ -95,6 +95,20 @@ export function isTopicVisible(
}
}
+/**
+ * Whether the user is following this topic.
+ */
+export function isTopicFollowed(streamId: number, topic: string, mute: MuteState): boolean {
+ switch (getTopicVisibilityPolicy(mute, streamId, topic)) {
+ case UserTopicVisibilityPolicy.None:
+ case UserTopicVisibilityPolicy.Muted:
+ case UserTopicVisibilityPolicy.Unmuted:
+ return false;
+ case UserTopicVisibilityPolicy.Followed:
+ return true;
+ }
+}
+
//
//
// Reducer.
diff --git a/src/pm-conversations/PmConversationList.js b/src/pm-conversations/PmConversationList.js
index 5ae0d57ed5d..80921cea634 100644
--- a/src/pm-conversations/PmConversationList.js
+++ b/src/pm-conversations/PmConversationList.js
@@ -1,11 +1,10 @@
/* @flow strict-local */
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
import type { Node } from 'react';
import { FlatList } from 'react-native';
import { useDispatch, useSelector } from '../react-redux';
import type { PmConversationData, UserOrBot } from '../types';
-import { createStyleSheet } from '../styles';
import { type PmKeyUsers } from '../utils/recipient';
import { pm1to1NarrowFromUser, pmNarrowFromUsers } from '../utils/narrow';
import UserItem from '../users/UserItem';
@@ -13,15 +12,9 @@ import GroupPmConversationItem from './GroupPmConversationItem';
import { doNarrow } from '../actions';
import { getMutedUsers } from '../selectors';
-const styles = createStyleSheet({
- list: {
- flex: 1,
- flexDirection: 'column',
- },
-});
-
type Props = $ReadOnly<{|
conversations: $ReadOnlyArray,
+ extraPaddingEnd?: number,
|}>;
/**
@@ -44,9 +37,20 @@ export default function PmConversationList(props: Props): Node {
[dispatch],
);
- const { conversations } = props;
+ const { conversations, extraPaddingEnd = 0 } = props;
const mutedUsers = useSelector(getMutedUsers);
+ const styles = useMemo(
+ () => ({
+ list: {
+ flex: 1,
+ flexDirection: 'column',
+ paddingRight: extraPaddingEnd,
+ },
+ }),
+ [extraPaddingEnd],
+ );
+
return (
void,
@@ -87,6 +88,7 @@ export default function StreamItem(props: Props): Node {
iconSize,
offersSubscribeButton = false,
unreadCount,
+ extraPaddingEnd = 0,
onPress,
onSubscribeButtonPressed,
} = props;
@@ -125,9 +127,9 @@ export default function StreamItem(props: Props): Node {
wrapper: {
flexDirection: 'row',
alignItems: 'center',
- ...(handleExpandCollapse
- ? { paddingRight: 16 }
- : { paddingVertical: 8, paddingHorizontal: 16 }),
+ paddingVertical: handleExpandCollapse ? 0 : 8,
+ paddingLeft: handleExpandCollapse ? 0 : 16,
+ paddingRight: extraPaddingEnd + 16,
backgroundColor,
opacity: isMuted ? 0.5 : 1,
},
@@ -156,7 +158,7 @@ export default function StreamItem(props: Props): Node {
fontSize: 12,
},
}),
- [backgroundColor, handleExpandCollapse, isMuted, textColor],
+ [backgroundColor, extraPaddingEnd, handleExpandCollapse, isMuted, textColor],
);
const collapseButton = handleExpandCollapse && (
diff --git a/src/streams/TopicItem.js b/src/streams/TopicItem.js
index 37e5f469a8e..ef01f72e83a 100644
--- a/src/streams/TopicItem.js
+++ b/src/streams/TopicItem.js
@@ -6,7 +6,7 @@ import { View } from 'react-native';
import { useActionSheet } from '@expo/react-native-action-sheet';
import styles, { BRAND_COLOR, createStyleSheet } from '../styles';
-import { IconMention } from '../common/Icons';
+import { IconMention, IconFollow } from '../common/Icons';
import ZulipText from '../common/ZulipText';
import Touchable from '../common/Touchable';
import UnreadCount from '../common/UnreadCount';
@@ -22,10 +22,11 @@ import {
getOwnUser,
getZulipFeatureLevel,
} from '../selectors';
-import { getMute } from '../mute/muteModel';
+import { getMute, isTopicFollowed } from '../mute/muteModel';
import { getUnread } from '../unread/unreadModel';
import { getOwnUserRole } from '../permissionSelectors';
import { useNavigation } from '../react-navigation';
+import { ThemeContext } from '../styles/theme';
const componentStyles = createStyleSheet({
selectedRow: {
@@ -44,6 +45,11 @@ const componentStyles = createStyleSheet({
muted: {
opacity: 0.5,
},
+ followedIcon: {
+ paddingLeft: 4,
+ width: 20,
+ opacity: 0.2,
+ },
});
type Props = $ReadOnly<{|
@@ -84,6 +90,10 @@ export default function TopicItem(props: Props): Node {
zulipFeatureLevel: getZulipFeatureLevel(state),
}));
+ const theme = useContext(ThemeContext);
+ const iconColor = theme.themeName === 'dark' ? 'white' : 'black';
+ const isFollowed = useSelector(state => isTopicFollowed(streamId, name, getMute(state)));
+
return (
onPress(streamId, name)}
@@ -112,6 +122,12 @@ export default function TopicItem(props: Props): Node {
/>
{isMentioned && }
+ {isFollowed ? (
+
+ ) : (
+ // $FlowFixMe[incompatible-type]: complains about `color` but that's not present
+
+ )}
);
diff --git a/src/title/TitleStream.js b/src/title/TitleStream.js
index 6741fe70c6b..0d97071b060 100644
--- a/src/title/TitleStream.js
+++ b/src/title/TitleStream.js
@@ -11,7 +11,7 @@ import type { Narrow } from '../types';
import styles, { createStyleSheet } from '../styles';
import { useSelector, useDispatch } from '../react-redux';
import StreamIcon from '../streams/StreamIcon';
-import { isTopicNarrow, topicOfNarrow } from '../utils/narrow';
+import { isTopicNarrow, streamIdOfNarrow, topicOfNarrow } from '../utils/narrow';
import {
getAuth,
getFlags,
@@ -22,12 +22,13 @@ import {
getSettings,
getZulipFeatureLevel,
} from '../selectors';
-import { getMute } from '../mute/muteModel';
+import { getMute, isTopicFollowed } from '../mute/muteModel';
import { showStreamActionSheet, showTopicActionSheet } from '../action-sheets';
import type { ShowActionSheetWithOptions } from '../action-sheets';
import { getUnread } from '../unread/unreadModel';
import { getOwnUserRole } from '../permissionSelectors';
import { useNavigation } from '../react-navigation';
+import { IconFollow } from '../common/Icons';
type Props = $ReadOnly<{|
narrow: Narrow,
@@ -46,6 +47,14 @@ const componentStyles = createStyleSheet({
flexDirection: 'row',
alignItems: 'center',
},
+ topicRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ followIcon: {
+ paddingLeft: 4,
+ opacity: 0.4,
+ },
});
export default function TitleStream(props: Props): Node {
@@ -70,6 +79,12 @@ export default function TitleStream(props: Props): Node {
useActionSheet().showActionSheetWithOptions;
const _ = useContext(TranslationContext);
+ const isFollowed = useSelector(
+ state =>
+ isTopicNarrow(narrow)
+ && isTopicFollowed(streamIdOfNarrow(narrow), topicOfNarrow(narrow), getMute(state)),
+ );
+
return (
{isTopicNarrow(narrow) && (
-
- {topicOfNarrow(narrow)}
-
+
+
+ {topicOfNarrow(narrow)}
+
+ {isFollowed && (
+
+ )}
+
)}
diff --git a/src/unread/UnreadCards.js b/src/unread/UnreadCards.js
index d1633a039f6..45e10d0b7cb 100644
--- a/src/unread/UnreadCards.js
+++ b/src/unread/UnreadCards.js
@@ -93,6 +93,7 @@ export default function UnreadCards(props: Props): Node {
isWebPublic={section.isWebPublic}
backgroundColor={section.color}
unreadCount={section.unread}
+ extraPaddingEnd={20}
onPress={stream => {
setTimeout(() => dispatch(doNarrow(streamNarrow(stream.stream_id))));
}}
@@ -101,7 +102,7 @@ export default function UnreadCards(props: Props): Node {
}
renderItem={({ item, section }) =>
section.key === 'private' ? (
-
+
) : (
zulip extra (no change) 1`] = `0`;
+exports[`getEditSequence correct for interesting changes within a given message follow a topic 1`] = `1`;
+
exports[`getEditSequence correct for interesting changes within a given message mute a sender 1`] = `1`;
exports[`getEditSequence correct for interesting changes within a given message polls choice added 1`] = `3`;
@@ -78,6 +80,8 @@ exports[`getEditSequence correct for interesting changes within a given message
exports[`getEditSequence correct for interesting changes within a given message star a message 1`] = `1`;
+exports[`getEditSequence correct for interesting changes within a given message unfollow a topic 1`] = `1`;
+
exports[`getEditSequence correct for interesting changes within a given message unmute a sender 1`] = `1`;
exports[`getEditSequence correct for interesting changes within a given message unstar a message 1`] = `1`;
@@ -92,7 +96,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 5, 1995
@@ -136,7 +140,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -185,7 +189,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 19, 1995
@@ -209,7 +213,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 2
+
topic 2
Feb 19, 1995
@@ -241,7 +245,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -265,7 +269,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjI=\\">
# stream 2
-
topic 2
+
topic 2
Mar 5, 1995
@@ -297,7 +301,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -612,7 +616,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 5, 1995
@@ -692,7 +696,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 19, 1995
@@ -716,7 +720,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 2
+
topic 2
Feb 19, 1995
@@ -880,7 +884,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -904,7 +908,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjI=\\">
# stream 2
-
topic 2
+
topic 2
Mar 5, 1995
@@ -928,7 +932,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -1000,7 +1004,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -1526,6 +1530,38 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible all-pm
"
`;
+exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message in followed topic 1`] = `
+"
+
+ Dec 31, 1969
+
+
+
+ # stream 1
+
+
example topic
+
Dec 31, 1969
+
+
+
+
+
+
+
+ Nonrandom name sender User
+
+
11:59 PM
+
+
This is an example stream message.
+
+
+
+
+
+
"
+`;
+
exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message in unsubscribed stream 1`] = `
"
@@ -1536,7 +1572,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1568,7 +1604,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1610,7 +1646,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1642,7 +1678,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1674,7 +1710,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1706,7 +1742,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1738,7 +1774,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1770,7 +1806,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1802,7 +1838,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1834,7 +1870,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1866,7 +1902,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1898,7 +1934,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1930,7 +1966,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1962,7 +1998,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1994,7 +2030,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2026,7 +2062,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2060,7 +2096,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2092,7 +2128,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2124,7 +2160,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2156,7 +2192,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2655,7 +2691,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Feb 5, 1995
-
topic 1
+
topic 1
Feb 5, 1995
@@ -2695,7 +2731,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Mar 5, 1995
-
topic 1
+
topic 1
Mar 5, 1995
@@ -2740,7 +2776,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Feb 19, 1995
-
topic 1
+
topic 1
Feb 19, 1995
@@ -2760,7 +2796,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
-
topic 2
+
topic 2
Feb 19, 1995
@@ -2788,7 +2824,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Mar 5, 1995
-
topic 1
+
topic 1
Mar 5, 1995
@@ -2837,7 +2873,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Feb 5, 1995
-
topic 1
+
topic 1
Feb 5, 1995
@@ -2890,7 +2926,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
-
topic 2
+
topic 2
Feb 19, 1995
@@ -2914,7 +2950,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Mar 5, 1995
-
topic 1
+
topic 1
Mar 5, 1995
diff --git a/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios b/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios
index 50b5af73d9c..f2dda872427 100644
--- a/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios
+++ b/src/webview/__tests__/__snapshots__/generateInboundEventEditSequence-test.js.snap.ios
@@ -68,6 +68,8 @@ exports[`getEditSequence correct for interesting changes within a given message
exports[`getEditSequence correct for interesting changes within a given message add/remove/change emoji status status emoji: zulip extra -> zulip extra (no change) 1`] = `0`;
+exports[`getEditSequence correct for interesting changes within a given message follow a topic 1`] = `1`;
+
exports[`getEditSequence correct for interesting changes within a given message mute a sender 1`] = `1`;
exports[`getEditSequence correct for interesting changes within a given message polls choice added 1`] = `3`;
@@ -78,6 +80,8 @@ exports[`getEditSequence correct for interesting changes within a given message
exports[`getEditSequence correct for interesting changes within a given message star a message 1`] = `1`;
+exports[`getEditSequence correct for interesting changes within a given message unfollow a topic 1`] = `1`;
+
exports[`getEditSequence correct for interesting changes within a given message unmute a sender 1`] = `1`;
exports[`getEditSequence correct for interesting changes within a given message unstar a message 1`] = `1`;
@@ -92,7 +96,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 5, 1995
@@ -136,7 +140,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -185,7 +189,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 19, 1995
@@ -209,7 +213,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 2
+
topic 2
Feb 19, 1995
@@ -241,7 +245,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -265,7 +269,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjI=\\">
# stream 2
-
topic 2
+
topic 2
Mar 5, 1995
@@ -297,7 +301,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -612,7 +616,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 5, 1995
@@ -692,7 +696,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Feb 19, 1995
@@ -716,7 +720,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 2
+
topic 2
Feb 19, 1995
@@ -880,7 +884,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -904,7 +908,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjI=\\">
# stream 2
-
topic 2
+
topic 2
Mar 5, 1995
@@ -928,7 +932,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -1000,7 +1004,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible HOME_N
background: #123456\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
topic 1
+
topic 1
Mar 5, 1995
@@ -1526,6 +1530,38 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible all-pm
"
`;
+exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message in followed topic 1`] = `
+"
+
+ Dec 31, 1969
+
+
+
+ # stream 1
+
+
example topic
+
Dec 31, 1969
+
+
+
+
+
+
+
+ Nonrandom name sender User
+
+
11:59 PM
+
+
This is an example stream message.
+
+
+
+
+
+
"
+`;
+
exports[`messages -> piece descriptors -> content HTML is stable/sensible other interesting cases (single messages) message in unsubscribed stream 1`] = `
"
@@ -1536,7 +1572,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1568,7 +1604,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1610,7 +1646,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1642,7 +1678,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1674,7 +1710,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1706,7 +1742,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1738,7 +1774,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1770,7 +1806,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1802,7 +1838,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1834,7 +1870,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1866,7 +1902,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1898,7 +1934,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1930,7 +1966,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1962,7 +1998,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -1994,7 +2030,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2026,7 +2062,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2060,7 +2096,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2092,7 +2128,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2124,7 +2160,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2156,7 +2192,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible other
background: hsl(0, 0%, 80%)\\" data-narrow=\\"c3RyZWFtOjE=\\">
# stream 1
-
example topic
+
example topic
Dec 31, 1969
@@ -2655,7 +2691,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Feb 5, 1995
-
topic 1
+
topic 1
Feb 5, 1995
@@ -2695,7 +2731,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Mar 5, 1995
-
topic 1
+
topic 1
Mar 5, 1995
@@ -2740,7 +2776,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Feb 19, 1995
-
topic 1
+
topic 1
Feb 19, 1995
@@ -2760,7 +2796,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
-
topic 2
+
topic 2
Feb 19, 1995
@@ -2788,7 +2824,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Mar 5, 1995
-
topic 1
+
topic 1
Mar 5, 1995
@@ -2837,7 +2873,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Feb 5, 1995
-
topic 1
+
topic 1
Feb 5, 1995
@@ -2890,7 +2926,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
-
topic 2
+
topic 2
Feb 19, 1995
@@ -2914,7 +2950,7 @@ exports[`messages -> piece descriptors -> content HTML is stable/sensible stream
Mar 5, 1995
-
topic 1
+
topic 1
Mar 5, 1995
diff --git a/src/webview/__tests__/generateInboundEventEditSequence-test.js b/src/webview/__tests__/generateInboundEventEditSequence-test.js
index 6882f4e53fb..96cc7bb9693 100644
--- a/src/webview/__tests__/generateInboundEventEditSequence-test.js
+++ b/src/webview/__tests__/generateInboundEventEditSequence-test.js
@@ -29,6 +29,8 @@ import getMessageListElements from '../../message/getMessageListElements';
import { getGlobalSettings } from '../../selectors';
import { getBackgroundData } from '../backgroundData';
import { randString } from '../../utils/misc';
+import { makeMuteState } from '../../mute/__tests__/mute-testlib';
+import { UserTopicVisibilityPolicy } from '../../api/modelTypes';
// Tell ESLint to recognize `check` as a helper function that runs
// assertions.
@@ -443,6 +445,23 @@ describe('messages -> piece descriptors -> content HTML is stable/sensible', ()
});
});
+ test('message in followed topic', () => {
+ check({
+ narrow: HOME_NARROW,
+ messages: [baseSingleMessage],
+ state: eg.reduxStatePlus({
+ streams: [...eg.plusReduxState.streams, stream1],
+ subscriptions: [
+ ...eg.plusReduxState.subscriptions,
+ eg.makeSubscription({ stream: stream1 }),
+ ],
+ mute: makeMuteState([
+ [stream1, baseSingleMessage.subject, UserTopicVisibilityPolicy.Followed],
+ ]),
+ }),
+ });
+ });
+
describe('message with reactions', () => {
describe('displayEmojiReactionUsers: false', () => {
const state = eg.reduxStatePlus({
@@ -991,6 +1010,42 @@ describe('getEditSequence correct for interesting changes', () => {
);
});
+ test('follow a topic', () => {
+ const message = eg.streamMessage();
+ check(
+ {
+ messages: [message],
+ state: eg.reduxStatePlus({
+ mute: makeMuteState([]),
+ }),
+ },
+ {
+ messages: [message],
+ state: eg.reduxStatePlus({
+ mute: makeMuteState([[eg.stream, message.subject, UserTopicVisibilityPolicy.Followed]]),
+ }),
+ },
+ );
+ });
+
+ test('unfollow a topic', () => {
+ const message = eg.streamMessage();
+ check(
+ {
+ messages: [message],
+ state: eg.reduxStatePlus({
+ mute: makeMuteState([[eg.stream, message.subject, UserTopicVisibilityPolicy.Followed]]),
+ }),
+ },
+ {
+ messages: [message],
+ state: eg.reduxStatePlus({
+ mute: makeMuteState([]),
+ }),
+ },
+ );
+ });
+
// TODO(#5208): We haven't settled how we want to track name/avatar
test.todo("sender's name/avatar changed");
diff --git a/src/webview/generateInboundEventEditSequence.js b/src/webview/generateInboundEventEditSequence.js
index c48576571d6..d0f96be4940 100644
--- a/src/webview/generateInboundEventEditSequence.js
+++ b/src/webview/generateInboundEventEditSequence.js
@@ -8,6 +8,7 @@ import { ensureUnreachable } from '../generics';
import type { BackgroundData } from './backgroundData';
import messageListElementHtml from './html/messageListElementHtml';
import { getUserStatusFromModel } from '../user-statuses/userStatusesCore';
+import { isTopicFollowed } from '../mute/muteModel';
const NODE_ENV = process.env.NODE_ENV;
@@ -72,7 +73,19 @@ function doElementsDifferInterestingly(
return !isEqual(oldElement, newElement);
case 'header':
// TODO(?): False positives on `.subsequentMessage.content` changes
- return !isEqual(oldElement, newElement);
+ if (!isEqual(oldElement, newElement)) {
+ return true;
+ }
+ if (newElement.subsequentMessage?.type === 'stream') {
+ const message = newElement.subsequentMessage;
+ if (
+ isTopicFollowed(message.stream_id, message.subject, oldBackgroundData.mute)
+ !== isTopicFollowed(message.stream_id, message.subject, newBackgroundData.mute)
+ ) {
+ return true;
+ }
+ }
+ return false;
case 'message': {
invariant(newElement.type === 'message', 'oldElement.type equals newElement.type');
diff --git a/src/webview/html/__tests__/header-test.js b/src/webview/html/__tests__/header-test.js
index fc212f20cf3..99b4eea6afb 100644
--- a/src/webview/html/__tests__/header-test.js
+++ b/src/webview/html/__tests__/header-test.js
@@ -3,8 +3,10 @@
import * as eg from '../../../__tests__/lib/exampleData';
import header from '../header';
import type { BackgroundData } from '../../backgroundData';
+import { makeMuteState } from '../../../mute/__tests__/mute-testlib';
const backgroundData: BackgroundData = ({
+ mute: makeMuteState([]),
ownEmail: eg.selfUser.email,
subscriptions: [eg.stream],
streams: new Map([[eg.stream.stream_id, eg.stream]]),
diff --git a/src/webview/html/header.js b/src/webview/html/header.js
index 6733053ccc4..66bb506050c 100644
--- a/src/webview/html/header.js
+++ b/src/webview/html/header.js
@@ -19,6 +19,7 @@ import {
streamNameOfStreamMessage,
} from '../../utils/recipient';
import { base64Utf8Encode } from '../../utils/encoding';
+import { isTopicFollowed } from '../../mute/muteModel';
const renderTopic = message =>
// TODO: pin down if '' happens, and what its proper semantics are.
@@ -32,7 +33,7 @@ const renderTopic = message =>
* This is a private helper of messageListElementHtml.
*/
export default (
- { ownUser, subscriptions }: BackgroundData,
+ { mute, ownUser, subscriptions }: BackgroundData,
element: HeaderMessageListElement,
): string => {
const { subsequentMessage: message, style: headerStyle } = element;
@@ -41,6 +42,7 @@ export default (
const streamName = streamNameOfStreamMessage(message);
const topicNarrowStr = keyFromNarrow(topicNarrow(message.stream_id, message.subject));
const topicHtml = renderTopic(message);
+ const isFollowed = isTopicFollowed(message.stream_id, message.subject, mute);
if (headerStyle === 'topic+date') {
return template`\
@@ -49,7 +51,7 @@ export default (
data-narrow="${base64Utf8Encode(topicNarrowStr)}"
data-msg-id="${message.id}"
>
-