+
+ );
+ }
+}
diff --git a/components/widgets/icons/admin_eye_icon.jsx b/components/widgets/icons/admin_eye_icon.jsx
new file mode 100644
index 000000000000..be3f82e1d7e3
--- /dev/null
+++ b/components/widgets/icons/admin_eye_icon.jsx
@@ -0,0 +1,35 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+
+export default class AdminEyeIcon extends React.PureComponent {
+ render() {
+ return (
+
+
+ {(ariaLabel) => (
+
+ )}
+
+
+ );
+ }
+}
diff --git a/e2e/cypress/integration/modals/product_notice_spec.js b/e2e/cypress/integration/modals/product_notice_spec.js
new file mode 100644
index 000000000000..8a0d14e5ca16
--- /dev/null
+++ b/e2e/cypress/integration/modals/product_notice_spec.js
@@ -0,0 +1,103 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+// ***************************************************************
+// - [#] indicates a test step (e.g. # Go to a page)
+// - [*] indicates an assertion (e.g. * Check the title)
+// - Use element ID when selecting an element. Create one if none.
+// ***************************************************************
+
+// Group: @modals
+
+describe('MM-T3361 In product notices', () => {
+ let testTeam;
+ let testUser;
+
+ before(() => {
+ cy.apiInitSetup().then(({team, user}) => {
+ testTeam = team;
+ testUser = user;
+ cy.visit(`/${testTeam.name}/channels/town-square`);
+ });
+ });
+
+ it('MM-T3361_1 UI for miltiple notices', () => {
+ cy.visit(`/${testTeam.name}/channels/town-square`);
+
+ // * Verify that the notices modal is open
+ cy.get('[aria-labelledby="genericModalLabel"').should('be.visible').within(() => {
+ // * Verify the title for sysadmin
+ cy.get('#genericModalLabel').should('be.visible').and('contain', 'Sysadmin notice title');
+
+ // * Verify the description for sysadmin notice
+ cy.get('.productNotices__helpText p').should('be.visible').and('contain', 'your eyes only!');
+
+ // * Verify the action button text and the href of the link
+ cy.get('.GenericModal__body #actionButton').should('have.attr', 'href', 'https://github.com/mattermost/mattermost-server').and('contain', 'Download');
+
+ // * Verify that the previous button is not visible as this is the first notice
+ cy.get('.GenericModal__button.cancel').should('be.not.visible');
+
+ // * Verify that the sysadmin notice has info that it is visible to only sysadmins
+ cy.get('.productNotices__info').within(() => cy.findByText('Visible to Admins only').should('be.visible'));
+
+ // * Verify that the first circle indicator circle has class active
+ cy.get('#tutorialIntroCircle0').should('have.class', 'active');
+
+ // * Verify that there is a next button and click on it
+ cy.findByText('Next').should('be.visible').click();
+
+ // * Verify the end user modal notice title
+ cy.get('#genericModalLabel').should('be.visible').and('contain', 'End user notice title');
+
+ // * Verify the end user modal notice description
+ cy.get('.productNotices__helpText p').should('be.visible').and('contain', 'End user notice description');
+
+ // * Verify the action button text and the href of the link
+ cy.get('.GenericModal__body #actionButton').should('have.attr', 'href', 'https://github.com/mattermost/mattermost-webapp').and('contain', 'Update');
+
+ // * Verify that the previous button is visible as this is the second notice
+ cy.get('.GenericModal__button.cancel').should('be.visible');
+
+ // * Verify that the second circle indicator circle has class active
+ cy.get('#tutorialIntroCircle1').should('have.class', 'active');
+
+ // * Verify that there is a done button and click on it
+ cy.findByText('Done').should('be.visible').click();
+ });
+
+ // * Verify that the notices modal is closed
+ cy.get('[aria-labelledby="genericModalLabel"').should('be.not.visible');
+ cy.reload();
+ cy.get('#postListContent').should('be.visible');
+
+ // * Verify that there is no notices modal
+ cy.get('[aria-labelledby="genericModalLabel"').should('be.not.visible');
+ });
+
+ it('MM-T3361_2 UI for end user notice', () => {
+ cy.apiLogout();
+
+ // # Login as test user and go to town square
+ cy.apiLogin(testUser);
+ cy.visit(`/${testTeam.name}/channels/town-square`);
+
+ // * Verify the end user modal notice title
+ cy.get('#genericModalLabel').should('be.visible').and('contain', 'End user notice title');
+
+ // * Verify the end user modal notice description
+ cy.get('.productNotices__helpText p').should('be.visible').and('contain', 'End user notice description');
+
+ // * Verify there is no action button
+ cy.get('.GenericModal__body #actionButton').should('be.not.visible');
+
+ // * Verify that the previous button is not visible as there is only one notice
+ cy.get('.GenericModal__button.cancel').should('be.not.visible');
+
+ // * Verify that there are no indicators
+ cy.get('.tutorial__circles').should('be.not.visible');
+
+ // * Verify that there is a done button and click on it
+ cy.findByText('Update').should('be.visible');
+ });
+});
diff --git a/i18n/en.json b/i18n/en.json
index c15da0955b24..6a8f2e470e78 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1070,6 +1070,10 @@
"admin.nav.menuAriaLabel": "Admin Console Menu",
"admin.nav.switch": "Team Selection",
"admin.nav.troubleshootingForum": "Troubleshooting Forum",
+ "admin.notices.enableAdminNoticesDescription": "When enabled, System Admins will receive notices about available server upgrades and relevant system administration features. [Learn more about notices](!https://about.mattermost.com/default-notices) in our documentation.",
+ "admin.notices.enableAdminNoticesTitle": "Enable Admin Notices: ",
+ "admin.notices.enableEndUserNoticesDescription": "When enabled, all users will receive notices about available client upgrades and relevant end user features to improve user experience. [Learn more about notices](!https://about.mattermost.com/default-notices) in our documentation.",
+ "admin.notices.enableEndUserNoticesTitle": "Enable End User Notices: ",
"admin.oauth.gitlab": "GitLab",
"admin.oauth.google": "Google Apps",
"admin.oauth.off": "Do not allow sign-in via an OAuth 2.0 provider",
@@ -1642,6 +1646,7 @@
"admin.sidebar.logs": "Server Logs",
"admin.sidebar.metrics": "Performance Monitoring",
"admin.sidebar.mfa": "MFA",
+ "admin.sidebar.notices": "Notices",
"admin.sidebar.notifications": "Notifications",
"admin.sidebar.oauth": "OAuth 2.0",
"admin.sidebar.password": "Password",
@@ -1672,6 +1677,7 @@
"admin.site.emoji": "Emoji",
"admin.site.fileSharingDownloads": "File Sharing and Downloads",
"admin.site.localization": "Localization",
+ "admin.site.notices": "Notices",
"admin.site.posts": "Posts",
"admin.site.public_links": "Public Links",
"admin.site.usersAndTeams": "Users and Teams",
@@ -2557,6 +2563,7 @@
"generic_icons.add": "Add Icon",
"generic_icons.add-mail": "Add Mail Icon",
"generic_icons.add-reaction": "Add Reaction Icon",
+ "generic_icons.adminOnlyIcon": "Admin View Only Icon",
"generic_icons.alert": "Alert Icon",
"generic_icons.archive": "Archive Icon",
"generic_icons.arrow.down": "Down Arrow Icon",
@@ -2609,6 +2616,9 @@
"generic_icons.warning": "Warning Icon",
"generic_modal.cancel": "Cancel",
"generic_modal.confirm": "Confirm",
+ "generic.done": "Done",
+ "generic.next": "Next",
+ "generic.previous": "Previous",
"get_app.continueToBrowser": "View in Browser",
"get_app.dontHaveTheDesktopApp": "Don't have the Desktop App?",
"get_app.dontHaveTheMobileApp": "Don't have the Mobile App?",
@@ -2763,6 +2773,7 @@
"help.messaging.reply": "**Reply to messages** by clicking the reply arrow next to the message text.",
"help.messaging.title": "Messaging Basics",
"help.messaging.write": "**Write messages** using the text input box at the bottom of Mattermost. Press ENTER to send a message. Use SHIFT+ENTER to create a new line without sending a message.",
+ "inProduct_notices.adminOnlyMessage": "Visible to Admins only",
"input.clear": "Clear",
"installed_command.header": "Slash Commands",
"installed_commands.add": "Add Slash Command",
diff --git a/package-lock.json b/package-lock.json
index 065f243d08dd..9612f79563f3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17655,8 +17655,8 @@
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
},
"mattermost-redux": {
- "version": "github:mattermost/mattermost-redux#285e4105b21fcbe8ff091d302b92a9f9a8e0b262",
- "from": "github:mattermost/mattermost-redux#285e4105b21fcbe8ff091d302b92a9f9a8e0b262",
+ "version": "github:mattermost/mattermost-redux#76b5cb575db485f0cc0ed40fdbded2fe020c1350",
+ "from": "github:mattermost/mattermost-redux#76b5cb575db485f0cc0ed40fdbded2fe020c1350",
"requires": {
"core-js": "3.6.5",
"form-data": "3.0.0",
diff --git a/package.json b/package.json
index dc9becf92528..eac3ee6c6f05 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"localforage-observable": "2.0.1",
"mark.js": "8.11.1",
"marked": "github:mattermost/marked#87769262aa02e1784570f61f4f962050e07cc335",
- "mattermost-redux": "github:mattermost/mattermost-redux#285e4105b21fcbe8ff091d302b92a9f9a8e0b262",
+ "mattermost-redux": "github:mattermost/mattermost-redux#76b5cb575db485f0cc0ed40fdbded2fe020c1350",
"moment-timezone": "0.5.31",
"p-queue": "6.6.1",
"pdfjs-dist": "2.1.266",
diff --git a/utils/user_agent.tsx b/utils/user_agent.tsx
index 1d7c01bb46db..906d57c8adda 100644
--- a/utils/user_agent.tsx
+++ b/utils/user_agent.tsx
@@ -144,3 +144,10 @@ export function isWindows7(): boolean {
return (/\bWindows NT 6\.1\b/).test(appVersion);
}
+
+export function getDesktopVersion(): string {
+ // use if the value window.desktop.version is not set yet
+ const regex = /Mattermost\/(\d\.\d\.\d{0,1})/gm;
+ const match = regex.exec(window.navigator.appVersion)?.[1] || '';
+ return match;
+}