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 +
+
+
+
+ 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 +
+
+
+
+ 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}" > -
$!${topicHtml}
+
$!${topicHtml}
${humanDate(new Date(message.timestamp * 1000))}
`; } else if (headerStyle === 'full') { @@ -70,7 +72,7 @@ export default ( data-narrow="${base64Utf8Encode(streamNarrowStr)}"> # ${streamName}
-
$!${topicHtml}
+
$!${topicHtml}
${humanDate(new Date(message.timestamp * 1000))}
`; } else { diff --git a/src/webview/html/template.js b/src/webview/html/template.js index 87cf27a0a57..be6fcc32f93 100644 --- a/src/webview/html/template.js +++ b/src/webview/html/template.js @@ -16,7 +16,7 @@ import escape from 'lodash.escape'; */ export default ( strings: $ReadOnlyArray, - ...values: $ReadOnlyArray + ...values: $ReadOnlyArray ): string => { // $FlowIssue[prop-missing] #2616 github.com/facebook/flow/issues/2616 const raw: $ReadOnlyArray = strings.raw; diff --git a/src/webview/static/base.css b/src/webview/static/base.css index 731c97371e5..673b8c369c6 100644 --- a/src/webview/static/base.css +++ b/src/webview/static/base.css @@ -116,10 +116,22 @@ body { .topic-text { flex: 1; padding: 0 8px; + display: flex; + align-items: center; overflow: hidden; text-overflow: ellipsis; pointer-events: none; } +.topic-text[data-followed="true"]::after { + content: ""; + background-image: url("images/follow.svg"); + margin-left: 12px; + width: 17px; + height: 17px; + /* opacity: 0.2 on web, but that's with the pastel stream colors; + * 0.3 stands up better to our gray. */ + opacity: 0.3; +} .topic-date { opacity: 0.5; padding: 0 8px; diff --git a/src/webview/static/images/follow.svg b/src/webview/static/images/follow.svg new file mode 100644 index 00000000000..3cbded1e623 --- /dev/null +++ b/src/webview/static/images/follow.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/fonts/zulip-icons.map.js b/static/assets/fonts/zulip-icons.map.js index 28b9b38e32a..a164f0de1f5 100644 --- a/static/assets/fonts/zulip-icons.map.js +++ b/static/assets/fonts/zulip-icons.map.js @@ -8,5 +8,9 @@ export default { "bot": 61697, "ellipsis-v-solid": 61698, "gif": 61699, - "globe": 61700 + "globe": 61700, + "language": 61701, + "mute": 61702, + "readreceipts": 61703, + "follow": 61704 }; diff --git a/static/assets/fonts/zulip-icons.ttf b/static/assets/fonts/zulip-icons.ttf index 710f9e0fa4d..a667b70e507 100644 Binary files a/static/assets/fonts/zulip-icons.ttf and b/static/assets/fonts/zulip-icons.ttf differ diff --git a/static/icons/follow.svg b/static/icons/follow.svg new file mode 100644 index 00000000000..3cbded1e623 --- /dev/null +++ b/static/icons/follow.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/translations/messages_en.json b/static/translations/messages_en.json index c9adb956ca2..19796fd8879 100644 --- a/static/translations/messages_en.json +++ b/static/translations/messages_en.json @@ -170,6 +170,10 @@ "Delete topic": "Delete topic", "Deleting a topic will immediately remove it and its messages for everyone. Other users may find this confusing, especially if they had received an email or push notification related to the deleted messages.\n\nAre you sure you want to permanently delete “{topic}”?": "Deleting a topic will immediately remove it and its messages for everyone. Other users may find this confusing, especially if they had received an email or push notification related to the deleted messages.\n\nAre you sure you want to permanently delete “{topic}”?", "Unmute topic": "Unmute topic", + "Follow topic": "Follow topic", + "Failed to follow topic": "Failed to follow topic", + "Unfollow topic": "Unfollow topic", + "Failed to unfollow topic": "Failed to unfollow topic", "Mute stream": "Mute stream", "Unmute stream": "Unmute stream", "No Internet connection": "No Internet connection", diff --git a/tools/build-icon-font b/tools/build-icon-font index 13b19d994c3..ee611adfbb7 100755 --- a/tools/build-icon-font +++ b/tools/build-icon-font @@ -46,12 +46,18 @@ const fontName = 'zulip-icons'; // The root of our tree. const rootDir = path.dirname(__dirname); -async function main() { - const srcDir = path.join(packagePath('@zulip/shared'), 'icons'); - const iconFiles = fs - .readdirSync(srcDir) +function listSvgFiles(dirPath) { + return fs + .readdirSync(dirPath) .filter(name => name.endsWith('.svg')) - .map(name => path.join(srcDir, name)); + .map(name => path.join(dirPath, name)); +} + +async function main() { + const iconFiles = [ + ...listSvgFiles(path.join(packagePath('@zulip/shared'), 'icons')), + ...listSvgFiles(path.join(rootDir, 'static/icons')), + ]; const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'build-icon-font-')); diff --git a/tools/build-webview b/tools/build-webview index f5735f3f051..766166d45fe 100755 --- a/tools/build-webview +++ b/tools/build-webview @@ -194,6 +194,7 @@ sync "src/webview/static" "${dest}" <