From 5af17952c8f4cb6833cb41b83c830375f3925b34 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Wed, 18 Mar 2020 16:05:13 -0400 Subject: [PATCH] Channel Sidebar Organization (#4999) * [MM-20881] Moved out old sidebar and added setting to activate new one (#4553) * Redux update * Update redux commit * Redux merge * Base channel sidebar architecture (#4706) Automatic Merge * [MM-21911] Moved favicon and titlebar update to its own component (#4811) * [MM-21911] Moved favicon and titlebar update to its own component * Moved favicon/title tests to their own file * PR feedback * [MM-21907][MM-21908] Unread Handling and Scrolling (#4799) * WIP * Barely working sidebar * Temp fix so you can use the sidebar better * Lint and type fixes * Unread badge and bolding class added * Split up sidebar channel types into different components * Lint/type fixes * WIP * PR feedback * Lint fix * Unread indicators and scrolling! * Auto scrolling when changing teams/when channel is in view * Lint and type fixes * Another type fix * Removing unnecessary styling * PR feedback * package fix * Reverted to regular scrollbars * Whoops, left my dumb test styling in :P * PR feedback * aaa * [MM-21909] Keyboard shortcuts and a11y-controller classes (#4821) * WIP * Barely working sidebar * Temp fix so you can use the sidebar better * Lint and type fixes * Unread badge and bolding class added * Split up sidebar channel types into different components * Lint/type fixes * WIP * PR feedback * Lint fix * Unread indicators and scrolling! * Auto scrolling when changing teams/when channel is in view * Lint and type fixes * Another type fix * Removing unnecessary styling * PR feedback * package fix * Added a11y classes to new sidebar * Keyboard shortcuts * Reverted to regular scrollbars * Whoops, left my dumb test styling in :P * PR feedback * Removed isSwitchingChannel * aaa * Revert redux commit until tests are fixed * [MM-22290] Collapsible Category Logic (#4873) * Barebone collapsing of categories (no saving) * Take into account unreads and if the channel is the current visible channel * Reactifying and cleaning up a couple small things * More cleanup * Clarity * Added new style sheet for new sidebar, and the animation for collapsing categories * Lint/type fixes and accessibility * Moved a11y section to just the title, for better reading and navigation * Removing code that won't be used * Merge'd * Category name is repeated * PR feedback * Removing unnecessary code * [MM-21910] Modal Handling for new Sidebar (#4847) * WIP * Barely working sidebar * Temp fix so you can use the sidebar better * Lint and type fixes * Unread badge and bolding class added * Split up sidebar channel types into different components * Lint/type fixes * WIP * PR feedback * Lint fix * Unread indicators and scrolling! * Auto scrolling when changing teams/when channel is in view * Lint and type fixes * Another type fix * Removing unnecessary styling * PR feedback * package fix * Added a11y classes to new sidebar * Keyboard shortcuts * Reverted to regular scrollbars * Whoops, left my dumb test styling in :P * Moved modals over to sidebar * PR feedback * Removed isSwitchingChannel * aaa * Modals functional * PR feedback * Merge'd * MM-22114 Add view state for collapsed categories and unread filter (#4914) * [MM-22291] Additional functionality from old sidebar (#4860) * [MM-22291] Additional functionality from old sidebar * PR feedback * PR feedback * Lank bline * Lint again * PR feedback * Merge'd * PR feedback * MM-21584 Replace removeItem call with setItem (#5003) * Update redux commit * Merging of CSS styles into new sidebar code (#4998) * [MM-22291] Additional functionality from old sidebar * PR feedback * PR feedback * Lank bline * Lint again * PR feedback * Merge'd * Actual functioning sidebar! * Using react-custom-scrollbars again and fixed a few styling issues * PR feedback * Lint fixes Co-authored-by: mattermod * [MM-22478] Unread Filtering and Create New Channel Menu (#5005) * [MM-22291] Additional functionality from old sidebar * PR feedback * PR feedback * Lank bline * Lint again * PR feedback * Merge'd * Actual functioning sidebar! * Using react-custom-scrollbars again and fixed a few styling issues * PR feedback * Added base structure * Unread filter and add channel dropdown design and some functionality * Unread filtering and collapsible category logic hookup to redux * Lint fixes * Added translations * Disable collapse when unread filter is on * Merge'd * PR feedback * And more Co-authored-by: mattermod * CSS fix for single team instances * PR feedback and some more missing redux hookups * Mobile/Desktop Sidebar View + Restored Team Sidebar and Header (#5016) * [MM-22291] Additional functionality from old sidebar * PR feedback * PR feedback * Lank bline * Lint again * PR feedback * Merge'd * Actual functioning sidebar! * Using react-custom-scrollbars again and fixed a few styling issues * PR feedback * Lint fixes * WIP * Added back original team sidebar and sidebar header for Phase 1 * Mobile bar fits, still styling issues * Style and lint fixes to make the sidebar work properly * Desktop and other mobile fixes * Merge'd Co-authored-by: mattermod * Fixed labels to match spec * Snapshot update * Channel Switcher + History Button Style (#5023) * [MM-22291] Additional functionality from old sidebar * PR feedback * PR feedback * Lank bline * Lint again * PR feedback * Merge'd * Actual functioning sidebar! * Using react-custom-scrollbars again and fixed a few styling issues * PR feedback * Lint fixes * Added channel switcher modal button and back/forward buttons * Lint fixes and formatting * Styled arrows and buttons, and mock back/forward * Unmocking for Phase 1 * Removed unnecessary ms-filter Co-authored-by: mattermod * Fixed so that the swap between old and new sidebars is instant, mobile view fixes * Type fix * Lint fix * PR feedback for Eric/Harrison * A bunch of alignment changes * Another design fix * [MM-23056] Analytics for new and old sidebar * More alignment fixes * Fixed letter cutoff issue and an alignment issue * [MM-22834] Hide unused Account Settings options when the new sidebar is enabled * Even more more alignment fixes, a test fix and some scrolling fixes * Additional alignment and style fixes * Alignment fixes for the history arrows * Fixed a test and a couple small issues * Added missing tooltips * Fix for the team switching crashing issue * Lint fix * A lot of a11y fixes, still need more * Lint and test fixes * Added some missing aria-labels * Fix crash issue #2 * Removed autoclose redirect since it can't fire * Removed more dead code * Redux update * PR feedback * Fixed a couple more small issues * Fixed a redux issue * More redux fixes and a bug fix * Fixed a test * Lint error Co-authored-by: Harrison Healey Co-authored-by: mattermod --- actions/views/channel_sidebar.test.ts | 33 + actions/views/channel_sidebar.ts | 30 + components/admin_console/admin_definition.jsx | 30 +- .../channel_header.test.jsx.snap | 14 +- components/channel_header/channel_header.js | 2 +- .../channel_layout/channel_controller.jsx | 15 +- .../channel_controller.test.jsx | 1 + .../favicon_title_handler.test.jsx | 84 ++ .../favicon_title_handler.tsx | 116 +++ components/favicon_title_handler/index.ts | 38 + .../__snapshots__/sidebar.test.jsx.snap | 24 +- .../sidebar_tutorial_tip.test.jsx.snap | 8 +- .../channel_create.jsx | 0 .../channel_more.jsx | 0 .../channel_name.jsx | 0 .../header/dropdown/index.js | 0 .../dropdown/sidebar_header_dropdown.jsx | 0 .../header/index.js | 0 .../header/sidebar_header.jsx | 0 .../header/sidebar_header_dropdown_button.jsx | 0 .../{sidebar => legacy_sidebar}/index.js | 0 .../more_public_direct_channels.jsx | 0 .../{sidebar => legacy_sidebar}/sidebar.jsx | 85 +- .../sidebar.test.jsx | 46 +- .../sidebar_channel.test.jsx.snap | 34 +- .../sidebar_channel/index.js | 0 .../sidebar_channel/sidebar_channel.jsx | 0 .../sidebar_channel/sidebar_channel.test.jsx | 2 +- ...debar_channel_button_or_link.test.jsx.snap | 10 +- ..._button_or_link_close_button.test.jsx.snap | 12 +- ..._channel_button_or_link_icon.test.jsx.snap | 48 + .../sidebar_channel_button_or_link.jsx | 0 .../sidebar_channel_button_or_link.test.jsx | 5 +- ...ar_channel_button_or_link_close_button.jsx | 0 ...annel_button_or_link_close_button.test.jsx | 6 +- .../sidebar_channel_button_or_link_icon.jsx | 0 ...debar_channel_button_or_link_icon.test.jsx | 5 +- .../sidebar_tutorial_tip.jsx | 0 .../sidebar_tutorial_tip.test.jsx | 5 +- .../components/team_button.jsx | 0 .../index.js | 4 +- .../legacy_team_sidebar_controller.jsx} | 2 +- components/needs_team/index.ts | 3 +- components/needs_team/needs_team.test.tsx | 1 + components/needs_team/needs_team.tsx | 2 + .../__snapshots__/search_bar.test.jsx.snap | 8 +- components/search_bar/search_bar.jsx | 2 +- components/sidebar/add_channel_dropdown.tsx | 76 ++ .../sidebar/channel_filter/channel_filter.tsx | 112 +++ components/sidebar/channel_filter/index.ts | 29 + .../channel_navigator/channel_navigator.tsx | 99 ++ components/sidebar/channel_navigator/index.ts | 53 ++ components/sidebar/index.ts | 43 + components/sidebar/sidebar.tsx | 151 +++ components/sidebar/sidebar_category/index.ts | 40 + .../sidebar_category/sidebar_category.tsx | 189 ++++ .../sidebar/sidebar_category_list/index.ts | 47 + .../sidebar_category_list.tsx | 386 ++++++++ .../sidebar_channel/channel_mention_badge.tsx | 30 + components/sidebar/sidebar_channel/index.ts | 57 ++ .../sidebar_base_channel/index.ts | 42 + .../sidebar_base_channel.tsx | 93 ++ .../sidebar_channel/sidebar_channel.tsx | 134 +++ .../sidebar_channel/sidebar_channel_close.tsx | 90 ++ .../sidebar_channel_icon/index.ts | 41 + .../sidebar_channel_icon.tsx | 31 + .../sidebar_channel_link/index.ts | 53 ++ .../sidebar_channel_link.tsx | 199 ++++ .../sidebar_direct_channel/index.ts | 67 ++ .../sidebar_direct_channel.tsx | 141 +++ .../sidebar_group_channel/index.ts | 62 ++ .../sidebar_group_channel.tsx | 65 ++ ..._channel_button_or_link_icon.test.jsx.snap | 48 - components/sidebar/sidebar_header/index.ts | 29 + .../sidebar/sidebar_header/sidebar_header.tsx | 57 ++ components/status_icon_new.tsx | 35 + components/team_sidebar/index.ts | 26 + components/team_sidebar/team_button/index.ts | 24 + .../team_sidebar/team_button/team_button.tsx | 24 + components/team_sidebar/team_sidebar.tsx | 46 + .../user_settings_sidebar.test.jsx.snap | 21 + components/user_settings/sidebar/index.js | 5 +- .../sidebar/user_settings_sidebar.jsx | 131 ++- .../sidebar/user_settings_sidebar.test.jsx | 13 + fonts/mattermosticons.eot | Bin 0 -> 42600 bytes fonts/mattermosticons.svg | 280 ++++++ fonts/mattermosticons.ttf | Bin 0 -> 42404 bytes fonts/mattermosticons.woff | Bin 0 -> 23808 bytes fonts/mattermosticons.woff2 | Bin 0 -> 19956 bytes i18n/en.json | 25 +- package-lock.json | 33 +- package.json | 3 +- reducers/views/channel_sidebar.ts | 25 + reducers/views/index.js | 2 + sass/base/_mattermost-icons.scss | 194 ++++ sass/base/_module.scss | 1 + sass/layout/_headers.scss | 1 + sass/layout/_legacy-sidebar-left.scss | 490 ++++++++++ sass/layout/_module.scss | 1 + sass/layout/_sidebar-left.scss | 865 ++++++++++-------- sass/responsive/_mobile.scss | 114 ++- selectors/storage.js | 6 +- selectors/views/channel_sidebar.test.ts | 48 + selectors/views/channel_sidebar.ts | 79 ++ types/store/index.ts | 8 +- utils/constants.jsx | 3 + utils/utils.jsx | 4 + 107 files changed, 5010 insertions(+), 636 deletions(-) create mode 100644 actions/views/channel_sidebar.test.ts create mode 100644 actions/views/channel_sidebar.ts create mode 100644 components/favicon_title_handler/favicon_title_handler.test.jsx create mode 100644 components/favicon_title_handler/favicon_title_handler.tsx create mode 100644 components/favicon_title_handler/index.ts rename components/{sidebar => legacy_sidebar}/__snapshots__/sidebar.test.jsx.snap (97%) rename components/{sidebar => legacy_sidebar}/__snapshots__/sidebar_tutorial_tip.test.jsx.snap (96%) rename components/{sidebar => legacy_sidebar}/channel_create.jsx (100%) rename components/{sidebar => legacy_sidebar}/channel_more.jsx (100%) rename components/{sidebar => legacy_sidebar}/channel_name.jsx (100%) rename components/{sidebar => legacy_sidebar}/header/dropdown/index.js (100%) rename components/{sidebar => legacy_sidebar}/header/dropdown/sidebar_header_dropdown.jsx (100%) rename components/{sidebar => legacy_sidebar}/header/index.js (100%) rename components/{sidebar => legacy_sidebar}/header/sidebar_header.jsx (100%) rename components/{sidebar => legacy_sidebar}/header/sidebar_header_dropdown_button.jsx (100%) rename components/{sidebar => legacy_sidebar}/index.js (100%) rename components/{sidebar => legacy_sidebar}/more_public_direct_channels.jsx (100%) rename components/{sidebar => legacy_sidebar}/sidebar.jsx (89%) rename components/{sidebar => legacy_sidebar}/sidebar.test.jsx (90%) rename components/{sidebar => legacy_sidebar}/sidebar_channel/__snapshots__/sidebar_channel.test.jsx.snap (80%) rename components/{sidebar => legacy_sidebar}/sidebar_channel/index.js (100%) rename components/{sidebar => legacy_sidebar}/sidebar_channel/sidebar_channel.jsx (100%) rename components/{sidebar => legacy_sidebar}/sidebar_channel/sidebar_channel.test.jsx (99%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/__snapshots__/sidebar_channel_button_or_link.test.jsx.snap (82%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/__snapshots__/sidebar_channel_button_or_link_close_button.test.jsx.snap (67%) create mode 100644 components/legacy_sidebar/sidebar_channel_button_or_link/__snapshots__/sidebar_channel_button_or_link_icon.test.jsx.snap rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/sidebar_channel_button_or_link.jsx (100%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/sidebar_channel_button_or_link.test.jsx (95%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/sidebar_channel_button_or_link_close_button.jsx (100%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/sidebar_channel_button_or_link_close_button.test.jsx (91%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/sidebar_channel_button_or_link_icon.jsx (100%) rename components/{sidebar => legacy_sidebar}/sidebar_channel_button_or_link/sidebar_channel_button_or_link_icon.test.jsx (92%) rename components/{sidebar => legacy_sidebar}/sidebar_tutorial_tip.jsx (100%) rename components/{sidebar => legacy_sidebar}/sidebar_tutorial_tip.test.jsx (92%) rename components/{team_sidebar => legacy_team_sidebar}/components/team_button.jsx (100%) rename components/{team_sidebar => legacy_team_sidebar}/index.js (94%) rename components/{team_sidebar/team_sidebar_controller.jsx => legacy_team_sidebar/legacy_team_sidebar_controller.jsx} (99%) create mode 100644 components/sidebar/add_channel_dropdown.tsx create mode 100644 components/sidebar/channel_filter/channel_filter.tsx create mode 100644 components/sidebar/channel_filter/index.ts create mode 100644 components/sidebar/channel_navigator/channel_navigator.tsx create mode 100644 components/sidebar/channel_navigator/index.ts create mode 100644 components/sidebar/index.ts create mode 100644 components/sidebar/sidebar.tsx create mode 100644 components/sidebar/sidebar_category/index.ts create mode 100644 components/sidebar/sidebar_category/sidebar_category.tsx create mode 100644 components/sidebar/sidebar_category_list/index.ts create mode 100644 components/sidebar/sidebar_category_list/sidebar_category_list.tsx create mode 100644 components/sidebar/sidebar_channel/channel_mention_badge.tsx create mode 100644 components/sidebar/sidebar_channel/index.ts create mode 100644 components/sidebar/sidebar_channel/sidebar_base_channel/index.ts create mode 100644 components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.tsx create mode 100644 components/sidebar/sidebar_channel/sidebar_channel.tsx create mode 100644 components/sidebar/sidebar_channel/sidebar_channel_close.tsx create mode 100644 components/sidebar/sidebar_channel/sidebar_channel_icon/index.ts create mode 100644 components/sidebar/sidebar_channel/sidebar_channel_icon/sidebar_channel_icon.tsx create mode 100644 components/sidebar/sidebar_channel/sidebar_channel_link/index.ts create mode 100644 components/sidebar/sidebar_channel/sidebar_channel_link/sidebar_channel_link.tsx create mode 100644 components/sidebar/sidebar_channel/sidebar_direct_channel/index.ts create mode 100644 components/sidebar/sidebar_channel/sidebar_direct_channel/sidebar_direct_channel.tsx create mode 100644 components/sidebar/sidebar_channel/sidebar_group_channel/index.ts create mode 100644 components/sidebar/sidebar_channel/sidebar_group_channel/sidebar_group_channel.tsx delete mode 100644 components/sidebar/sidebar_channel_button_or_link/__snapshots__/sidebar_channel_button_or_link_icon.test.jsx.snap create mode 100644 components/sidebar/sidebar_header/index.ts create mode 100644 components/sidebar/sidebar_header/sidebar_header.tsx create mode 100644 components/status_icon_new.tsx create mode 100644 components/team_sidebar/index.ts create mode 100644 components/team_sidebar/team_button/index.ts create mode 100644 components/team_sidebar/team_button/team_button.tsx create mode 100644 components/team_sidebar/team_sidebar.tsx create mode 100644 fonts/mattermosticons.eot create mode 100644 fonts/mattermosticons.svg create mode 100644 fonts/mattermosticons.ttf create mode 100644 fonts/mattermosticons.woff create mode 100644 fonts/mattermosticons.woff2 create mode 100644 reducers/views/channel_sidebar.ts create mode 100755 sass/base/_mattermost-icons.scss create mode 100644 sass/layout/_legacy-sidebar-left.scss create mode 100644 selectors/views/channel_sidebar.test.ts create mode 100644 selectors/views/channel_sidebar.ts diff --git a/actions/views/channel_sidebar.test.ts b/actions/views/channel_sidebar.test.ts new file mode 100644 index 000000000000..71a86bf834e9 --- /dev/null +++ b/actions/views/channel_sidebar.test.ts @@ -0,0 +1,33 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import configureStore from 'store'; + +import {isCategoryCollapsedFromStorage} from 'selectors/views/channel_sidebar'; +import {getPrefix} from 'utils/storage_utils'; + +import * as Actions from './channel_sidebar'; + +describe('setCategoryCollapsed', () => { + test('should save category expanded and category collapsed', async () => { + const category1 = 'category1'; + const initialState = { + users: { + currentUserId: 'user1', + profiles: { + user1: {}, + } + } + }; + + const store = await configureStore(initialState); + + store.dispatch(Actions.setCategoryCollapsed(category1, true)); + + expect(isCategoryCollapsedFromStorage(getPrefix(store.getState()), store.getState().storage.storage, category1)).toBe(true); + + store.dispatch(Actions.setCategoryCollapsed(category1, false)); + + expect(isCategoryCollapsedFromStorage(getPrefix(store.getState()), store.getState().storage.storage, category1)).toBe(false); + }); +}); diff --git a/actions/views/channel_sidebar.ts b/actions/views/channel_sidebar.ts new file mode 100644 index 000000000000..2477f92c0917 --- /dev/null +++ b/actions/views/channel_sidebar.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {setItem} from 'actions/storage'; + +import {ActionTypes, StoragePrefixes} from 'utils/constants'; + +export function collapseCategory(categoryId: string) { + return setItem(StoragePrefixes.CHANNEL_CATEGORY_COLLAPSED + categoryId, true); +} + +export function expandCategory(categoryId: string) { + // You should be able to use removeItem here, but removeItem was not working at all + return setItem(StoragePrefixes.CHANNEL_CATEGORY_COLLAPSED + categoryId, false); +} + +export function setCategoryCollapsed(categoryId: string, collapsed: boolean) { + if (collapsed) { + return collapseCategory(categoryId); + } + + return expandCategory(categoryId); +} + +export function setUnreadFilterEnabled(enabled: boolean) { + return { + type: ActionTypes.SET_UNREAD_FILTER_ENABLED, + enabled, + }; +} diff --git a/components/admin_console/admin_definition.jsx b/components/admin_console/admin_definition.jsx index 60c946b988ec..2d4573ea65b9 100644 --- a/components/admin_console/admin_definition.jsx +++ b/components/admin_console/admin_definition.jsx @@ -4456,13 +4456,39 @@ const AdminDefinition = { help_text_markdown: false, isHidden: it.isnt(it.licensedForFeature('SAML')), }, + { + type: Constants.SettingsTypes.TYPE_DROPDOWN, + key: 'ServiceSettings.ExperimentalChannelSidebarOrganization', + label: t('admin.experimental.experimentalChannelSidebarOrganization.title'), + label_default: 'Experimental Sidebar Features', + help_text: t('admin.experimental.experimentalChannelSidebarOrganization.desc'), + help_text_default: 'When enabled, users can access experimental channel sidebar features, including collapsible sections and unreads filtering. If default on, this enabled the new sidebar features by default for all users on this server. Users can disable the features in **Account Settings > Sidebar > Experimental Sidebar Features**. If default off, users must enable the experimental sidebar features in Account Settings. [Learn more](!https://about.mattermost.com/default-sidebar/) or [give us feedback](!https://about.mattermost.com/default-sidebar-survey/)', + help_text_markdown: true, + options: [ + { + value: 'disabled', + display_name: t('admin.experimental.experimentalChannelSidebarOrganization.disabled'), + display_name_default: 'Disabled', + }, + { + value: 'default_on', + display_name: t('admin.experimental.experimentalChannelSidebarOrganization.default_on'), + display_name_default: 'Enabled (Default On)', + }, + { + value: 'default_off', + display_name: t('admin.experimental.experimentalChannelSidebarOrganization.default_off'), + display_name_default: 'Enabled (Default Off)', + }, + ], + }, { type: Constants.SettingsTypes.TYPE_BOOL, key: 'ServiceSettings.ExperimentalChannelOrganization', label: t('admin.experimental.experimentalChannelOrganization.title'), - label_default: 'Sidebar Organization:', + label_default: 'Channel Grouping and Sorting', help_text: t('admin.experimental.experimentalChannelOrganization.desc'), - help_text_default: 'Enables channel sidebar organization options in **Account Settings > Sidebar > Channel grouping and sorting** including options for grouping unread channels, sorting channels by most recent post and combining all channel types into a single list.', + help_text_default: 'Enables channel sidebar organization options in **Account Settings > Sidebar > Channel grouping and sorting** including options for grouping unread channels, sorting channels by most recent post and combining all channel types into a single list. These settings are not available if **Account Settings > Sidebar > Experimental Sidebar Features** are enabled.', help_text_markdown: true, }, { diff --git a/components/channel_header/__snapshots__/channel_header.test.jsx.snap b/components/channel_header/__snapshots__/channel_header.test.jsx.snap index 9e085955f144..4f2bc8f3db2a 100644 --- a/components/channel_header/__snapshots__/channel_header.test.jsx.snap +++ b/components/channel_header/__snapshots__/channel_header.test.jsx.snap @@ -4,7 +4,7 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = `