[MM-61716][MM-62643] Introduce Modal component, remove bootstrap and react-bootstrap from most modals, update styles (#3317)
* Introduce Modal, add some styles from the webapp * Remove bootstrap from LoadingScreen/WelcomeScreen * Migrate add/edit server modal to Modal, remove bootstrap * Migrate remove server modal to Modal, remove bootstrap * Migrate certificate modal to Modal, remove bootstrap * Migrate login and permission modals to Modal, remove Bootstrap * Misc fixes * E2E tests for current modals * Update src/renderer/components/Modal.tsx Co-authored-by: Daniel Espino García <larkox@gmail.com> * Update src/renderer/components/Modal.tsx Co-authored-by: Daniel Espino García <larkox@gmail.com> * PR feedback --------- Co-authored-by: Daniel Espino García <larkox@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
@@ -99,8 +99,8 @@ describe('focus', function desc() {
|
|||||||
const newServerView = await this.app.waitForEvent('window', {
|
const newServerView = await this.app.waitForEvent('window', {
|
||||||
predicate: (window) => window.url().includes('newServer'),
|
predicate: (window) => window.url().includes('newServer'),
|
||||||
});
|
});
|
||||||
await newServerView.waitForSelector('#cancelNewServerModal');
|
await newServerView.waitForSelector('#newServerModal_cancel');
|
||||||
await newServerView.click('#cancelNewServerModal');
|
await newServerView.click('#newServerModal_cancel');
|
||||||
|
|
||||||
const isTextboxFocused = await firstServer.$eval('#post_textbox', (el) => el === document.activeElement);
|
const isTextboxFocused = await firstServer.$eval('#post_textbox', (el) => el === document.activeElement);
|
||||||
isTextboxFocused.should.be.true;
|
isTextboxFocused.should.be.true;
|
||||||
|
@@ -80,7 +80,7 @@ describe('menu_bar/dropdown', function desc() {
|
|||||||
const newServerModal = await this.app.waitForEvent('window', {
|
const newServerModal = await this.app.waitForEvent('window', {
|
||||||
predicate: (window) => window.url().includes('newServer'),
|
predicate: (window) => window.url().includes('newServer'),
|
||||||
});
|
});
|
||||||
const modalTitle = await newServerModal.innerText('#newServerModal .modal-title');
|
const modalTitle = await newServerModal.innerText('#newServerModal .Modal__header__text_container');
|
||||||
modalTitle.should.equal('Add Server');
|
modalTitle.should.equal('Add Server');
|
||||||
|
|
||||||
await afterFunc();
|
await afterFunc();
|
||||||
|
@@ -46,7 +46,7 @@ describe('Add Server Modal', function desc() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4388 should close the window after clicking cancel', async () => {
|
it('MM-T4388 should close the window after clicking cancel', async () => {
|
||||||
await newServerView.click('#cancelNewServerModal');
|
await newServerView.click('#newServerModal_cancel');
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(this.app.windows().find((window) => window.url().includes('newServer')));
|
const existing = Boolean(this.app.windows().find((window) => window.url().includes('newServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
@@ -54,19 +54,17 @@ describe('Add Server Modal', function desc() {
|
|||||||
|
|
||||||
describe('MM-T4389 Invalid messages', () => {
|
describe('MM-T4389 Invalid messages', () => {
|
||||||
it('MM-T4389_1 should not be valid and save should be disabled if no server name or URL has been set', async () => {
|
it('MM-T4389_1 should not be valid and save should be disabled if no server name or URL has been set', async () => {
|
||||||
const existing = await newServerView.isVisible('#nameValidation.error');
|
const disabled = await newServerView.getAttribute('#newServerModal_confirm', 'disabled');
|
||||||
existing.should.be.true;
|
|
||||||
const disabled = await newServerView.getAttribute('#saveNewServerModal', 'disabled');
|
|
||||||
(disabled === '').should.be.true;
|
(disabled === '').should.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should warn the user if a server with the same URL exists, but still allow them to save', async () => {
|
it('should warn the user if a server with the same URL exists, but still allow them to save', async () => {
|
||||||
await newServerView.type('#serverNameInput', 'some-new-server');
|
await newServerView.type('#serverNameInput', 'some-new-server');
|
||||||
await newServerView.type('#serverUrlInput', config.teams[0].url);
|
await newServerView.type('#serverUrlInput', config.teams[0].url);
|
||||||
await newServerView.waitForSelector('#urlValidation.warning');
|
await newServerView.waitForSelector('#customMessage_url.Input___warning');
|
||||||
const existing = await newServerView.isVisible('#urlValidation.warning');
|
const existing = await newServerView.isVisible('#customMessage_url.Input___warning');
|
||||||
existing.should.be.true;
|
existing.should.be.true;
|
||||||
const disabled = await newServerView.getAttribute('#saveNewServerModal', 'disabled');
|
const disabled = await newServerView.getAttribute('#newServerModal_confirm', 'disabled');
|
||||||
(disabled === '').should.be.false;
|
(disabled === '').should.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,8 +74,8 @@ describe('Add Server Modal', function desc() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4389_2 Name should not be marked invalid, but should not be able to save', async () => {
|
it('MM-T4389_2 Name should not be marked invalid, but should not be able to save', async () => {
|
||||||
await newServerView.waitForSelector('#nameValidation.error', {state: 'detached'});
|
await newServerView.waitForSelector('#customMessage_name.Input___error', {state: 'detached'});
|
||||||
const disabled = await newServerView.getAttribute('#saveNewServerModal', 'disabled');
|
const disabled = await newServerView.getAttribute('#newServerModal_confirm', 'disabled');
|
||||||
(disabled === '').should.be.true;
|
(disabled === '').should.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -88,9 +86,10 @@ describe('Add Server Modal', function desc() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4389_3 URL should not be marked invalid, name should be marked invalid', async () => {
|
it('MM-T4389_3 URL should not be marked invalid, name should be marked invalid', async () => {
|
||||||
const existingUrl = await newServerView.isVisible('#urlValidation.error');
|
await newServerView.waitForSelector('#customMessage_name.Input___error');
|
||||||
const existingName = await newServerView.isVisible('#nameValidation.error');
|
const existingUrl = await newServerView.isVisible('#customMessage_url.Input___error');
|
||||||
const disabled = await newServerView.getAttribute('#saveNewServerModal', 'disabled');
|
const existingName = await newServerView.isVisible('#customMessage_name.Input___error');
|
||||||
|
const disabled = await newServerView.getAttribute('#newServerModal_confirm', 'disabled');
|
||||||
existingName.should.be.true;
|
existingName.should.be.true;
|
||||||
existingUrl.should.be.false;
|
existingUrl.should.be.false;
|
||||||
(disabled === '').should.be.true;
|
(disabled === '').should.be.true;
|
||||||
@@ -100,8 +99,8 @@ describe('Add Server Modal', function desc() {
|
|||||||
|
|
||||||
it('MM-T2826_1 should not be valid if an invalid server address has been set', async () => {
|
it('MM-T2826_1 should not be valid if an invalid server address has been set', async () => {
|
||||||
await newServerView.type('#serverUrlInput', 'superInvalid url');
|
await newServerView.type('#serverUrlInput', 'superInvalid url');
|
||||||
await newServerView.waitForSelector('#urlValidation.error');
|
await newServerView.waitForSelector('#customMessage_url.Input___error');
|
||||||
const existing = await newServerView.isVisible('#urlValidation.error');
|
const existing = await newServerView.isVisible('#customMessage_url.Input___error');
|
||||||
existing.should.be.true;
|
existing.should.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,16 +108,16 @@ describe('Add Server Modal', function desc() {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await newServerView.type('#serverUrlInput', 'http://example.org');
|
await newServerView.type('#serverUrlInput', 'http://example.org');
|
||||||
await newServerView.type('#serverNameInput', 'TestServer');
|
await newServerView.type('#serverNameInput', 'TestServer');
|
||||||
await newServerView.waitForSelector('#urlValidation.warning');
|
await newServerView.waitForSelector('#customMessage_url.Input___warning');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be possible to click add', async () => {
|
it('should be possible to click add', async () => {
|
||||||
const disabled = await newServerView.getAttribute('#saveNewServerModal', 'disabled');
|
const disabled = await newServerView.getAttribute('#newServerModal_confirm', 'disabled');
|
||||||
(disabled === null).should.be.true;
|
(disabled === null).should.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T2826_2 should add the server to the config file', async () => {
|
it('MM-T2826_2 should add the server to the config file', async () => {
|
||||||
await newServerView.click('#saveNewServerModal');
|
await newServerView.click('#newServerModal_confirm');
|
||||||
await asyncSleep(2000);
|
await asyncSleep(2000);
|
||||||
const existing = Boolean(this.app.windows().find((window) => window.url().includes('newServer')));
|
const existing = Boolean(this.app.windows().find((window) => window.url().includes('newServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
|
@@ -40,7 +40,7 @@ describe('EditServerModal', function desc() {
|
|||||||
let editServerView;
|
let editServerView;
|
||||||
|
|
||||||
it('should not edit server when Cancel is pressed', async () => {
|
it('should not edit server when Cancel is pressed', async () => {
|
||||||
await editServerView.click('#cancelNewServerModal');
|
await editServerView.click('#newServerModal_cancel');
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
@@ -70,7 +70,7 @@ describe('EditServerModal', function desc() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4391_1 should not edit server when Save is pressed but nothing edited', async () => {
|
it('MM-T4391_1 should not edit server when Save is pressed but nothing edited', async () => {
|
||||||
await editServerView.click('#saveNewServerModal');
|
await editServerView.click('#newServerModal_confirm');
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
@@ -100,15 +100,15 @@ describe('EditServerModal', function desc() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T2826_3 should not edit server if an invalid server address has been set', async () => {
|
it('MM-T2826_3 should not edit server if an invalid server address has been set', async () => {
|
||||||
await editServerView.type('#serverUrlInput', 'superInvalid url');
|
await editServerView.fill('#serverUrlInput', 'superInvalid url');
|
||||||
await editServerView.waitForSelector('#urlValidation.error');
|
await editServerView.waitForSelector('#customMessage_url.Input___error');
|
||||||
const existing = await editServerView.isVisible('#urlValidation.error');
|
const existing = await editServerView.isVisible('#customMessage_url.Input___error');
|
||||||
existing.should.be.true;
|
existing.should.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4391_2 should edit server when Save is pressed and name edited', async () => {
|
it('MM-T4391_2 should edit server when Save is pressed and name edited', async () => {
|
||||||
await editServerView.fill('#serverNameInput', 'NewTestServer');
|
await editServerView.fill('#serverNameInput', 'NewTestServer');
|
||||||
await editServerView.click('#saveNewServerModal');
|
await editServerView.click('#newServerModal_confirm');
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
@@ -160,7 +160,7 @@ describe('EditServerModal', function desc() {
|
|||||||
|
|
||||||
it('MM-T4391_3 should edit server when Save is pressed and URL edited', async () => {
|
it('MM-T4391_3 should edit server when Save is pressed and URL edited', async () => {
|
||||||
await editServerView.fill('#serverUrlInput', 'http://google.com');
|
await editServerView.fill('#serverUrlInput', 'http://google.com');
|
||||||
await editServerView.click('#saveNewServerModal');
|
await editServerView.click('#newServerModal_confirm');
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
@@ -213,7 +213,7 @@ describe('EditServerModal', function desc() {
|
|||||||
it('MM-T4391_4 should edit server when Save is pressed and both edited', async () => {
|
it('MM-T4391_4 should edit server when Save is pressed and both edited', async () => {
|
||||||
await editServerView.fill('#serverNameInput', 'NewTestServer');
|
await editServerView.fill('#serverNameInput', 'NewTestServer');
|
||||||
await editServerView.fill('#serverUrlInput', 'http://google.com');
|
await editServerView.fill('#serverUrlInput', 'http://google.com');
|
||||||
await editServerView.click('#saveNewServerModal');
|
await editServerView.click('#newServerModal_confirm');
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('editServer')));
|
||||||
existing.should.be.false;
|
existing.should.be.false;
|
||||||
|
@@ -45,7 +45,7 @@ describe('LongServerName', function desc() {
|
|||||||
it('MM-T4050 Long server name', async () => {
|
it('MM-T4050 Long server name', async () => {
|
||||||
await newServerView.type('#serverNameInput', longServerName);
|
await newServerView.type('#serverNameInput', longServerName);
|
||||||
await newServerView.type('#serverUrlInput', longServerUrl);
|
await newServerView.type('#serverUrlInput', longServerUrl);
|
||||||
await newServerView.click('#saveNewServerModal');
|
await newServerView.click('#newServerModal_confirm');
|
||||||
|
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(this.app.windows().find((window) => window.url().includes('newServer')));
|
const existing = Boolean(this.app.windows().find((window) => window.url().includes('newServer')));
|
||||||
|
@@ -73,7 +73,7 @@ describe('RemoveServerModal', function desc() {
|
|||||||
it('MM-T4390_4 should disappear on click background', async () => {
|
it('MM-T4390_4 should disappear on click background', async () => {
|
||||||
// ignore any target closed error
|
// ignore any target closed error
|
||||||
try {
|
try {
|
||||||
await removeServerView.click('.modal', {position: {x: 20, y: 20}});
|
await removeServerView.click('.Modal', {position: {x: 20, y: 20}});
|
||||||
} catch {} // eslint-disable-line no-empty
|
} catch {} // eslint-disable-line no-empty
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('removeServer')));
|
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('removeServer')));
|
||||||
|
@@ -146,6 +146,8 @@
|
|||||||
"main.windows.mainWindow.minimizeToTray.dialog.checkboxLabel": "Don't show again",
|
"main.windows.mainWindow.minimizeToTray.dialog.checkboxLabel": "Don't show again",
|
||||||
"main.windows.mainWindow.minimizeToTray.dialog.message": "{appName} will continue to run in the system tray. This can be disabled in Settings.",
|
"main.windows.mainWindow.minimizeToTray.dialog.message": "{appName} will continue to run in the system tray. This can be disabled in Settings.",
|
||||||
"main.windows.mainWindow.minimizeToTray.dialog.title": "Minimize to Tray",
|
"main.windows.mainWindow.minimizeToTray.dialog.title": "Minimize to Tray",
|
||||||
|
"modal.cancel": "Cancel",
|
||||||
|
"modal.confirm": "Confirm",
|
||||||
"renderer.components.autoSaveIndicator.saved": "Saved",
|
"renderer.components.autoSaveIndicator.saved": "Saved",
|
||||||
"renderer.components.autoSaveIndicator.saving": "Saving...",
|
"renderer.components.autoSaveIndicator.saving": "Saving...",
|
||||||
"renderer.components.configureServer.cardtitle": "Enter your server details",
|
"renderer.components.configureServer.cardtitle": "Enter your server details",
|
||||||
@@ -200,8 +202,8 @@
|
|||||||
"renderer.components.newServerModal.warning.notMattermost": "The server URL provided does not appear to point to a valid Mattermost server. Please verify the URL and check your connection.",
|
"renderer.components.newServerModal.warning.notMattermost": "The server URL provided does not appear to point to a valid Mattermost server. Please verify the URL and check your connection.",
|
||||||
"renderer.components.newServerModal.warning.urlNotMatched": "The server URL does not match the configured Site URL on your Mattermost server. Server version: {serverVersion}",
|
"renderer.components.newServerModal.warning.urlNotMatched": "The server URL does not match the configured Site URL on your Mattermost server. Server version: {serverVersion}",
|
||||||
"renderer.components.newServerModal.warning.urlUpdated": "The server URL provided has been updated to match the configured Site URL on your Mattermost server. Server version: {serverVersion}",
|
"renderer.components.newServerModal.warning.urlUpdated": "The server URL provided has been updated to match the configured Site URL on your Mattermost server. Server version: {serverVersion}",
|
||||||
"renderer.components.removeServerModal.body": "This will remove the server from your Desktop App but will not delete any of its data - you can add the server back to the app at any time.",
|
"renderer.components.removeServerModal.body": "This will remove the server from your Desktop App but will not delete any of its data - you can add the server back at any time.",
|
||||||
"renderer.components.removeServerModal.confirm": "Confirm you wish to remove the {serverName} server?",
|
"renderer.components.removeServerModal.confirm": "Are you sure you wish to remove the server?",
|
||||||
"renderer.components.removeServerModal.title": "Remove Server",
|
"renderer.components.removeServerModal.title": "Remove Server",
|
||||||
"renderer.components.saveButton.save": "Save",
|
"renderer.components.saveButton.save": "Save",
|
||||||
"renderer.components.saveButton.saving": "Saving",
|
"renderer.components.saveButton.saving": "Saving",
|
||||||
@@ -266,12 +268,12 @@
|
|||||||
"renderer.components.showCertificateModal.algorithm": "Algorithm",
|
"renderer.components.showCertificateModal.algorithm": "Algorithm",
|
||||||
"renderer.components.showCertificateModal.commonName": "Common Name",
|
"renderer.components.showCertificateModal.commonName": "Common Name",
|
||||||
"renderer.components.showCertificateModal.issuerName": "Issuer Name",
|
"renderer.components.showCertificateModal.issuerName": "Issuer Name",
|
||||||
"renderer.components.showCertificateModal.noCertSelected": "No certificate Selected",
|
|
||||||
"renderer.components.showCertificateModal.notValidAfter": "Not Valid After",
|
"renderer.components.showCertificateModal.notValidAfter": "Not Valid After",
|
||||||
"renderer.components.showCertificateModal.notValidBefore": "Not Valid Before",
|
"renderer.components.showCertificateModal.notValidBefore": "Not Valid Before",
|
||||||
"renderer.components.showCertificateModal.publicKeyInfo": "Public Key Info",
|
"renderer.components.showCertificateModal.publicKeyInfo": "Public Key Info",
|
||||||
"renderer.components.showCertificateModal.serialNumber": "Serial Number",
|
"renderer.components.showCertificateModal.serialNumber": "Serial Number",
|
||||||
"renderer.components.showCertificateModal.subjectName": "Subject Name",
|
"renderer.components.showCertificateModal.subjectName": "Subject Name",
|
||||||
|
"renderer.components.showCertificateModal.title": "Certificate information",
|
||||||
"renderer.components.welcomeScreen.button.getStarted": "Get Started",
|
"renderer.components.welcomeScreen.button.getStarted": "Get Started",
|
||||||
"renderer.components.welcomeScreen.slides.calls.subtitle": "When typing isn’t fast enough, seamlessly move from chat to audio calls and screenshare without switching tools.",
|
"renderer.components.welcomeScreen.slides.calls.subtitle": "When typing isn’t fast enough, seamlessly move from chat to audio calls and screenshare without switching tools.",
|
||||||
"renderer.components.welcomeScreen.slides.calls.title": "Start secure calls instantly",
|
"renderer.components.welcomeScreen.slides.calls.title": "Start secure calls instantly",
|
||||||
|
@@ -204,11 +204,11 @@ export class ServerViewState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modalPromise = ModalManager.addModal<string, boolean>(
|
const modalPromise = ModalManager.addModal<null, boolean>(
|
||||||
'removeServer',
|
'removeServer',
|
||||||
'mattermost-desktop://renderer/removeServer.html',
|
'mattermost-desktop://renderer/removeServer.html',
|
||||||
getLocalPreload('internalAPI.js'),
|
getLocalPreload('internalAPI.js'),
|
||||||
server.name,
|
null,
|
||||||
mainWindow,
|
mainWindow,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -3,20 +3,23 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Button, Modal} from 'react-bootstrap';
|
|
||||||
|
import {Modal} from './Modal';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
body: React.ReactNode;
|
body: React.ReactNode;
|
||||||
acceptLabel: string;
|
acceptLabel: string;
|
||||||
cancelLabel: string;
|
cancelLabel: string;
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
onAccept: React.MouseEventHandler<HTMLButtonElement>;
|
onAccept: () => void;
|
||||||
onCancel: React.MouseEventHandler<HTMLButtonElement>;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DestructiveConfirmationModal(props: Props) {
|
export default function DestructiveConfirmationModal(props: Props) {
|
||||||
const {
|
const {
|
||||||
|
id,
|
||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
acceptLabel,
|
acceptLabel,
|
||||||
@@ -27,23 +30,18 @@ export default function DestructiveConfirmationModal(props: Props) {
|
|||||||
...rest} = props;
|
...rest} = props;
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
onHide={onHide}
|
id={id}
|
||||||
|
onExited={onHide}
|
||||||
|
isDeleteModal={true}
|
||||||
|
modalHeaderText={title}
|
||||||
|
handleCancel={onCancel}
|
||||||
|
handleConfirm={onAccept}
|
||||||
|
confirmButtonText={acceptLabel}
|
||||||
|
cancelButtonText={cancelLabel}
|
||||||
|
confirmButtonClassName='btn-danger'
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<Modal.Header closeButton={true}>
|
|
||||||
<Modal.Title>{title}</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
{body}
|
{body}
|
||||||
<Modal.Footer>
|
|
||||||
<Button
|
|
||||||
variant='link'
|
|
||||||
onClick={onCancel}
|
|
||||||
>{cancelLabel}</Button>
|
|
||||||
<Button
|
|
||||||
variant='danger'
|
|
||||||
onClick={onAccept}
|
|
||||||
>{acceptLabel}</Button>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
274
src/renderer/components/Modal.tsx
Normal file
274
src/renderer/components/Modal.tsx
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React, {useState, useRef, useEffect, useCallback} from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import 'renderer/css/components/Modal.scss';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
id: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
onExited: () => void;
|
||||||
|
|
||||||
|
className?: string;
|
||||||
|
modalHeaderText?: React.ReactNode;
|
||||||
|
modalSubheaderText?: React.ReactNode;
|
||||||
|
show?: boolean;
|
||||||
|
handleCancel?: () => void;
|
||||||
|
handleConfirm?: () => void;
|
||||||
|
handleEnterKeyPress?: () => void;
|
||||||
|
handleKeydown?: (event?: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||||
|
confirmButtonText?: React.ReactNode;
|
||||||
|
confirmButtonClassName?: string;
|
||||||
|
cancelButtonText?: React.ReactNode;
|
||||||
|
cancelButtonClassName?: string;
|
||||||
|
isConfirmDisabled?: boolean;
|
||||||
|
isDeleteModal?: boolean;
|
||||||
|
autoCloseOnCancelButton?: boolean;
|
||||||
|
autoCloseOnConfirmButton?: boolean;
|
||||||
|
ariaLabel?: string;
|
||||||
|
errorText?: string | React.ReactNode;
|
||||||
|
tabIndex?: number;
|
||||||
|
autoFocusConfirmButton?: boolean;
|
||||||
|
headerInput?: React.ReactNode;
|
||||||
|
bodyPadding?: boolean;
|
||||||
|
bodyDivider?: boolean;
|
||||||
|
footerContent?: React.ReactNode;
|
||||||
|
footerDivider?: boolean;
|
||||||
|
appendedContent?: React.ReactNode;
|
||||||
|
headerButton?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Modal: React.FC<Props> = ({
|
||||||
|
id = 'modal',
|
||||||
|
children,
|
||||||
|
onExited,
|
||||||
|
className,
|
||||||
|
modalHeaderText,
|
||||||
|
modalSubheaderText,
|
||||||
|
show = true,
|
||||||
|
handleCancel,
|
||||||
|
handleConfirm,
|
||||||
|
handleEnterKeyPress,
|
||||||
|
handleKeydown,
|
||||||
|
confirmButtonText,
|
||||||
|
confirmButtonClassName,
|
||||||
|
cancelButtonText,
|
||||||
|
cancelButtonClassName,
|
||||||
|
isConfirmDisabled,
|
||||||
|
isDeleteModal,
|
||||||
|
autoCloseOnCancelButton = true,
|
||||||
|
autoCloseOnConfirmButton = true,
|
||||||
|
ariaLabel,
|
||||||
|
errorText,
|
||||||
|
tabIndex,
|
||||||
|
autoFocusConfirmButton,
|
||||||
|
headerInput,
|
||||||
|
bodyPadding = true,
|
||||||
|
bodyDivider,
|
||||||
|
footerContent,
|
||||||
|
footerDivider,
|
||||||
|
appendedContent,
|
||||||
|
headerButton,
|
||||||
|
}) => {
|
||||||
|
const [showState, setShowState] = useState<boolean>();
|
||||||
|
const backdropRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShowState(show ?? true);
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
const onHide = () => {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
backdropRef.current?.addEventListener('transitionend', () => {
|
||||||
|
resolve();
|
||||||
|
}, {once: true});
|
||||||
|
setShowState(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = useCallback(async () => {
|
||||||
|
await onHide();
|
||||||
|
onExited();
|
||||||
|
}, [onExited]);
|
||||||
|
|
||||||
|
const handleCancelClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (autoCloseOnCancelButton) {
|
||||||
|
await onHide();
|
||||||
|
}
|
||||||
|
handleCancel?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (autoCloseOnConfirmButton) {
|
||||||
|
await onHide();
|
||||||
|
}
|
||||||
|
handleConfirm?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEnterKeyDown = async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
if (event.nativeEvent.isComposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (autoCloseOnConfirmButton) {
|
||||||
|
await onHide();
|
||||||
|
}
|
||||||
|
if (handleEnterKeyPress) {
|
||||||
|
handleEnterKeyPress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleKeydown?.(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
let confirmButton;
|
||||||
|
if (handleConfirm) {
|
||||||
|
const isConfirmOrDeleteClassName = isDeleteModal ? 'delete' : 'confirm';
|
||||||
|
let confirmButtonTextNode: React.ReactNode = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='modal.confirm'
|
||||||
|
defaultMessage='Confirm'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (confirmButtonText) {
|
||||||
|
confirmButtonTextNode = confirmButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmButton = (
|
||||||
|
<button
|
||||||
|
id={`${id}_confirm`}
|
||||||
|
autoFocus={autoFocusConfirmButton}
|
||||||
|
type='submit'
|
||||||
|
className={classNames('Modal__button btn btn-primary', isConfirmOrDeleteClassName, confirmButtonClassName, {
|
||||||
|
disabled: isConfirmDisabled,
|
||||||
|
})}
|
||||||
|
onClick={handleConfirmClick}
|
||||||
|
disabled={isConfirmDisabled}
|
||||||
|
>
|
||||||
|
{confirmButtonTextNode}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelButton;
|
||||||
|
if (handleCancel) {
|
||||||
|
let cancelButtonTextNode: React.ReactNode = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='modal.cancel'
|
||||||
|
defaultMessage='Cancel'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (cancelButtonText) {
|
||||||
|
cancelButtonTextNode = cancelButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelButton = (
|
||||||
|
<button
|
||||||
|
id={`${id}_cancel`}
|
||||||
|
type='button'
|
||||||
|
className={classNames('Modal__button btn btn-tertiary', cancelButtonClassName)}
|
||||||
|
onClick={handleCancelClick}
|
||||||
|
>
|
||||||
|
{cancelButtonTextNode}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerText = modalHeaderText && (
|
||||||
|
<div className='Modal__header'>
|
||||||
|
<h1
|
||||||
|
id='modalLabel'
|
||||||
|
className='Modal_title'
|
||||||
|
>
|
||||||
|
{modalHeaderText}
|
||||||
|
</h1>
|
||||||
|
{headerButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
ref={backdropRef}
|
||||||
|
className={classNames('Modal_backdrop fade', {show: showState})}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
role='dialog'
|
||||||
|
className={classNames('Modal fade', {show: showState})}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id={id}
|
||||||
|
role='dialog'
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
aria-labelledby={ariaLabel ? undefined : 'modalLabel'}
|
||||||
|
className={classNames(
|
||||||
|
'Modal_dialog Modal__compassDesign',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
onClick={useCallback((event) => event.stopPropagation(), [])}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onKeyDown={onEnterKeyDown}
|
||||||
|
tabIndex={tabIndex || 0}
|
||||||
|
className='Modal_content'
|
||||||
|
>
|
||||||
|
<div className='Modal_header'>
|
||||||
|
<div className='Modal__header__text_container'>
|
||||||
|
{headerText}
|
||||||
|
{headerInput}
|
||||||
|
{
|
||||||
|
modalSubheaderText &&
|
||||||
|
<div className='Modal_subheading-container'>
|
||||||
|
<p
|
||||||
|
id='Modal_subHeading'
|
||||||
|
className='Modal_subheading'
|
||||||
|
>
|
||||||
|
{modalSubheaderText}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='close'
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<span aria-hidden='true'>{'×'}</span>
|
||||||
|
<span className='sr-only'>{'Close'}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={classNames('Modal_body', {divider: bodyDivider})}>
|
||||||
|
{errorText && (
|
||||||
|
<div className='Modal_error'>
|
||||||
|
<i className='icon icon-alert-outline'/>
|
||||||
|
<span>{errorText}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={classNames('Modal__body', {padding: bodyPadding})}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(cancelButton || confirmButton || footerContent) && (
|
||||||
|
<div className={classNames('Modal_footer', {divider: footerDivider})}>
|
||||||
|
{(cancelButton || confirmButton) ? (
|
||||||
|
<>
|
||||||
|
{cancelButton}
|
||||||
|
{confirmButton}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
footerContent
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{Boolean(appendedContent) && appendedContent}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@@ -3,7 +3,6 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Modal, Button, FormGroup, FormControl, FormLabel, FormText, Spinner} from 'react-bootstrap';
|
|
||||||
import type {IntlShape} from 'react-intl';
|
import type {IntlShape} from 'react-intl';
|
||||||
import {FormattedMessage, injectIntl} from 'react-intl';
|
import {FormattedMessage, injectIntl} from 'react-intl';
|
||||||
|
|
||||||
@@ -14,20 +13,22 @@ import type {UniqueServer} from 'types/config';
|
|||||||
import type {Permissions} from 'types/permissions';
|
import type {Permissions} from 'types/permissions';
|
||||||
import type {URLValidationResult} from 'types/server';
|
import type {URLValidationResult} from 'types/server';
|
||||||
|
|
||||||
|
import Input, {SIZE, STATUS} from './Input';
|
||||||
|
import {Modal} from './Modal';
|
||||||
|
|
||||||
import 'renderer/css/components/NewServerModal.scss';
|
import 'renderer/css/components/NewServerModal.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClose?: () => void;
|
onClose: () => void;
|
||||||
onSave?: (server: UniqueServer, permissions?: Permissions) => void;
|
onSave?: (server: UniqueServer, permissions?: Permissions) => void;
|
||||||
server?: UniqueServer;
|
server?: UniqueServer;
|
||||||
permissions?: Permissions;
|
permissions?: Permissions;
|
||||||
editMode?: boolean;
|
editMode?: boolean;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
restoreFocus?: boolean;
|
|
||||||
currentOrder?: number;
|
currentOrder?: number;
|
||||||
setInputRef?: (inputRef: HTMLInputElement) => void;
|
|
||||||
intl: IntlShape;
|
intl: IntlShape;
|
||||||
prefillURL?: string;
|
prefillURL?: string;
|
||||||
|
unremoveable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@@ -49,10 +50,6 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
validationTimeout?: NodeJS.Timeout;
|
validationTimeout?: NodeJS.Timeout;
|
||||||
mounted: boolean;
|
mounted: boolean;
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
restoreFocus: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@@ -159,19 +156,13 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
getServerURLMessage = () => {
|
getServerURLMessage = () => {
|
||||||
if (this.state.validationStarted) {
|
if (this.state.validationStarted) {
|
||||||
return (
|
return {
|
||||||
<div>
|
type: STATUS.INFO,
|
||||||
<Spinner
|
value: this.props.intl.formatMessage({
|
||||||
className='NewServerModal-validationSpinner'
|
id: 'renderer.components.newServerModal.validating',
|
||||||
animation='border'
|
defaultMessage: 'Validating...',
|
||||||
size='sm'
|
}),
|
||||||
/>
|
};
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.newServerModal.validating'
|
|
||||||
defaultMessage='Validating...'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.validationResult) {
|
if (!this.state.validationResult) {
|
||||||
@@ -180,114 +171,78 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
switch (this.state.validationResult?.status) {
|
switch (this.state.validationResult?.status) {
|
||||||
case URLValidationStatus.Missing:
|
case URLValidationStatus.Missing:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.ERROR,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='error'
|
id: 'renderer.components.newServerModal.error.urlRequired',
|
||||||
>
|
defaultMessage: 'URL is required.',
|
||||||
<i className='icon-close-circle'/>
|
}),
|
||||||
<FormattedMessage
|
};
|
||||||
id='renderer.components.newServerModal.error.urlRequired'
|
|
||||||
defaultMessage='URL is required.'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case URLValidationStatus.Invalid:
|
case URLValidationStatus.Invalid:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.ERROR,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='error'
|
id: 'renderer.components.newServerModal.error.urlIncorrectFormatting',
|
||||||
>
|
defaultMessage: 'URL is not formatted correctly.',
|
||||||
<i className='icon-close-circle'/>
|
}),
|
||||||
<FormattedMessage
|
};
|
||||||
id='renderer.components.newServerModal.error.urlIncorrectFormatting'
|
|
||||||
defaultMessage='URL is not formatted correctly.'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case URLValidationStatus.URLExists:
|
case URLValidationStatus.URLExists:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.WARNING,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='warning'
|
id: 'renderer.components.newServerModal.error.serverUrlExists',
|
||||||
>
|
defaultMessage: 'A server named {serverName} with the same Site URL already exists.',
|
||||||
<i className='icon-alert-outline'/>
|
}, {
|
||||||
<FormattedMessage
|
serverName: this.state.validationResult.existingServerName,
|
||||||
id='renderer.components.newServerModal.error.serverUrlExists'
|
}),
|
||||||
defaultMessage='A server named {serverName} with the same Site URL already exists.'
|
};
|
||||||
values={{serverName: this.state.validationResult.existingServerName}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case URLValidationStatus.Insecure:
|
case URLValidationStatus.Insecure:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.WARNING,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='warning'
|
id: 'renderer.components.newServerModal.warning.insecure',
|
||||||
>
|
defaultMessage: 'Your server URL is potentially insecure. For best results, use a URL with the HTTPS protocol.',
|
||||||
<i className='icon-alert-outline'/>
|
}),
|
||||||
<FormattedMessage
|
};
|
||||||
id='renderer.components.newServerModal.warning.insecure'
|
|
||||||
defaultMessage='Your server URL is potentially insecure. For best results, use a URL with the HTTPS protocol.'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case URLValidationStatus.NotMattermost:
|
case URLValidationStatus.NotMattermost:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.WARNING,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='warning'
|
id: 'renderer.components.newServerModal.warning.notMattermost',
|
||||||
>
|
defaultMessage: 'The server URL provided does not appear to point to a valid Mattermost server. Please verify the URL and check your connection.',
|
||||||
<i className='icon-alert-outline'/>
|
}),
|
||||||
<FormattedMessage
|
};
|
||||||
id='renderer.components.newServerModal.warning.notMattermost'
|
|
||||||
defaultMessage='The server URL provided does not appear to point to a valid Mattermost server. Please verify the URL and check your connection.'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case URLValidationStatus.URLNotMatched:
|
case URLValidationStatus.URLNotMatched:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.WARNING,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='warning'
|
id: 'renderer.components.newServerModal.warning.urlNotMatched',
|
||||||
>
|
defaultMessage: 'The server URL does not match the configured Site URL on your Mattermost server. Server version: {serverVersion}',
|
||||||
<i className='icon-alert-outline'/>
|
}, {
|
||||||
<FormattedMessage
|
serverVersion: this.state.validationResult.serverVersion,
|
||||||
id='renderer.components.newServerModal.warning.urlNotMatched'
|
}),
|
||||||
defaultMessage='The server URL does not match the configured Site URL on your Mattermost server. Server version: {serverVersion}'
|
};
|
||||||
values={{serverVersion: this.state.validationResult.serverVersion}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case URLValidationStatus.URLUpdated:
|
case URLValidationStatus.URLUpdated:
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.INFO,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='info'
|
id: 'renderer.components.newServerModal.warning.urlUpdated',
|
||||||
>
|
defaultMessage: 'The server URL provided has been updated to match the configured Site URL on your Mattermost server. Server version: {serverVersion}',
|
||||||
<i className='icon-information-outline'/>
|
}, {
|
||||||
<FormattedMessage
|
serverVersion: this.state.validationResult.serverVersion,
|
||||||
id='renderer.components.newServerModal.warning.urlUpdated'
|
}),
|
||||||
defaultMessage='The server URL provided has been updated to match the configured Site URL on your Mattermost server. Server version: {serverVersion}'
|
};
|
||||||
values={{serverVersion: this.state.validationResult.serverVersion}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.SUCCESS,
|
||||||
id='urlValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='success'
|
id: 'renderer.components.newServerModal.success.ok',
|
||||||
>
|
defaultMessage: 'Server URL is valid. Server version: {serverVersion}',
|
||||||
<i className='icon-check-circle'/>
|
}, {
|
||||||
<FormattedMessage
|
serverVersion: this.state.validationResult.serverVersion,
|
||||||
id='renderer.components.newServerModal.success.ok'
|
}),
|
||||||
defaultMessage='Server URL is valid. Server version: {serverVersion}'
|
};
|
||||||
values={{serverVersion: this.state.validationResult.serverVersion}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
openNotificationPrefs = () => {
|
openNotificationPrefs = () => {
|
||||||
@@ -303,19 +258,18 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getServerNameMessage = () => {
|
getServerNameMessage = () => {
|
||||||
|
if (!this.state.validationResult) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.state.serverName.length) {
|
if (!this.state.serverName.length) {
|
||||||
return (
|
return {
|
||||||
<div
|
type: STATUS.ERROR,
|
||||||
id='nameValidation'
|
value: this.props.intl.formatMessage({
|
||||||
className='error'
|
id: 'renderer.components.newServerModal.error.nameRequired',
|
||||||
>
|
defaultMessage: 'Name is required.',
|
||||||
<i className='icon-close-circle'/>
|
}),
|
||||||
<FormattedMessage
|
};
|
||||||
id='renderer.components.newServerModal.error.nameRequired'
|
|
||||||
defaultMessage='Name is required.'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -401,114 +355,71 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
bsClass='modal'
|
|
||||||
className='NewServerModal'
|
|
||||||
show={this.props.show}
|
show={this.props.show}
|
||||||
id='newServerModal'
|
id='newServerModal'
|
||||||
enforceFocus={true}
|
className='NewServerModal'
|
||||||
onEntered={() => this.serverUrlInputRef?.focus()}
|
onExited={this.props.unremoveable ? () => {} : this.props.onClose}
|
||||||
onHide={this.props.onClose}
|
modalHeaderText={this.getModalTitle()}
|
||||||
restoreFocus={this.props.restoreFocus}
|
confirmButtonText={this.getSaveButtonLabel()}
|
||||||
onKeyDown={(e: React.KeyboardEvent) => {
|
handleConfirm={this.save}
|
||||||
switch (e.key) {
|
isConfirmDisabled={!this.state.serverName.length || !this.state.validationResult || this.isServerURLErrored()}
|
||||||
case 'Enter':
|
handleCancel={this.props.onClose}
|
||||||
this.save();
|
bodyDivider={true}
|
||||||
|
footerDivider={true}
|
||||||
// The add button from behind this might still be focused
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
break;
|
|
||||||
case 'Escape':
|
|
||||||
this.props.onClose?.();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Modal.Header>
|
<>
|
||||||
<Modal.Title>{this.getModalTitle()}</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
|
|
||||||
<Modal.Body>
|
|
||||||
{!(this.props.editMode && this.props.server?.isPredefined) &&
|
{!(this.props.editMode && this.props.server?.isPredefined) &&
|
||||||
<>
|
<>
|
||||||
<form>
|
<Input
|
||||||
<FormGroup>
|
autoFocus={true}
|
||||||
<FormLabel>
|
id='serverUrlInput'
|
||||||
<FormattedMessage
|
name='url'
|
||||||
id='renderer.components.newServerModal.serverURL'
|
type='text'
|
||||||
defaultMessage='Server URL'
|
inputSize={SIZE.LARGE}
|
||||||
/>
|
value={this.state.serverUrl}
|
||||||
</FormLabel>
|
onChange={this.handleServerUrlChange}
|
||||||
<FormControl
|
customMessage={this.getServerURLMessage() ?? ({
|
||||||
id='serverUrlInput'
|
type: STATUS.INFO,
|
||||||
type='text'
|
value: this.props.intl.formatMessage({
|
||||||
value={this.state.serverUrl}
|
id: 'renderer.components.newServerModal.serverURL.description',
|
||||||
placeholder='https://example.com'
|
defaultMessage: 'The URL of your Mattermost server. Must start with http:// or https://.',
|
||||||
onChange={this.handleServerUrlChange}
|
}),
|
||||||
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
|
})}
|
||||||
e.stopPropagation();
|
placeholder={this.props.intl.formatMessage({
|
||||||
}}
|
id: 'renderer.components.newServerModal.serverURL',
|
||||||
ref={(ref: HTMLInputElement) => {
|
defaultMessage: 'Server URL',
|
||||||
this.serverUrlInputRef = ref;
|
})}
|
||||||
if (this.props.setInputRef) {
|
/>
|
||||||
this.props.setInputRef(ref);
|
<Input
|
||||||
}
|
id='serverNameInput'
|
||||||
}}
|
name='name'
|
||||||
isInvalid={this.isServerURLErrored()}
|
type='text'
|
||||||
autoFocus={true}
|
inputSize={SIZE.LARGE}
|
||||||
/>
|
value={this.state.serverName}
|
||||||
<FormControl.Feedback/>
|
onChange={this.handleServerNameChange}
|
||||||
<FormText>
|
customMessage={this.getServerNameMessage() ?? ({
|
||||||
<FormattedMessage
|
type: STATUS.INFO,
|
||||||
id='renderer.components.newServerModal.serverURL.description'
|
value: this.props.intl.formatMessage({
|
||||||
defaultMessage='The URL of your Mattermost server. Must start with http:// or https://.'
|
id: 'renderer.components.newServerModal.serverDisplayName.description',
|
||||||
/>
|
defaultMessage: 'The name of the server displayed on your desktop app tab bar.',
|
||||||
</FormText>
|
}),
|
||||||
</FormGroup>
|
})}
|
||||||
<FormGroup className='NewServerModal-noBottomSpace'>
|
placeholder={this.props.intl.formatMessage({
|
||||||
<FormLabel>
|
id: 'renderer.components.newServerModal.serverDisplayName',
|
||||||
<FormattedMessage
|
defaultMessage: 'Server display name',
|
||||||
id='renderer.components.newServerModal.serverDisplayName'
|
})}
|
||||||
defaultMessage='Server Display Name'
|
/>
|
||||||
/>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl
|
|
||||||
id='serverNameInput'
|
|
||||||
type='text'
|
|
||||||
value={this.state.serverName}
|
|
||||||
placeholder={this.props.intl.formatMessage({id: 'renderer.components.newServerModal.serverDisplayName', defaultMessage: 'Server Display Name'})}
|
|
||||||
onChange={this.handleServerNameChange}
|
|
||||||
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
isInvalid={!this.state.serverName.length}
|
|
||||||
/>
|
|
||||||
<FormControl.Feedback/>
|
|
||||||
<FormText className='NewServerModal-noBottomSpace'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.newServerModal.serverDisplayName.description'
|
|
||||||
defaultMessage='The name of the server displayed on your desktop app tab bar.'
|
|
||||||
/>
|
|
||||||
</FormText>
|
|
||||||
</FormGroup>
|
|
||||||
</form>
|
|
||||||
<div
|
|
||||||
className='NewServerModal-validation'
|
|
||||||
>
|
|
||||||
{this.getServerNameMessage()}
|
|
||||||
{this.getServerURLMessage()}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{this.props.editMode &&
|
{this.props.editMode &&
|
||||||
<>
|
<>
|
||||||
<hr/>
|
<hr/>
|
||||||
<h5>
|
<h3 className='NewServerModal__permissions__title'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='renderer.components.newServerModal.permissions.title'
|
id='renderer.components.newServerModal.permissions.title'
|
||||||
defaultMessage='Permissions'
|
defaultMessage='Permissions'
|
||||||
/>
|
/>
|
||||||
</h5>
|
</h3>
|
||||||
<Toggle
|
<Toggle
|
||||||
isChecked={this.state.permissions.media?.allowed}
|
isChecked={this.state.permissions.media?.allowed}
|
||||||
onChange={this.handleChangePermission('media')}
|
onChange={this.handleChangePermission('media')}
|
||||||
@@ -520,7 +431,7 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
defaultMessage='Microphone and Camera'
|
defaultMessage='Microphone and Camera'
|
||||||
/>
|
/>
|
||||||
{this.state.cameraDisabled &&
|
{this.state.cameraDisabled &&
|
||||||
<FormText>
|
<small className='NewServerModal__toggle__description'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='renderer.components.newServerModal.permissions.microphoneAndCamera.windowsCameraPermissions'
|
id='renderer.components.newServerModal.permissions.microphoneAndCamera.windowsCameraPermissions'
|
||||||
defaultMessage='Camera is disabled in Windows Settings. Click <link>here</link> to open the Camera Settings.'
|
defaultMessage='Camera is disabled in Windows Settings. Click <link>here</link> to open the Camera Settings.'
|
||||||
@@ -535,10 +446,10 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormText>
|
</small>
|
||||||
}
|
}
|
||||||
{this.state.microphoneDisabled &&
|
{this.state.microphoneDisabled &&
|
||||||
<FormText>
|
<small className='NewServerModal__toggle__description'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='renderer.components.newServerModal.permissions.microphoneAndCamera.windowsMicrophoneaPermissions'
|
id='renderer.components.newServerModal.permissions.microphoneAndCamera.windowsMicrophoneaPermissions'
|
||||||
defaultMessage='Microphone is disabled in Windows Settings. Click <link>here</link> to open the Microphone Settings.'
|
defaultMessage='Microphone is disabled in Windows Settings. Click <link>here</link> to open the Microphone Settings.'
|
||||||
@@ -553,7 +464,7 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormText>
|
</small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
@@ -568,22 +479,22 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
defaultMessage='Notifications'
|
defaultMessage='Notifications'
|
||||||
/>
|
/>
|
||||||
{window.process.platform === 'darwin' &&
|
{window.process.platform === 'darwin' &&
|
||||||
<FormText>
|
<small className='NewServerModal__toggle__description'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='renderer.components.newServerModal.permissions.notifications.mac'
|
id='renderer.components.newServerModal.permissions.notifications.mac'
|
||||||
defaultMessage='You may also need to enable notifications in macOS for Mattermost. Click <link>here</link> to open the System Preferences.'
|
defaultMessage='You may also need to enable notifications in macOS for Mattermost. Click <link>here</link> to open the System Preferences.'
|
||||||
values={notificationValues}
|
values={notificationValues}
|
||||||
/>
|
/>
|
||||||
</FormText>
|
</small>
|
||||||
}
|
}
|
||||||
{window.process.platform === 'win32' &&
|
{window.process.platform === 'win32' &&
|
||||||
<FormText>
|
<small className='NewServerModal__toggle__description'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='renderer.components.newServerModal.permissions.notifications.windows'
|
id='renderer.components.newServerModal.permissions.notifications.windows'
|
||||||
defaultMessage='You may also need to enable notifications in Windows for Mattermost. Click <link>here</link> to open the Notification Settings.'
|
defaultMessage='You may also need to enable notifications in Windows for Mattermost. Click <link>here</link> to open the Notification Settings.'
|
||||||
values={notificationValues}
|
values={notificationValues}
|
||||||
/>
|
/>
|
||||||
</FormText>
|
</small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
@@ -609,33 +520,7 @@ class NewServerModal extends React.PureComponent<Props, State> {
|
|||||||
</Toggle>
|
</Toggle>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</Modal.Body>
|
</>
|
||||||
|
|
||||||
<Modal.Footer>
|
|
||||||
{this.props.onClose &&
|
|
||||||
<Button
|
|
||||||
id='cancelNewServerModal'
|
|
||||||
onClick={this.props.onClose}
|
|
||||||
variant='link'
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.cancel'
|
|
||||||
defaultMessage='Cancel'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
{this.props.onSave &&
|
|
||||||
<Button
|
|
||||||
id='saveNewServerModal'
|
|
||||||
onClick={this.save}
|
|
||||||
disabled={!this.state.serverName.length || !this.state.validationResult || this.isServerURLErrored()}
|
|
||||||
variant='primary'
|
|
||||||
>
|
|
||||||
{this.getSaveButtonLabel()}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
</Modal.Footer>
|
|
||||||
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -10,36 +10,32 @@ import DestructiveConfirmationModal from './DestructiveConfirmModal';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
serverName: string;
|
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
onAccept: React.MouseEventHandler<HTMLButtonElement>;
|
onAccept: () => void;
|
||||||
onCancel: React.MouseEventHandler<HTMLButtonElement>;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function RemoveServerModal(props: Props) {
|
function RemoveServerModal(props: Props) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {serverName, ...rest} = props;
|
const {...rest} = props;
|
||||||
return (
|
return (
|
||||||
<DestructiveConfirmationModal
|
<DestructiveConfirmationModal
|
||||||
{...rest}
|
{...rest}
|
||||||
|
id='removeServerModal'
|
||||||
title={intl.formatMessage({id: 'renderer.components.removeServerModal.title', defaultMessage: 'Remove Server'})}
|
title={intl.formatMessage({id: 'renderer.components.removeServerModal.title', defaultMessage: 'Remove Server'})}
|
||||||
acceptLabel={intl.formatMessage({id: 'label.remove', defaultMessage: 'Remove'})}
|
acceptLabel={intl.formatMessage({id: 'label.remove', defaultMessage: 'Remove'})}
|
||||||
cancelLabel={intl.formatMessage({id: 'label.cancel', defaultMessage: 'Cancel'})}
|
cancelLabel={intl.formatMessage({id: 'label.cancel', defaultMessage: 'Cancel'})}
|
||||||
body={(
|
body={(
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<p>
|
<FormattedMessage
|
||||||
<FormattedMessage
|
id='renderer.components.removeServerModal.body'
|
||||||
id='renderer.components.removeServerModal.body'
|
defaultMessage='This will remove the server from your Desktop App but will not delete any of its data - you can add the server back at any time.'
|
||||||
defaultMessage='This will remove the server from your Desktop App but will not delete any of its data - you can add the server back to the app at any time.'
|
/>
|
||||||
/>
|
<br/><br/>
|
||||||
</p>
|
<FormattedMessage
|
||||||
<p>
|
id='renderer.components.removeServerModal.confirm'
|
||||||
<FormattedMessage
|
defaultMessage='Are you sure you wish to remove the server?'
|
||||||
id='renderer.components.removeServerModal.confirm'
|
/>
|
||||||
defaultMessage='Confirm you wish to remove the {serverName} server?'
|
|
||||||
values={{serverName}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
import type {Certificate} from 'electron/renderer';
|
import type {Certificate} from 'electron/renderer';
|
||||||
import React, {Fragment} from 'react';
|
import React, {Fragment} from 'react';
|
||||||
import {Modal, Button, Row, Col} from 'react-bootstrap';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import {Modal} from 'renderer/components/Modal';
|
||||||
|
import IntlProvider from 'renderer/intl_provider';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
certificate: Certificate;
|
certificate: Certificate;
|
||||||
onOk: () => void;
|
onOk: () => void;
|
||||||
@@ -32,7 +34,7 @@ export default class ShowCertificateModal extends React.PureComponent<Props, Sta
|
|||||||
const certificateSection = (descriptor: React.ReactNode) => {
|
const certificateSection = (descriptor: React.ReactNode) => {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<dt className={'certificate-key'}>{descriptor}</dt>
|
<dt className={'certificate-key'}><strong>{descriptor}</strong></dt>
|
||||||
<dd className={'certificate-section'}><span/></dd>
|
<dd className={'certificate-section'}><span/></dd>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
@@ -41,29 +43,12 @@ export default class ShowCertificateModal extends React.PureComponent<Props, Sta
|
|||||||
const val = value ? `${value}` : <span/>;
|
const val = value ? `${value}` : <span/>;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<dt className={'certificate-key'}>{descriptor}</dt>
|
<dt className={'certificate-key'}><strong>{descriptor}</strong></dt>
|
||||||
<dd className={'certificate-value'}>{val}</dd>
|
<dd className={'certificate-value'}>{val}</dd>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.certificate === null) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
bsClass='modal'
|
|
||||||
className='show-certificate'
|
|
||||||
onHide={() => {}}
|
|
||||||
>
|
|
||||||
<Modal.Body>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.components.showCertificateModal.noCertSelected'
|
|
||||||
defaultMessage='No certificate Selected'
|
|
||||||
/>
|
|
||||||
</Modal.Body>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const utcSeconds = (date: number) => {
|
const utcSeconds = (date: number) => {
|
||||||
const d = new Date(0);
|
const d = new Date(0);
|
||||||
d.setUTCSeconds(date);
|
d.setUTCSeconds(date);
|
||||||
@@ -74,18 +59,27 @@ export default class ShowCertificateModal extends React.PureComponent<Props, Sta
|
|||||||
const creation = utcSeconds(this.state.certificate?.validStart || 0);
|
const creation = utcSeconds(this.state.certificate?.validStart || 0);
|
||||||
const dateDisplayOptions = {dateStyle: 'full' as const, timeStyle: 'full' as const};
|
const dateDisplayOptions = {dateStyle: 'full' as const, timeStyle: 'full' as const};
|
||||||
const dateLocale = 'en-US'; // TODO: Translate?
|
const dateLocale = 'en-US'; // TODO: Translate?
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<IntlProvider>
|
||||||
bsClass='modal'
|
<Modal
|
||||||
className='show-certificate'
|
id='showCertificateModal'
|
||||||
show={this.state.certificate !== null}
|
show={this.state.certificate !== null}
|
||||||
onHide={() => {}}
|
onExited={this.handleOk}
|
||||||
>
|
modalHeaderText={
|
||||||
<Modal.Header className={'no-border'}>
|
<FormattedMessage
|
||||||
<Modal.Title>{'Certificate information'}</Modal.Title>
|
id='renderer.components.showCertificateModal.title'
|
||||||
</Modal.Header>
|
defaultMessage='Certificate information'
|
||||||
<Modal.Body>
|
/>
|
||||||
<p className='details'>{'Details'}</p>
|
}
|
||||||
|
confirmButtonText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='label.close'
|
||||||
|
defaultMessage='Close'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
handleConfirm={this.handleOk}
|
||||||
|
>
|
||||||
<dl>
|
<dl>
|
||||||
{certificateSection(
|
{certificateSection(
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -154,26 +148,8 @@ export default class ShowCertificateModal extends React.PureComponent<Props, Sta
|
|||||||
this.state.certificate?.fingerprint.split('/')[0],
|
this.state.certificate?.fingerprint.split('/')[0],
|
||||||
)}
|
)}
|
||||||
</dl>
|
</dl>
|
||||||
</Modal.Body>
|
</Modal>
|
||||||
<Modal.Footer className={'no-border'}>
|
</IntlProvider>
|
||||||
<div className='container-fluid'>
|
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
<Button
|
|
||||||
variant='primary'
|
|
||||||
onClick={this.handleOk}
|
|
||||||
className={'primary'}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.close'
|
|
||||||
defaultMessage='Close'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
358
src/renderer/css/_buttons.scss
Normal file
358
src/renderer/css/_buttons.scss
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
@use "variables";
|
||||||
|
|
||||||
|
.style--none {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn--block {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-button {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
.unread-badge {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
margin: 0 0 0 40px;
|
||||||
|
background: #f74343;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
// Important is given to override bootstrap styles on different states
|
||||||
|
display: inline-flex;
|
||||||
|
height: 40px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
gap: 8px;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
&.btn-icon {
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
padding: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), var(--icon-opacity));
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
color: rgba(var(--center-channel-color-rgb), var(--icon-opacity-hover));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: rgba(var(--button-bg-rgb), 0.08);
|
||||||
|
color: rgba(var(--button-bg-rgb), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-xs {
|
||||||
|
width: 24px;
|
||||||
|
min-width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&.btn-compact {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14.4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-sm {
|
||||||
|
width: 32px;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&.btn-compact {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-lg {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&.btn-compact {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 31.2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.a11y--active) {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + .btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-xs {
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
font-size: 14.4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-sm {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
font-size: 14.4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-lg {
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-link {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(var(--button-bg-rgb), 1);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-primary {
|
||||||
|
position: relative;
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: rgb(var(--button-bg-rgb));
|
||||||
|
color: rgb(var(--button-color-rgb));
|
||||||
|
|
||||||
|
// These hover and active values are for things outside the app__body, the correct theme styles for the primary button are applied in utils.jsx
|
||||||
|
&:hover {
|
||||||
|
background-color: #1a51c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
background-color: #184ab6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&:disabled:hover,
|
||||||
|
&:disabled:active {
|
||||||
|
background: rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.32);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-secondary {
|
||||||
|
border: 1px solid rgb(var(--button-bg-rgb));
|
||||||
|
background: transparent;
|
||||||
|
color: rgb(var(--button-bg-rgb));
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
border-color: currentColor;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--error-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: currentColor;
|
||||||
|
background-color: rgba(var(--error-text-color-rgb), 0.08);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
border-color: currentColor;
|
||||||
|
background-color: rgba(var(--error-text-color-rgb), 0.16);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(var(--button-bg-rgb), 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: rgb(var(--button-bg-rgb), 0.16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-tertiary {
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.08);
|
||||||
|
color: rgb(var(--button-bg-rgb));
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(var(--button-bg-rgb), 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: rgb(var(--button-bg-rgb), 0.16);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&:disabled:hover,
|
||||||
|
&:disabled:active {
|
||||||
|
background: rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.32);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background-color: rgba(var(--error-text-color-rgb), 0.08);
|
||||||
|
color: var(--error-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(var(--error-text-color-rgb), 0.12);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
background-color: rgba(var(--error-text-color-rgb), 0.16);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-quaternary {
|
||||||
|
background: transparent;
|
||||||
|
color: rgb(var(--button-bg-rgb));
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: rgb(var(--button-bg-rgb), 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background: var(--error-text);
|
||||||
|
color: variables.$white;
|
||||||
|
|
||||||
|
.app__body & {
|
||||||
|
color: variables.$white;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
color: variables.$white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
color: variables.$white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-transparent {
|
||||||
|
padding: 7px 12px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-inactive {
|
||||||
|
border-color: transparent;
|
||||||
|
background: variables.$light-gray;
|
||||||
|
color: variables.$white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
margin-right: 3px;
|
||||||
|
|
||||||
|
&.margin-right {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.margin-left {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,19 @@
|
|||||||
--elevation-5: 0 12px 32px 0 rgba(0, 0, 0, 0.12);
|
--elevation-5: 0 12px 32px 0 rgba(0, 0, 0, 0.12);
|
||||||
--elevation-6: 0 20px 32px 0 rgba(0, 0, 0, 0.12);
|
--elevation-6: 0 20px 32px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
/* Corner Radius variables */
|
||||||
|
--radius-xs: 2px;
|
||||||
|
--radius-s: 4px;
|
||||||
|
--radius-m: 8px;
|
||||||
|
--radius-l: 12px;
|
||||||
|
--radius-xl: 16px;
|
||||||
|
--radius-full: 50%;
|
||||||
|
|
||||||
|
/* Border variables */
|
||||||
|
--border-default: solid 1px rgba(var(--center-channel-color-rgb), 0.12);
|
||||||
|
--border-light: solid 1px rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
--border-dark: solid 1px rgba(var(--center-channel-color-rgb), 0.16);
|
||||||
|
|
||||||
/* Denim - used for light mode */
|
/* Denim - used for light mode */
|
||||||
--away-indicator-rgb: 255, 188, 31;
|
--away-indicator-rgb: 255, 188, 31;
|
||||||
--button-bg-rgb: 28, 88, 217;
|
--button-bg-rgb: 28, 88, 217;
|
||||||
@@ -69,7 +82,7 @@
|
|||||||
--mention-highlight-link: #1b1d22;
|
--mention-highlight-link: #1b1d22;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LoadingScreen--darkMode, .ErrorView.darkMode {
|
.darkMode .Modal, .LoadingScreen--darkMode, .ErrorView.darkMode {
|
||||||
/* Onyx - used for dark mode*/
|
/* Onyx - used for dark mode*/
|
||||||
--away-indicator-rgb: 245, 171, 0;
|
--away-indicator-rgb: 245, 171, 0;
|
||||||
--button-bg-rgb: 74, 124, 232;
|
--button-bg-rgb: 74, 124, 232;
|
||||||
|
@@ -79,3 +79,15 @@
|
|||||||
transform: translate3d(4px, 0, 0);
|
transform: translate3d(4px, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin font-smoothing($value: antialiased) {
|
||||||
|
@if $value == antialiased {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
-webkit-font-smoothing: subpixel-antialiased;
|
||||||
|
-moz-osx-font-smoothing: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
148
src/renderer/css/_typography.scss
Normal file
148
src/renderer/css/_typography.scss
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
@use "mixins";
|
||||||
|
@use "variables";
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-300.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-300italic.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-regular.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-italic.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-600.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-600italic.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Metropolis';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url('../../../assets/fonts/Metropolis-SemiBold.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Metropolis';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url('../../../assets/fonts/Metropolis-SemiBoldItalic.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Metropolis';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('../../../assets/fonts/Metropolis-Regular.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Metropolis';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('../../../assets/fonts/Metropolis-RegularItalic.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Metropolis';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('../../../assets/fonts/Metropolis-Light.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Metropolis';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('../../../assets/fonts/Metropolis-LightItalic.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'FontAwesome';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('../../../assets/fonts/fontawesome-webfont.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: variables.$primary-color;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
color: variables.$primary-color--hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@include mixins.font-smoothing;
|
||||||
|
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
font-family: Metropolis, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
&.has-error {
|
||||||
|
color: variables.$red;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
opacity: 0.73;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding-left: 22px;
|
||||||
|
margin-top: 3px;
|
||||||
|
margin-bottom: 0.11em;
|
||||||
|
}
|
@@ -1,2 +1,11 @@
|
|||||||
|
@use 'sass:color';
|
||||||
|
|
||||||
// Since they can be used on any modal, menu or popover for now they are highest
|
// Since they can be used on any modal, menu or popover for now they are highest
|
||||||
$z-index-tooltip: 1350;
|
$z-index-tooltip: 1350;
|
||||||
|
|
||||||
|
// Color Variables
|
||||||
|
$primary-color: #166de0;
|
||||||
|
$primary-color--hover: color.adjust($primary-color, $lightness: -10%);
|
||||||
|
$white: rgb(255, 255, 255);
|
||||||
|
$light-gray: rgba(0, 0, 0, 0.15);
|
||||||
|
$red: rgb(214, 73, 70);
|
@@ -119,7 +119,7 @@
|
|||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
background: none;
|
background: none;
|
||||||
color: rgba(var(--center-channel-text-rgb), 0.32);
|
color: rgba(var(--center-channel-color-rgb), 0.32);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,210 +0,0 @@
|
|||||||
.certificate-modal .modal,
|
|
||||||
.show-certificate .modal {
|
|
||||||
background-color: aliceblue;
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate .modal-dialog {
|
|
||||||
width: 800px;
|
|
||||||
}
|
|
||||||
.modal-header {
|
|
||||||
border-bottom: 0px;
|
|
||||||
}
|
|
||||||
.modal-header::after {
|
|
||||||
border-bottom: solid 1px #E5E5E5;
|
|
||||||
width: 100%;
|
|
||||||
transform: translateY(15px);
|
|
||||||
}
|
|
||||||
.modal-footer::before {
|
|
||||||
border-top: solid 1px #E5E5E5;
|
|
||||||
width: 100%;
|
|
||||||
transform: translateY(-15px);
|
|
||||||
}
|
|
||||||
.modal-body :last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-modal .col-sm-4 {
|
|
||||||
padding: 0px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.certificate-modal .col-sm-8 {
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list thead {
|
|
||||||
width: 557.89px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list thead>tr {
|
|
||||||
padding: 0px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list thead>tr>th {
|
|
||||||
font-family: Helvetica Neue;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 18px;
|
|
||||||
padding: 3px 5px;
|
|
||||||
border-bottom: 1px solid #CCCCCC;
|
|
||||||
color: #333333;
|
|
||||||
height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list thead tr th:first-child span {
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
.certificate-list thead tr th span {
|
|
||||||
border-right: solid 1px #E5E5E5;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.certificate-list thead tr th:last-child span {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list tbody tr {
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #555555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list tbody>tr>td {
|
|
||||||
max-width: 165px;
|
|
||||||
height: 47px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 17px;
|
|
||||||
padding: 15px 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list tbody tr td:first-child {
|
|
||||||
padding-left: 15px;
|
|
||||||
max-width: 227px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-list tbody tr.selected {
|
|
||||||
background: #457AB2;
|
|
||||||
color: #FFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.certificate-list {
|
|
||||||
background: #FFFFFF;
|
|
||||||
border: 1px solid #CCCCCC;
|
|
||||||
border-radius: 4px;
|
|
||||||
border-collapse: unset;
|
|
||||||
box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.0008);
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.certificate-list:focus {
|
|
||||||
border: 1px solid #66AFE9;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 0px 0px 8px rgba(102, 175, 233, 0.6), inset 1px 1px 0px rgba(0, 0, 0, 0.00075);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.show-certificate button,
|
|
||||||
.certificate-modal button {
|
|
||||||
background: #FFFFFF;
|
|
||||||
border: 1px solid #CCCCCC;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 9px 12px;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate button:disabled,
|
|
||||||
.certificate-modal button:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate button.primary,
|
|
||||||
.certificate-modal button.primary {
|
|
||||||
background: #457AB2;
|
|
||||||
color: #FFFFFF;
|
|
||||||
border: 1px solid #2E6DA4;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.show-certificate button.primary:hover,
|
|
||||||
.certificate-modal button.primary:hover {
|
|
||||||
background: #659AD2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-modal button.info {
|
|
||||||
color: #457AB2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-modal button.info:disabled {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate .subtitle,
|
|
||||||
.certificate-modal .subtitle {
|
|
||||||
color: #737373;
|
|
||||||
margin: 0px 0px 15px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate .no-border,
|
|
||||||
.certificate-modal .no-border {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate dl {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate dt, dd {
|
|
||||||
float: left;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate dt {
|
|
||||||
width: 150px;
|
|
||||||
clear:both
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate dd {
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-key {
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 15px;
|
|
||||||
color: #333333;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.certificate-value {
|
|
||||||
padding-top: 1px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 14px;
|
|
||||||
text-align: left;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificate-section {
|
|
||||||
border-bottom: 1px solid #E5E5E5;
|
|
||||||
width: 598px;
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-certificate .details {
|
|
||||||
margin: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 15px;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
50
src/renderer/css/components/CertificateModal.scss
Normal file
50
src/renderer/css/components/CertificateModal.scss
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
@use "../css_variables";
|
||||||
|
|
||||||
|
.Modal .CertificateModal {
|
||||||
|
width: 832px;
|
||||||
|
max-width: 832px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CertificateModal_certInfoButton {
|
||||||
|
margin-right: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.CertificateModal_list {
|
||||||
|
text-align: left;
|
||||||
|
border: 1px solid rgba(var(--center-channel-color-rgb), 0.12);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
|
||||||
|
& + th {
|
||||||
|
border-left: 1px solid rgba(var(--center-channel-color-rgb), 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.12);
|
||||||
|
|
||||||
|
& + td {
|
||||||
|
border-left: 1px solid rgba(var(--center-channel-color-rgb), 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(var(--center-channel-color-rgb), 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: rgba(var(--button-bg-rgb), 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,7 +8,7 @@
|
|||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
|
|
||||||
.alternate-link__message {
|
.alternate-link__message {
|
||||||
color: var(--center-channel-text);
|
color: var(--center-channel-color);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
.ConfigureServer__card {
|
.ConfigureServer__card {
|
||||||
width: 540px;
|
width: 540px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid rgba(var(--center-channel-text-rgb), 0.08);
|
border: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
margin-left: 60px;
|
margin-left: 60px;
|
||||||
background-color: var(--center-channel-bg);
|
background-color: var(--center-channel-bg);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -143,6 +143,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ConfigureServer__card-form {
|
.ConfigureServer__card-form {
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
padding: 0 40px;
|
padding: 0 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.Header__main {
|
.Header__main {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@@ -34,6 +34,7 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgba(var(--center-channel-color-rgb), 0.64);
|
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
body {
|
body {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LoadingScreen {
|
.LoadingScreen {
|
||||||
|
464
src/renderer/css/components/Modal.scss
Normal file
464
src/renderer/css/components/Modal.scss
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
@use '../css_variables';
|
||||||
|
@use "../buttons";
|
||||||
|
@use "../typography";
|
||||||
|
|
||||||
|
@import '~@mattermost/compass-icons/css/compass-icons.css';
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: inherit;
|
||||||
|
|
||||||
|
&:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1050;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
outline: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.Modal_body {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: calc(90vh - 80px);
|
||||||
|
padding: 2px 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&.overflow--visible {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.divider {
|
||||||
|
border-top: var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
height: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: var(--border-default);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-error {
|
||||||
|
border-color: var(--error-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-clear {
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 16px;
|
||||||
|
opacity: 0.48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_footer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: end;
|
||||||
|
padding: 24px 32px;
|
||||||
|
border: none;
|
||||||
|
grid-row-gap: 8px;
|
||||||
|
|
||||||
|
&.divider {
|
||||||
|
border-top: var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Modal_footer--invisible {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
margin: 10px;
|
||||||
|
float: left;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .btn + .btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-textarea {
|
||||||
|
padding: 12px 30px 12px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info__label {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info__value {
|
||||||
|
padding-left: 10px;
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
p {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_dialog {
|
||||||
|
max-width: 95%;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
max-width: 500px;
|
||||||
|
|
||||||
|
&.Modal_xl {
|
||||||
|
width: 800px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_push-down {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_next-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_header {
|
||||||
|
display: flex;
|
||||||
|
min-height: 76px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 64px 16px 32px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
button.close {
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
float: right;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: none;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: -2px;
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
clip: rect(0,0,0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.divider {
|
||||||
|
border-bottom: var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_header-back-button {
|
||||||
|
margin-left: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
right: 18px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 1;
|
||||||
|
text-shadow: none;
|
||||||
|
|
||||||
|
&.icon-close {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.08);
|
||||||
|
color: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_title {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_title {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 28px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_content {
|
||||||
|
border: var(--border-default);
|
||||||
|
border-radius: var(--radius-l);
|
||||||
|
background: var(--center-channel-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_chevron-icon {
|
||||||
|
top: 50%;
|
||||||
|
font-size: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_prev-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_overflow {
|
||||||
|
.Modal_body {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-header__img {
|
||||||
|
margin-top: -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_header {
|
||||||
|
.Modal__header__text_container {
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.Modal_subheading-container {
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p#Modal_subHeading {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-block: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_body {
|
||||||
|
max-height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&.divider {
|
||||||
|
border-top: var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
height: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: var(--border-default);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-error {
|
||||||
|
border-color: var(--error-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.MaxLengthInput {
|
||||||
|
&.form-control.has-error {
|
||||||
|
padding-right: 66px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__validation {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 56px;
|
||||||
|
color: var(--error-text);
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-clear {
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 16px;
|
||||||
|
opacity: 0.48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__compassDesign {
|
||||||
|
.Modal_content {
|
||||||
|
.Modal_body {
|
||||||
|
.Modal__body {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&.padding {
|
||||||
|
padding: 0px 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_error {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
padding: 14px 32px;
|
||||||
|
border: 1px solid rgba(var(--dnd-indicator-rgb), 0.16);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
background: rgba(var(--dnd-indicator-rgb), 0.08);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: var(--error-text);
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.Modal_header {
|
||||||
|
box-shadow: var(--elevation-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fade {
|
||||||
|
.Modal_dialog {
|
||||||
|
transition: transform .3s ease-out,-webkit-transform .3s ease-out;
|
||||||
|
transform: translate(0, -50px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.show .Modal_dialog {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_body.divider .Input_container {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Input_container + .Input_container {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade {
|
||||||
|
transition: opacity 0.15s linear;
|
||||||
|
|
||||||
|
&:not(.show) {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-primary.btn-danger:hover {
|
||||||
|
background: linear-gradient(0deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08)), var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_backdrop {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1040;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #000;
|
||||||
|
|
||||||
|
&.fade {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.in {
|
||||||
|
opacity: 0.64;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,38 +1,34 @@
|
|||||||
.NewServerModal-noBottomSpace {
|
@use "../css_variables";
|
||||||
padding-bottom: 0px;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.NewServerModal-validation {
|
.Modal .NewServerModal {
|
||||||
margin-top: 8px;
|
width: 600px;
|
||||||
margin-right: auto;
|
max-width: 600px;
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-top: 24px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Modal_body {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.NewServerModal__toggle__description {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
font-weight: 400;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
display: flex;
|
line-height: 16px;
|
||||||
flex-direction: column;
|
max-width: 400px;
|
||||||
|
|
||||||
> div {
|
|
||||||
margin-top: 4px;
|
|
||||||
> span {
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: #d24b4e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
color: #c79e3f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
color: #06d6a0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.NewServerModal-validationSpinner {
|
.NewServerModal__permissions__title {
|
||||||
width: 0.75rem;
|
margin-top: 24px;
|
||||||
height: 0.75rem;
|
margin-bottom: 12px;
|
||||||
margin-left: 2px;
|
font-size: 16px;
|
||||||
margin-right: 4px;
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
}
|
}
|
@@ -4,10 +4,11 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
column-gap: 12px;
|
column-gap: 12px;
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
line-height: 16px;
|
line-height: 24px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
@@ -63,6 +64,10 @@
|
|||||||
&.disabled {
|
&.disabled {
|
||||||
background-color: var(--button-bg-30);
|
background-color: var(--button-bg-30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -23,7 +23,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.WelcomeScreenSlide__subtitle {
|
.WelcomeScreenSlide__subtitle {
|
||||||
color: rgba(var(--center-channel-text-rgb), 0.72);
|
color: rgba(var(--center-channel-color-rgb), 0.72);
|
||||||
font-family: Open Sans;
|
font-family: Open Sans;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
@@ -4,6 +4,5 @@
|
|||||||
@import url("PermissionRequestDialog.css");
|
@import url("PermissionRequestDialog.css");
|
||||||
@import url("TabBar.css");
|
@import url("TabBar.css");
|
||||||
@import url("UpdaterPage.css");
|
@import url("UpdaterPage.css");
|
||||||
@import url("CertificateModal.css");
|
|
||||||
@import url("LoadingScreen.css");
|
@import url("LoadingScreen.css");
|
||||||
@import url("LoadingAnimation.css");
|
@import url("LoadingAnimation.css");
|
||||||
|
@@ -4,6 +4,18 @@
|
|||||||
@include meta.load-css('bootstrap-dark/src/bootstrap-dark.css');
|
@include meta.load-css('bootstrap-dark/src/bootstrap-dark.css');
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
|
> div.modal {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.modal-header .modal-title, .modal-header .close {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: #191B1F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.SettingsPage__spellCheckerLocalesDropdown .SettingsPage__spellCheckerLocalesDropdown__control {
|
.SettingsPage__spellCheckerLocalesDropdown .SettingsPage__spellCheckerLocalesDropdown__control {
|
||||||
background: #242a30;
|
background: #242a30;
|
||||||
}
|
}
|
||||||
@@ -34,5 +46,52 @@
|
|||||||
.Toggle___switch.disabled {
|
.Toggle___switch.disabled {
|
||||||
background: rgba(var(--center-channel-bg-rgb), 0.08);
|
background: rgba(var(--center-channel-bg-rgb), 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Input {
|
||||||
|
color: var(--button-color);
|
||||||
|
background-color: unset;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: rgba(var(--button-color-rgb), 0.56);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Input_wrapper {
|
||||||
|
color: rgba(var(--button-color-rgb), 0.56);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Input___info {
|
||||||
|
color: rgba(var(--button-color-rgb), 0.56);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Input_fieldset {
|
||||||
|
background-color: #191B1F;
|
||||||
|
border: 1px solid rgba(#fff, 0.16);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(#fff, 0.48);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: #fff;
|
||||||
|
box-shadow: inset 0 0 0 1px #fff;
|
||||||
|
color: var(--button-color);
|
||||||
|
|
||||||
|
.Input_legend {
|
||||||
|
color: var(--button-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Input_legend {
|
||||||
|
background-color: #191B1F;
|
||||||
|
color: rgba(var(--button-color-rgb), 0.64);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
.Input_fieldset {
|
||||||
|
background: rgba(var(--button-color-rgb), 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,10 +5,6 @@ import type {Certificate} from 'electron/renderer';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
import 'renderer/css/components/CertificateModal.css';
|
|
||||||
|
|
||||||
import type {CertificateModalInfo} from 'types/modals';
|
import type {CertificateModalInfo} from 'types/modals';
|
||||||
|
|
||||||
import SelectCertificateModal from './certificateModal';
|
import SelectCertificateModal from './certificateModal';
|
||||||
|
@@ -1,18 +1,21 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
import type {Certificate} from 'electron/renderer';
|
import type {Certificate} from 'electron/renderer';
|
||||||
import React, {Fragment} from 'react';
|
import React, {Fragment} from 'react';
|
||||||
import {Modal, Button, Table, Row, Col} from 'react-bootstrap';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import {Modal} from 'renderer/components/Modal';
|
||||||
import IntlProvider from 'renderer/intl_provider';
|
import IntlProvider from 'renderer/intl_provider';
|
||||||
|
|
||||||
import ShowCertificateModal from '../../components/showCertificateModal';
|
import ShowCertificateModal from '../../components/showCertificateModal';
|
||||||
|
|
||||||
|
import 'renderer/css/components/CertificateModal.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSelect: (cert: Certificate) => void;
|
onSelect: (cert: Certificate) => void;
|
||||||
onCancel?: () => void;
|
onCancel: () => void;
|
||||||
getCertInfo: () => Promise<{url: string; list: Certificate[]}>;
|
getCertInfo: () => Promise<{url: string; list: Certificate[]}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,107 +120,99 @@ export default class SelectCertificateModal extends React.PureComponent<Props, S
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const footerContent = (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
disabled={this.state.selectedIndex === undefined}
|
||||||
|
onClick={this.handleCertificateInfo}
|
||||||
|
className={classNames('Modal__button btn btn-tertiary CertificateModal_certInfoButton', {
|
||||||
|
disabled: this.state.selectedIndex === undefined,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.modals.certificate.certificateModal.certInfoButton'
|
||||||
|
defaultMessage='Certificate Information'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className={classNames('Modal__button btn btn-tertiary')}
|
||||||
|
onClick={this.props.onCancel}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='modal.cancel'
|
||||||
|
defaultMessage='Cancel'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type='submit'
|
||||||
|
className={classNames('Modal__button btn btn-primary confirm', {
|
||||||
|
disabled: this.state.selectedIndex === undefined,
|
||||||
|
})}
|
||||||
|
onClick={this.handleOk}
|
||||||
|
disabled={this.state.selectedIndex === undefined}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='modal.confirm'
|
||||||
|
defaultMessage='Confirm'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider>
|
<IntlProvider>
|
||||||
<Modal
|
<Modal
|
||||||
bsClass='modal'
|
id='selectCertificateModal'
|
||||||
className='certificate-modal'
|
className='CertificateModal'
|
||||||
show={Boolean(this.state.list && this.state.url)}
|
show={Boolean(this.state.list && this.state.url)}
|
||||||
onHide={() => {}}
|
onExited={this.props.onCancel}
|
||||||
|
modalHeaderText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.modals.certificate.certificateModal.title'
|
||||||
|
defaultMessage='Select a certificate'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
modalSubheaderText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.modals.certificate.certificateModal.subtitle'
|
||||||
|
defaultMessage='Select a certificate to authenticate yourself to {url}'
|
||||||
|
values={{url: this.state.url}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
footerContent={footerContent}
|
||||||
>
|
>
|
||||||
<Modal.Header>
|
<table
|
||||||
<Modal.Title>
|
className='CertificateModal_list'
|
||||||
<FormattedMessage
|
tabIndex={1}
|
||||||
id='renderer.modals.certificate.certificateModal.title'
|
>
|
||||||
defaultMessage='Select a certificate'
|
<thead>
|
||||||
/>
|
<tr>
|
||||||
</Modal.Title>
|
<th><span className={'divider'}>
|
||||||
</Modal.Header>
|
<FormattedMessage
|
||||||
<Modal.Body>
|
id='renderer.modals.certificate.certificateModal.subject'
|
||||||
<p className={'subtitle'}>
|
defaultMessage='Subject'
|
||||||
<FormattedMessage
|
/>
|
||||||
id='renderer.modals.certificate.certificateModal.subtitle'
|
</span></th>
|
||||||
defaultMessage='Select a certificate to authenticate yourself to {url}'
|
<th><span className={'divider'}>
|
||||||
values={{url: this.state.url}}
|
<FormattedMessage
|
||||||
/>
|
id='renderer.modals.certificate.certificateModal.issuer'
|
||||||
</p>
|
defaultMessage='Issuer'
|
||||||
<Table
|
/>
|
||||||
striped={true}
|
</span></th>
|
||||||
hover={true}
|
<th>
|
||||||
responsive={true}
|
<FormattedMessage
|
||||||
className='certificate-list'
|
id='renderer.modals.certificate.certificateModal.serial'
|
||||||
tabIndex={1}
|
defaultMessage='Serial'
|
||||||
>
|
/>
|
||||||
<thead>
|
</th>
|
||||||
<tr>
|
</tr>
|
||||||
<th><span className={'divider'}>
|
</thead>
|
||||||
<FormattedMessage
|
<tbody>
|
||||||
id='renderer.modals.certificate.certificateModal.subject'
|
{this.renderCerts(this.state.list!)}
|
||||||
defaultMessage='Subject'
|
</tbody>
|
||||||
/>
|
</table>
|
||||||
</span></th>
|
|
||||||
<th><span className={'divider'}>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.modals.certificate.certificateModal.issuer'
|
|
||||||
defaultMessage='Issuer'
|
|
||||||
/>
|
|
||||||
</span></th>
|
|
||||||
<th>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.modals.certificate.certificateModal.serial'
|
|
||||||
defaultMessage='Serial'
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{this.renderCerts(this.state.list!)}
|
|
||||||
<tr/* this is to correct table height without affecting real rows *//>
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
</Modal.Body>
|
|
||||||
<Modal.Footer className={'no-border'}>
|
|
||||||
<div className={'container-fluid'}>
|
|
||||||
<Row>
|
|
||||||
<Col sm={4}>
|
|
||||||
<Button
|
|
||||||
variant='info'
|
|
||||||
disabled={this.state.selectedIndex === null}
|
|
||||||
onClick={this.handleCertificateInfo}
|
|
||||||
className={'info'}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.modals.certificate.certificateModal.certInfoButton'
|
|
||||||
defaultMessage='Certificate Information'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
<Col sm={8}>
|
|
||||||
<Button
|
|
||||||
variant='link'
|
|
||||||
onClick={this.props.onCancel}
|
|
||||||
className={'secondary'}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.cancel'
|
|
||||||
defaultMessage='Cancel'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant='primary'
|
|
||||||
onClick={this.handleOk}
|
|
||||||
disabled={this.state.selectedIndex === null}
|
|
||||||
className={'primary'}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.ok'
|
|
||||||
defaultMessage='OK'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import 'renderer/css/modals-dark.scss';
|
|
||||||
|
|
||||||
export default function addDarkModeListener() {
|
export default function addDarkModeListener() {
|
||||||
const setDarkMode = (darkMode: boolean) => {
|
const setDarkMode = (darkMode: boolean) => {
|
||||||
if (darkMode) {
|
if (darkMode) {
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
|
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
@@ -6,8 +6,6 @@ import ReactDOM from 'react-dom';
|
|||||||
|
|
||||||
import LoadingScreen from '../../components/LoadingScreen';
|
import LoadingScreen from '../../components/LoadingScreen';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
import 'renderer/css/components/LoadingAnimation.css';
|
import 'renderer/css/components/LoadingAnimation.css';
|
||||||
import 'renderer/css/components/LoadingScreen.css';
|
import 'renderer/css/components/LoadingScreen.css';
|
||||||
|
|
||||||
|
@@ -9,9 +9,6 @@ import IntlProvider from 'renderer/intl_provider';
|
|||||||
|
|
||||||
import type {LoginModalInfo} from 'types/modals';
|
import type {LoginModalInfo} from 'types/modals';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
|
|
||||||
import LoginModal from './loginModal';
|
import LoginModal from './loginModal';
|
||||||
|
|
||||||
import setupDarkMode from '../darkMode';
|
import setupDarkMode from '../darkMode';
|
||||||
|
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
import type {AuthenticationResponseDetails, AuthInfo} from 'electron/renderer';
|
import type {AuthenticationResponseDetails, AuthInfo} from 'electron/renderer';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Button, Col, FormLabel, Form, FormGroup, FormControl, Modal} from 'react-bootstrap';
|
|
||||||
import type {IntlShape} from 'react-intl';
|
import type {IntlShape} from 'react-intl';
|
||||||
import {FormattedMessage, injectIntl} from 'react-intl';
|
import {FormattedMessage, injectIntl} from 'react-intl';
|
||||||
|
|
||||||
import {parseURL} from 'common/utils/url';
|
import {parseURL} from 'common/utils/url';
|
||||||
|
import Input, {SIZE} from 'renderer/components/Input';
|
||||||
|
import {Modal} from 'renderer/components/Modal';
|
||||||
|
|
||||||
import type {LoginModalInfo} from 'types/modals';
|
import type {LoginModalInfo} from 'types/modals';
|
||||||
|
|
||||||
@@ -44,8 +45,7 @@ class LoginModal extends React.PureComponent<Props, State> {
|
|||||||
this.setState({request, authInfo});
|
this.setState({request, authInfo});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
handleSubmit = () => {
|
||||||
event.preventDefault();
|
|
||||||
this.props.onLogin(this.state.request!, this.state.username, this.state.password);
|
this.props.onLogin(this.state.request!, this.state.username, this.state.password);
|
||||||
this.setState({
|
this.setState({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -55,8 +55,7 @@ class LoginModal extends React.PureComponent<Props, State> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleCancel = (event: React.MouseEvent<HTMLButtonElement>) => {
|
handleCancel = () => {
|
||||||
event.preventDefault();
|
|
||||||
this.props.onCancel(this.state.request!);
|
this.props.onCancel(this.state.request!);
|
||||||
this.setState({
|
this.setState({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -101,94 +100,44 @@ class LoginModal extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
id='loginModal'
|
||||||
show={Boolean(this.state.request && this.state.authInfo)}
|
show={Boolean(this.state.request && this.state.authInfo)}
|
||||||
|
onExited={this.handleCancel}
|
||||||
|
modalHeaderText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='renderer.modals.login.loginModal.title'
|
||||||
|
defaultMessage='Authentication Required'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
handleConfirm={this.handleSubmit}
|
||||||
|
confirmButtonText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='label.login'
|
||||||
|
defaultMessage='Login'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
handleCancel={this.handleCancel}
|
||||||
|
modalSubheaderText={this.renderLoginModalMessage()}
|
||||||
>
|
>
|
||||||
<Modal.Header>
|
<Input
|
||||||
<Modal.Title>
|
autoFocus={true}
|
||||||
<FormattedMessage
|
id='loginModalUsername'
|
||||||
id='renderer.modals.login.loginModal.title'
|
name='username'
|
||||||
defaultMessage='Authentication Required'
|
type='text'
|
||||||
/>
|
inputSize={SIZE.LARGE}
|
||||||
</Modal.Title>
|
value={this.state.username}
|
||||||
</Modal.Header>
|
onChange={this.setUsername}
|
||||||
<Modal.Body>
|
placeholder={intl.formatMessage({id: 'renderer.modals.login.loginModal.username', defaultMessage: 'User Name'})}
|
||||||
<p>
|
/>
|
||||||
{this.renderLoginModalMessage()}
|
<Input
|
||||||
</p>
|
id='loginModalPassword'
|
||||||
<Form
|
name='password'
|
||||||
onSubmit={this.handleSubmit}
|
type='password'
|
||||||
>
|
inputSize={SIZE.LARGE}
|
||||||
<FormGroup>
|
onChange={this.setPassword}
|
||||||
<Col
|
value={this.state.password}
|
||||||
as={FormLabel}
|
placeholder={intl.formatMessage({id: 'renderer.modals.login.loginModal.password', defaultMessage: 'Password'})}
|
||||||
sm={2}
|
/>
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.modals.login.loginModal.username'
|
|
||||||
defaultMessage='User Name'
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col sm={10}>
|
|
||||||
<FormControl
|
|
||||||
type='text'
|
|
||||||
placeholder={intl.formatMessage({id: 'renderer.modals.login.loginModal.username', defaultMessage: 'User Name'})}
|
|
||||||
onChange={this.setUsername}
|
|
||||||
value={this.state.username}
|
|
||||||
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup>
|
|
||||||
<Col
|
|
||||||
as={FormLabel}
|
|
||||||
sm={2}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='renderer.modals.login.loginModal.password'
|
|
||||||
defaultMessage='Password'
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col sm={10}>
|
|
||||||
<FormControl
|
|
||||||
type='password'
|
|
||||||
placeholder={intl.formatMessage({id: 'renderer.modals.login.loginModal.password', defaultMessage: 'Password'})}
|
|
||||||
onChange={this.setPassword}
|
|
||||||
value={this.state.password}
|
|
||||||
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup>
|
|
||||||
<Col sm={12}>
|
|
||||||
<div className='pull-right'>
|
|
||||||
<Button
|
|
||||||
type='submit'
|
|
||||||
variant='primary'
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.login'
|
|
||||||
defaultMessage='Login'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
{ ' ' }
|
|
||||||
<Button
|
|
||||||
variant='link'
|
|
||||||
onClick={this.handleCancel}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.cancel'
|
|
||||||
defaultMessage='Cancel'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
</Modal.Body>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
|
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
@@ -42,7 +39,8 @@ const NewServerModalWrapper: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<IntlProvider>
|
<IntlProvider>
|
||||||
<NewServerModal
|
<NewServerModal
|
||||||
onClose={unremoveable ? undefined : onClose}
|
unremoveable={unremoveable}
|
||||||
|
onClose={onClose}
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
editMode={false}
|
editMode={false}
|
||||||
prefillURL={data?.prefillURL}
|
prefillURL={data?.prefillURL}
|
||||||
|
@@ -8,9 +8,6 @@ import IntlProvider from 'renderer/intl_provider';
|
|||||||
|
|
||||||
import type {PermissionModalInfo} from 'types/modals';
|
import type {PermissionModalInfo} from 'types/modals';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
|
|
||||||
import PermissionModal from './permissionModal';
|
import PermissionModal from './permissionModal';
|
||||||
|
|
||||||
import setupDarkMode from '../darkMode';
|
import setupDarkMode from '../darkMode';
|
||||||
|
@@ -2,19 +2,19 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Modal, Button} from 'react-bootstrap';
|
|
||||||
import type {IntlShape} from 'react-intl';
|
|
||||||
import {FormattedMessage, injectIntl} from 'react-intl';
|
import {FormattedMessage, injectIntl} from 'react-intl';
|
||||||
|
import type {IntlShape} from 'react-intl';
|
||||||
|
|
||||||
import {PERMISSION_DESCRIPTION} from 'common/permissions';
|
import {PERMISSION_DESCRIPTION} from 'common/permissions';
|
||||||
import {parseURL} from 'common/utils/url';
|
import {parseURL} from 'common/utils/url';
|
||||||
import {t} from 'common/utils/util';
|
import {t} from 'common/utils/util';
|
||||||
|
import {Modal} from 'renderer/components/Modal';
|
||||||
|
|
||||||
import type {PermissionModalInfo} from 'types/modals';
|
import type {PermissionModalInfo} from 'types/modals';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
handleDeny: React.MouseEventHandler<HTMLButtonElement>;
|
handleDeny: () => void;
|
||||||
handleGrant: React.MouseEventHandler<HTMLButtonElement>;
|
handleGrant: () => void;
|
||||||
getPermissionInfo: () => Promise<PermissionModalInfo>;
|
getPermissionInfo: () => Promise<PermissionModalInfo>;
|
||||||
openExternalLink: (protocol: string, url: string) => void;
|
openExternalLink: (protocol: string, url: string) => void;
|
||||||
intl: IntlShape;
|
intl: IntlShape;
|
||||||
@@ -67,74 +67,52 @@ class PermissionModal extends React.PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<p>
|
<FormattedMessage
|
||||||
<FormattedMessage
|
id='renderer.modals.permission.permissionModal.body'
|
||||||
id='renderer.modals.permission.permissionModal.body'
|
defaultMessage={'A site that\'s not included in your Mattermost server configuration requires access for {permission}.'}
|
||||||
defaultMessage={'A site that\'s not included in your Mattermost server configuration requires access for {permission}.'}
|
values={{
|
||||||
values={{
|
permission: this.props.intl.formatMessage({id: PERMISSION_DESCRIPTION[permission!]}),
|
||||||
permission: this.props.intl.formatMessage({id: PERMISSION_DESCRIPTION[permission!]}),
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<p/>
|
||||||
{}
|
<FormattedMessage
|
||||||
</p>
|
id='renderer.modals.permission.permissionModal.requestOriginatedFromOrigin'
|
||||||
<p>
|
defaultMessage='This request originated from <link>{origin}</link>'
|
||||||
<FormattedMessage
|
values={{
|
||||||
id='renderer.modals.permission.permissionModal.requestOriginatedFromOrigin'
|
origin: originDisplay,
|
||||||
defaultMessage='This request originated from <link>{origin}</link>'
|
link: (msg: React.ReactNode) => (
|
||||||
values={{
|
<a
|
||||||
origin: originDisplay,
|
|
||||||
link: (msg: React.ReactNode) => (
|
|
||||||
<a
|
|
||||||
|
|
||||||
onClick={click}
|
onClick={click}
|
||||||
href='#'
|
href='#'
|
||||||
>
|
>
|
||||||
{msg}
|
{msg}
|
||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</p>
|
</>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
bsClass='modal'
|
|
||||||
className='permission-modal'
|
|
||||||
show={Boolean(this.state.url && this.state.permission)}
|
|
||||||
id='requestPermissionModal'
|
id='requestPermissionModal'
|
||||||
enforceFocus={true}
|
show={Boolean(this.state.url && this.state.permission)}
|
||||||
onHide={() => {}}
|
onExited={() => {}}
|
||||||
|
modalHeaderText={this.getModalTitle()}
|
||||||
|
handleConfirm={this.props.handleGrant}
|
||||||
|
confirmButtonText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='label.accept'
|
||||||
|
defaultMessage='Accept'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
handleCancel={this.props.handleDeny}
|
||||||
>
|
>
|
||||||
<Modal.Header>
|
{this.getModalBody()}
|
||||||
<Modal.Title>{this.getModalTitle()}</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Body>
|
|
||||||
{this.getModalBody()}
|
|
||||||
</Modal.Body>
|
|
||||||
<Modal.Footer className={'remove-border'}>
|
|
||||||
<div>
|
|
||||||
<Button onClick={this.props.handleDeny}>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.cancel'
|
|
||||||
defaultMessage='Cancel'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant='primary'
|
|
||||||
onClick={this.props.handleGrant}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='label.accept'
|
|
||||||
defaultMessage='Accept'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import React from 'react';
|
||||||
import 'renderer/css/modals.css';
|
|
||||||
|
|
||||||
import React, {useEffect, useState} from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import IntlProvider from 'renderer/intl_provider';
|
import IntlProvider from 'renderer/intl_provider';
|
||||||
@@ -23,19 +20,10 @@ const onSave = (data: boolean) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const RemoveServerModalWrapper: React.FC = () => {
|
const RemoveServerModalWrapper: React.FC = () => {
|
||||||
const [serverName, setServerName] = useState<string>('');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.desktop.modals.getModalInfo<{name: string}>().then(({name}) => {
|
|
||||||
setServerName(name);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider>
|
<IntlProvider>
|
||||||
<RemoveServerModal
|
<RemoveServerModal
|
||||||
show={true}
|
show={true}
|
||||||
serverName={serverName}
|
|
||||||
onHide={() => {
|
onHide={() => {
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
import 'renderer/css/index.css';
|
import 'renderer/css/index.css';
|
||||||
import 'renderer/css/settings.css';
|
import 'renderer/css/settings.css';
|
||||||
|
import 'renderer/css/modals-dark.scss';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
@@ -11,8 +11,6 @@ import type {UniqueServer} from 'types/config';
|
|||||||
import ConfigureServer from '../../components/ConfigureServer';
|
import ConfigureServer from '../../components/ConfigureServer';
|
||||||
import WelcomeScreen from '../../components/WelcomeScreen';
|
import WelcomeScreen from '../../components/WelcomeScreen';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
|
|
||||||
const MOBILE_SCREEN_WIDTH = 1200;
|
const MOBILE_SCREEN_WIDTH = 1200;
|
||||||
|
|
||||||
const onConnect = (data: UniqueServer) => {
|
const onConnect = (data: UniqueServer) => {
|
||||||
|
Reference in New Issue
Block a user