Merge branch 'master' into SDA-2651

This commit is contained in:
mattias-symphony 2020-11-06 08:13:42 +01:00 committed by GitHub
commit 16c3ebf741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2545 additions and 1561 deletions

19
.vscode/launch.json vendored
View File

@ -10,7 +10,9 @@
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.exe"
},
"args": ["."],
"args": [
"."
],
"env": {
"ELECTRON_DEBUGGING": "true",
"ELECTRON_DEV": "true"
@ -30,7 +32,10 @@
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.exe"
},
"args": [".", "--url=https://corporate.symphony.com"],
"args": [
".",
"--url=https://corporate.symphony.com"
],
"env": {
"ELECTRON_DEBUGGING": "true",
"ELECTRON_DEV": "true"
@ -50,7 +55,10 @@
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.exe"
},
"args": [".", "--url=https://local-dev.symphony.com:9090"],
"args": [
".",
"--url=https://local-dev.symphony.com:9090"
],
"env": {
"ELECTRON_DEBUGGING": "true",
"ELECTRON_DEV": "true"
@ -70,7 +78,10 @@
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.exe"
},
"args": [".", "--url=${workspaceFolder}/src/demo/index.html"],
"args": [
".",
"--url=file://${workspaceFolder}/src/demo/index.html"
],
"env": {
"ELECTRON_DEBUGGING": "true",
"ELECTRON_DEV": "true"

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"npm.packageManager": "npm",
"typescript.tsdk": "./node_modules/typescript/lib",
"editor.formatOnSave": true,
"jestrunner.configPath": "jest-config.json",
}

View File

@ -159,6 +159,7 @@
"electron-spellchecker": "git+https://github.com/symphonyoss/electron-spellchecker.git#v2.0.4",
"ffi-napi": "3.0.0",
"filesize": "6.1.0",
"image-size": "^0.9.3",
"react": "16.13.0",
"react-dom": "16.13.0",
"ref-napi": "1.4.3",

View File

@ -0,0 +1,65 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Snipping Tool should render correctly 1`] = `
<div
className="SnippingTool"
lang="en-US"
>
<header>
<div
className="DrawActions"
>
<button
className="ActionButton"
onClick={[Function]}
>
<img
src="../renderer/assets/snip-draw.svg"
/>
</button>
<button
className="ActionButton"
onClick={[Function]}
>
<img
src="../renderer/assets/snip-highlight.svg"
/>
</button>
<button
className="ActionButton"
onClick={[Function]}
>
<img
src="../renderer/assets/snip-erase.svg"
/>
</button>
</div>
<div
className="ClearActions"
>
<button
className="ClearButton"
onClick={[Function]}
>
Clear
</button>
</div>
</header>
<main>
<img
alt="Screen snippet"
className="SnippetImage"
height={600}
src="Screen-Snippet"
width={800}
/>
</main>
<footer>
<button
onClick={[Function]}
>
Done
</button>
</footer>
</div>
`;

20
spec/snippingTool.spec.ts Normal file
View File

@ -0,0 +1,20 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import SnippingTool from '../src/renderer/components/snipping-tool';
import { ipcRenderer } from './__mocks__/electron';
describe('Snipping Tool', () => {
const snippingToolLabel = 'snipping-tool-data';
const onLabelEvent = 'on';
it('should render correctly', () => {
const wrapper = shallow(React.createElement(SnippingTool));
expect(wrapper).toMatchSnapshot();
});
it('should call `snipping-tool-data` event when component is mounted', () => {
const spy = jest.spyOn(ipcRenderer, onLabelEvent);
shallow(React.createElement(SnippingTool));
expect(spy).toBeCalledWith(snippingToolLabel, expect.any(Function));
});
});

View File

@ -1,12 +1,19 @@
import { app, BrowserWindow } from 'electron';
import { app, BrowserWindow, ipcMain } from 'electron';
import * as fs from 'fs';
import sizeOf from 'image-size';
import * as os from 'os';
import * as path from 'path';
import { ChildProcess, ExecException, execFile } from 'child_process';
import * as util from 'util';
import { IScreenSnippet } from '../common/api-interface';
import { isDevEnv, isElectronQA, isLinux, isMac, isWindowsOS } from '../common/env';
import {
isDevEnv,
isElectronQA,
isLinux,
isMac,
isWindowsOS,
} from '../common/env';
import { i18n } from '../common/i18n';
import { logger } from '../common/logger';
import { updateAlwaysOnTop } from './window-actions';
@ -33,9 +40,13 @@ class ScreenSnippet {
fs.mkdirSync(this.tempDir);
}
}
this.captureUtil = isMac ? '/usr/sbin/screencapture' : isDevEnv
? path.join(__dirname,
'../../../node_modules/screen-snippet/ScreenSnippet.exe')
this.captureUtil = isMac
? '/usr/sbin/screencapture'
: isDevEnv
? path.join(
__dirname,
'../../../node_modules/screen-snippet/ScreenSnippet.exe',
)
: path.join(path.dirname(app.getPath('exe')), 'ScreenSnippet.exe');
if (isLinux) {
@ -58,14 +69,21 @@ class ScreenSnippet {
}
}
logger.info(`screen-snippet-handler: Starting screen capture!`);
this.outputFileName = path.join(this.tempDir, 'symphonyImage-' + Date.now() + '.png');
this.outputFileName = path.join(
this.tempDir,
'symphonyImage-' + Date.now() + '.png',
);
if (isMac) {
this.captureUtilArgs = [ '-i', '-s', '-t', 'png', this.outputFileName ];
this.captureUtilArgs = ['-i', '-s', '-t', 'png', this.outputFileName];
} else if (isWindowsOS) {
if (windowHandler.isMana) {
this.captureUtilArgs = [ '--no-annotate', this.outputFileName, i18n.getLocale() ];
this.captureUtilArgs = [
'--no-annotate',
this.outputFileName,
i18n.getLocale(),
];
} else {
this.captureUtilArgs = [ this.outputFileName, i18n.getLocale() ];
this.captureUtilArgs = [this.outputFileName, i18n.getLocale()];
}
} else if (isLinux) {
this.captureUtilArgs = ['-a', '-f', this.outputFileName];
@ -74,22 +92,41 @@ class ScreenSnippet {
}
this.focusedWindow = BrowserWindow.getFocusedWindow();
logger.info(`screen-snippet-handler: Capturing snippet with file ${this.outputFileName} and args ${this.captureUtilArgs}!`);
logger.info(
`screen-snippet-handler: Capturing snippet with file ${this.outputFileName} and args ${this.captureUtilArgs}!`,
);
// only allow one screen capture at a time.
if (this.child) {
logger.info(`screen-snippet-handler: Child screen capture exists, killing it and keeping only 1 instance!`);
logger.info(
`screen-snippet-handler: Child screen capture exists, killing it and keeping only 1 instance!`,
);
this.killChildProcess();
}
try {
await this.execCmd(this.captureUtil, this.captureUtilArgs);
const { message, data, type }: IScreenSnippet = await this.convertFileToData();
logger.info(`screen-snippet-handler: Snippet captured! Sending data to SFE`);
if (windowHandler.isMana) {
windowHandler.closeSnippingToolWindow();
const dimensions = this.getImageSize();
windowHandler.createSnippingToolWindow(this.outputFileName, dimensions);
this.uploadSnippet(webContents);
return;
}
const {
message,
data,
type,
}: IScreenSnippet = await this.convertFileToData();
logger.info(
`screen-snippet-handler: Snippet captured! Sending data to SFE`,
);
webContents.send('screen-snippet-data', { message, data, type });
await this.verifyAndUpdateAlwaysOnTop();
} catch (error) {
await this.verifyAndUpdateAlwaysOnTop();
logger.error(`screen-snippet-handler: screen capture failed with error: ${error}!`);
logger.error(
`screen-snippet-handler: screen capture failed with error: ${error}!`,
);
}
}
@ -108,7 +145,9 @@ class ScreenSnippet {
await this.verifyAndUpdateAlwaysOnTop();
} catch (error) {
await this.verifyAndUpdateAlwaysOnTop();
logger.error(`screen-snippet-handler: screen capture cancel failed with error: ${error}!`);
logger.error(
`screen-snippet-handler: screen capture cancel failed with error: ${error}!`,
);
}
}
@ -132,16 +171,25 @@ class ScreenSnippet {
* @param captureUtilArgs {captureUtilArgs}
* @example execCmd('-i -s', '/user/desktop/symphonyImage-1544025391698.png')
*/
private execCmd(captureUtil: string, captureUtilArgs: ReadonlyArray<string>): Promise<ChildProcess> {
logger.info(`screen-snippet-handlers: execCmd ${captureUtil} ${captureUtilArgs}`);
private execCmd(
captureUtil: string,
captureUtilArgs: ReadonlyArray<string>,
): Promise<ChildProcess> {
logger.info(
`screen-snippet-handlers: execCmd ${captureUtil} ${captureUtilArgs}`,
);
return new Promise<ChildProcess>((resolve, reject) => {
return this.child = execFile(captureUtil, captureUtilArgs, (error: ExecException | null) => {
return (this.child = execFile(
captureUtil,
captureUtilArgs,
(error: ExecException | null) => {
if (error && error.killed) {
// processs was killed, just resolve with no data.
return reject(error);
}
resolve();
});
},
));
});
}
@ -154,12 +202,16 @@ class ScreenSnippet {
private async convertFileToData(): Promise<IScreenSnippet> {
try {
if (!this.outputFileName) {
logger.info(`screen-snippet-handler: screen capture failed! output file doesn't exist!`);
logger.info(
`screen-snippet-handler: screen capture failed! output file doesn't exist!`,
);
return { message: 'output file name is required', type: 'ERROR' };
}
const data = await readFile(this.outputFileName);
if (!data) {
logger.info(`screen-snippet-handler: screen capture failed! data doesn't exist!`);
logger.info(
`screen-snippet-handler: screen capture failed! data doesn't exist!`,
);
return { message: `no file data provided`, type: 'ERROR' };
}
// convert binary data to base64 encoded string
@ -179,9 +231,13 @@ class ScreenSnippet {
// remove tmp file (async)
if (this.outputFileName) {
fs.unlink(this.outputFileName, (removeErr) => {
logger.info(`screen-snippet-handler: cleaning up temp snippet file: ${this.outputFileName}!`);
logger.info(
`screen-snippet-handler: cleaning up temp snippet file: ${this.outputFileName}!`,
);
if (removeErr) {
logger.error(`screen-snippet-handler: error removing temp snippet file: ${this.outputFileName}, err: ${removeErr}`);
logger.error(
`screen-snippet-handler: error removing temp snippet file: ${this.outputFileName}, err: ${removeErr}`,
);
}
});
}
@ -197,6 +253,49 @@ class ScreenSnippet {
this.shouldUpdateAlwaysOnTop = false;
}
}
/**
* Gets the height & width of an image
*/
private getImageSize():
| {
height: number | undefined;
width: number | undefined;
}
| undefined {
if (!this.outputFileName) {
return undefined;
}
const dimensions = sizeOf(this.outputFileName);
return {
height: dimensions.height,
width: dimensions.width,
};
}
/**
* Uploads a screen snippet
* @param webContents A browser window's web contents object
*/
private uploadSnippet(webContents: Electron.webContents) {
ipcMain.once('upload-snippet', async (_event, snipImage: string) => {
windowHandler.closeSnippingToolWindow();
if (snipImage) {
this.outputFileName = snipImage;
}
const {
message,
data,
type,
}: IScreenSnippet = await this.convertFileToData();
logger.info(
`screen-snippet-handler: Snippet captured! Sending data to SFE`,
);
webContents.send('screen-snippet-data', { message, data, type });
await this.verifyAndUpdateAlwaysOnTop();
});
}
}
const screenSnippet = new ScreenSnippet();

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,7 @@ enum styleNames {
}
const checkValidWindow = true;
const { ctWhitelist } = config.getConfigFields([ 'ctWhitelist' ]);
const { ctWhitelist } = config.getConfigFields(['ctWhitelist']);
// Network status check variables
const networkStatusCheckInterval = 10 * 1000;
@ -76,7 +76,7 @@ export const preventWindowNavigation = (browserWindow: BrowserWindow, isPopOutWi
if (browserWindow && windowExists(browserWindow)) {
const response = await electron.dialog.showMessageBox(browserWindow, {
type: 'warning',
buttons: [ 'OK' ],
buttons: ['OK'],
title: i18n.t('Not Allowed')(),
message: `${i18n.t(`Sorry, you are not allowed to access this website`)()} (${winUrl}), ${i18n.t('please contact your administrator for more details')()}`,
});
@ -317,7 +317,7 @@ export const getBounds = (winPos: ICustomRectangle | Electron.Rectangle | undefi
const displays = electron.screen.getAllDisplays();
for (let i = 0, len = displays.length; i < len; i++) {
const bounds = displays[ i ].bounds;
const bounds = displays[i].bounds;
logger.info('window-utils: getBounds, bounds: ' + JSON.stringify(bounds));
if (winPos.x >= bounds.x && winPos.y >= bounds.y &&
((winPos.x + winPos.width) <= (bounds.x + bounds.width)) &&
@ -659,11 +659,11 @@ export const monitorNetworkInterception = (url: string) => {
logger.info('window-utils: monitoring network interception for url', podUrl);
// Filter applied w.r.t pod url
const filter = { urls: [ podUrl + '*' ] };
const filter = { urls: [podUrl + '*'] };
if (mainWindow && windowExists(mainWindow)) {
isNetworkMonitorInitialized = true;
mainWindow.webContents.session.webRequest.onErrorOccurred(filter,async (details) => {
mainWindow.webContents.session.webRequest.onErrorOccurred(filter, async (details) => {
if (!mainWindow || !windowExists(mainWindow)) {
return;
}

View File

@ -158,7 +158,8 @@
"Erase": "Erase",
"Highlight": "Highlight",
"Pen": "Pen",
"Snipping Tool": "Snipping Tool"
"Snipping Tool": "Snipping Tool",
"Clear": "Clear"
},
"Select All": "Select All",
"Services": "Services",

View File

@ -158,7 +158,8 @@
"Erase": "Erase",
"Highlight": "Highlight",
"Pen": "Pen",
"Snipping Tool": "Snipping Tool"
"Snipping Tool": "Snipping Tool",
"Clear": "Clear"
},
"Select All": "Select All",
"Services": "Services",

View File

@ -31,7 +31,6 @@
"Build expired": "Construit expiré",
"Cancel": "Annuler",
"Certificate Error": "Erreur de certificat",
"Changing GPU settings requires Symphony to relaunch.": "La modification des paramètres du GPU nécessite la relance de Symphony.",
"Clear cache and Reload": "Vider le cache et rafraîchir Symphony",
"Close": "Fermer",
"ContextMenu": {
@ -159,7 +158,8 @@
"Erase": "Effacer",
"Highlight": "Surligner",
"Pen": "Stylo",
"Snipping Tool": "Outil Capture"
"Snipping Tool": "Outil Capture",
"Clear": "Claire"
},
"Select All": "Tout sélectionner",
"Services": "Services",

View File

@ -158,7 +158,8 @@
"Erase": "Effacer",
"Highlight": "Surligner",
"Pen": "Stylo",
"Snipping Tool": "Outil Capture"
"Snipping Tool": "Outil Capture",
"Clear": "Claire"
},
"Select All": "Tout sélectionner",
"Services": "Services",

View File

@ -158,7 +158,8 @@
"Erase": "消去",
"Highlight": "強調",
"Pen": "ペン",
"Snipping Tool": "切り取りツール"
"Snipping Tool": "切り取りツール",
"Clear": "晴れ"
},
"Select All": "すべてを選択",
"Services": "サービス",

View File

@ -158,7 +158,8 @@
"Erase": "消去",
"Highlight": "強調",
"Pen": "ペン",
"Snipping Tool": "切り取りツール"
"Snipping Tool": "切り取りツール",
"Clear": "晴れ"
},
"Select All": "すべてを選択",
"Services": "サービス",

View File

@ -0,0 +1,4 @@
<svg width="10" height="16" viewBox="0 0 10 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 12V16H10V10L5 0L0 10V16H2V12H8ZM7.5 10L5.54186 6H4.45814L2.5 10H7.5Z" fill="#525760"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 12V16H10V10L5 0L0 10V16H2V12H8ZM7.5 10L5.54186 6H4.45814L2.5 10H7.5Z" fill="white" fill-opacity="0.24"/>
</svg>

After

Width:  |  Height:  |  Size: 399 B

View File

@ -0,0 +1,4 @@
<svg width="10" height="16" viewBox="0 0 10 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 5V16H2V10H8V16H10V5C10 2.23858 7.76142 0 5 0C2.23858 0 0 2.23858 0 5ZM8 8V6H2V8H8Z" fill="#525760"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 5V16H2V10H8V16H10V5C10 2.23858 7.76142 0 5 0C2.23858 0 0 2.23858 0 5ZM8 8V6H2V8H8Z" fill="white" fill-opacity="0.24"/>
</svg>

After

Width:  |  Height:  |  Size: 427 B

View File

@ -0,0 +1,4 @@
<svg width="12" height="16" viewBox="0 0 12 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 1.5L2.95814 5H2.90657L0 10V16H2V12H10V16H12V10L8.62966 5.07316L8 0L3.5 1.5ZM10 10L7.75 7H3.75L2 10H10Z" fill="#525760"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 1.5L2.95814 5H2.90657L0 10V16H2V12H10V16H12V10L8.62966 5.07316L8 0L3.5 1.5ZM10 10L7.75 7H3.75L2 10H10Z" fill="white" fill-opacity="0.24"/>
</svg>

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.7071 6.70706C16.0976 6.31654 16.0976 5.68337 15.7071 5.29285L10.7071 0.29289C10.3166 -0.0976323 9.68341 -0.0976299 9.29289 0.292896C8.90237 0.683422 8.90237 1.31659 9.2929 1.70711L12.5858 5.00001H5.5C2.46243 5.00001 0 7.46245 0 10.5C0 13.5376 2.46243 16 5.5 16H7C7.55228 16 8 15.5523 8 15C8 14.4477 7.55228 14 7 14H5.5C3.567 14 2 12.433 2 10.5C2 8.56701 3.567 7.00001 5.5 7.00001H12.5858L9.29289 10.2929C8.90237 10.6834 8.90237 11.3166 9.2929 11.7071C9.68342 12.0976 10.3166 12.0976 10.7071 11.7071L15.7071 6.70706Z" fill="#525760"/>
<path d="M15.7071 6.70706C16.0976 6.31654 16.0976 5.68337 15.7071 5.29285L10.7071 0.29289C10.3166 -0.0976323 9.68341 -0.0976299 9.29289 0.292896C8.90237 0.683422 8.90237 1.31659 9.2929 1.70711L12.5858 5.00001H5.5C2.46243 5.00001 0 7.46245 0 10.5C0 13.5376 2.46243 16 5.5 16H7C7.55228 16 8 15.5523 8 15C8 14.4477 7.55228 14 7 14H5.5C3.567 14 2 12.433 2 10.5C2 8.56701 3.567 7.00001 5.5 7.00001H12.5858L9.29289 10.2929C8.90237 10.6834 8.90237 11.3166 9.2929 11.7071C9.68342 12.0976 10.3166 12.0976 10.7071 11.7071L15.7071 6.70706Z" fill="white" fill-opacity="0.72"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.292891 6.70706C-0.0976315 6.31654 -0.0976276 5.68337 0.292897 5.29285L5.2929 0.29289C5.68342 -0.0976323 6.31659 -0.0976299 6.70711 0.292896C7.09763 0.683422 7.09763 1.31659 6.7071 1.70711L3.41418 5.00001H10.5C13.5376 5.00001 16 7.46245 16 10.5C16 13.5376 13.5376 16 10.5 16H9C8.44772 16 8 15.5523 8 15C8 14.4477 8.44772 14 9 14H10.5C12.433 14 14 12.433 14 10.5C14 8.56701 12.433 7.00001 10.5 7.00001H3.41425L6.70711 10.2929C7.09763 10.6834 7.09763 11.3166 6.7071 11.7071C6.31658 12.0976 5.68341 12.0976 5.29289 11.7071L0.292891 6.70706Z" fill="#525760"/>
<path d="M0.292891 6.70706C-0.0976315 6.31654 -0.0976276 5.68337 0.292897 5.29285L5.2929 0.29289C5.68342 -0.0976323 6.31659 -0.0976299 6.70711 0.292896C7.09763 0.683422 7.09763 1.31659 6.7071 1.70711L3.41418 5.00001H10.5C13.5376 5.00001 16 7.46245 16 10.5C16 13.5376 13.5376 16 10.5 16H9C8.44772 16 8 15.5523 8 15C8 14.4477 8.44772 14 9 14H10.5C12.433 14 14 12.433 14 10.5C14 8.56701 12.433 7.00001 10.5 7.00001H3.41425L6.70711 10.2929C7.09763 10.6834 7.09763 11.3166 6.7071 11.7071C6.31658 12.0976 5.68341 12.0976 5.29289 11.7071L0.292891 6.70706Z" fill="white" fill-opacity="0.24"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -133,12 +133,12 @@ export default class AboutApp extends React.Component<{}, IState> {
public copy(): void {
const data = this.state;
if (data) {
remote.clipboard.write({ text: JSON.stringify(data, null, 4) }, 'clipboard' );
remote.clipboard.write({ text: JSON.stringify(data, null, 4) }, 'clipboard');
}
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object} { buildNumber, clientVersion, version }

View File

@ -58,13 +58,13 @@ export default class BasicAuth extends React.Component<{}, IState> {
<tr>
<td id='username-text'>{i18n.t('User name:', BASIC_AUTH_NAMESPACE)()}</td>
<td>
<input id='username' name='username' title='Username' onChange={this.eventHandlers.onChange} required/>
<input id='username' name='username' title='Username' onChange={this.eventHandlers.onChange} required />
</td>
</tr>
<tr>
<td id='password-text'>{i18n.t('Password:', BASIC_AUTH_NAMESPACE)()}</td>
<td>
<input name='password' id='password' type='password' title='Password' onChange={this.eventHandlers.onChange} required/>
<input name='password' id='password' type='password' title='Password' onChange={this.eventHandlers.onChange} required />
</td>
</tr>
</tbody>
@ -89,7 +89,7 @@ export default class BasicAuth extends React.Component<{}, IState> {
*/
private change(event): void {
this.setState({
[ (event.target as any).id ]: (event.target as any).value,
[(event.target as any).id]: (event.target as any).value,
} as IState);
}
@ -111,7 +111,7 @@ export default class BasicAuth extends React.Component<{}, IState> {
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object} { hostname, isValidCredentials }

View File

@ -50,11 +50,11 @@ export default class LoadingScreen extends React.Component<{}, IState> {
return (
<div className='LoadingScreen'>
<img className='LoadingScreen-logo' src={this.getImage(error)}/>
<img className='LoadingScreen-logo' src={this.getImage(error)} />
<span className='LoadingScreen-name'>{appName}</span>
<svg width='100%' height='100%' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 200' preserveAspectRatio='xMidYMid'>
<circle cx='50' cy='50' fill='none' ng-attr-stroke='{{config.color}}' stroke='#ffffff' strokeWidth='10' r='35' strokeDasharray='164.93361431346415 56.97787143782138' transform='rotate(59.6808 50 50)'>
<animateTransform attributeName='transform' type='rotate' calcMode='linear' values='0 50 50;360 50 50' keyTimes='0;1' dur='1s' begin='0s' repeatCount='indefinite'/>
<animateTransform attributeName='transform' type='rotate' calcMode='linear' values='0 50 50;360 50 50' keyTimes='0;1' dur='1s' begin='0s' repeatCount='indefinite' />
</circle>
</svg>
</div>
@ -78,7 +78,7 @@ export default class LoadingScreen extends React.Component<{}, IState> {
private renderErrorContent(error: string): JSX.Element {
return (
<div className='LoadingScreen'>
<img className='LoadingScreen-logo' src={this.getImage(error)}/>
<img className='LoadingScreen-logo' src={this.getImage(error)} />
<span className='LoadingScreen-name'>{i18n.t('Problem connecting to Symphony')()}</span>
<div id='error-code' className='LoadingScreen-error-code'>{error}</div>
<div>
@ -104,7 +104,7 @@ export default class LoadingScreen extends React.Component<{}, IState> {
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object}

View File

@ -4,7 +4,7 @@ import * as React from 'react';
import { i18n } from '../../common/i18n-preload';
const whiteColorRegExp = new RegExp(/^(?:white|#fff(?:fff)?|rgba?\(\s*255\s*,\s*255\s*,\s*255\s*(?:,\s*1\s*)?\))$/i);
const darkTheme = [ '#e23030', '#b5616a', '#ab8ead', '#ebc875', '#a3be77', '#58c6ff', '#ebab58' ];
const darkTheme = ['#e23030', '#b5616a', '#ab8ead', '#ebc875', '#a3be77', '#58c6ff', '#ebab58'];
type Theme = '' | 'light' | 'dark';
interface IState {
@ -84,22 +84,22 @@ export default class NotificationComp extends React.Component<{}, IState> {
onMouseEnter={this.eventHandlers.onMouseEnter(id)}
onMouseLeave={this.eventHandlers.onMouseLeave(id)}
>
{isExternal ? <div className='ext-border'/> : null}
{isExternal ? <div className='ext-border' /> : null}
<div className='logo-container'>
<div className='logo'>
<svg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M0 20C0 12.1746 0 8.26188 1.80534 5.41094C2.72586 3.95728 3.95728 2.72586 5.41094 1.80534C8.26188 0 12.1746 0 20 0C27.8254 0 31.7381 0 34.5891 1.80534C36.0427 2.72586 37.2741 3.95728 38.1947 5.41094C40 8.26188 40 12.1746 40 20C40 27.8254 40 31.7381 38.1947 34.5891C37.2741 36.0427 36.0427 37.2741 34.5891 38.1947C31.7381 40 27.8254 40 20 40C12.1746 40 8.26188 40 5.41094 38.1947C3.95728 37.2741 2.72586 36.0427 1.80534 34.5891C0 31.7381 0 27.8254 0 20Z' fill='#000028'/>
<path d='M0 20C0 12.1746 0 8.26188 1.80534 5.41094C2.72586 3.95728 3.95728 2.72586 5.41094 1.80534C8.26188 0 12.1746 0 20 0C27.8254 0 31.7381 0 34.5891 1.80534C36.0427 2.72586 37.2741 3.95728 38.1947 5.41094C40 8.26188 40 12.1746 40 20C40 27.8254 40 31.7381 38.1947 34.5891C37.2741 36.0427 36.0427 37.2741 34.5891 38.1947C31.7381 40 27.8254 40 20 40C12.1746 40 8.26188 40 5.41094 38.1947C3.95728 37.2741 2.72586 36.0427 1.80534 34.5891C0 31.7381 0 27.8254 0 20Z' fill='url(#paint0_linear)'/>
<path d='M28 17.1029V13.4094C28 12.6528 27.56 11.9425 26.8467 11.5534C25.7833 10.9728 23.48 10 20 10C16.52 10 14.2167 10.9728 13.1533 11.5565C12.44 11.9425 12 12.6528 12 13.4094V18.9559L24.6667 22.3529V24.8235C24.6667 25.1571 24.44 25.3918 24.0533 25.5678L20 27.4485L15.9233 25.5585C15.56 25.3918 15.3333 25.1571 15.3333 24.8235V22.9706L12 22.0441V24.8235C12 26.3491 12.9433 27.6462 14.4433 28.3225L20 31L25.5333 28.3349C27.0567 27.6462 28 26.3491 28 24.8235V20.1912L15.3333 16.7941V13.9746C16.24 13.57 17.78 13.0882 20 13.0882C22.22 13.0882 23.76 13.57 24.6667 13.9746V16.1765L28 17.1029Z' fill='#0098FF'/>
<path d='M28 17.1029V13.4094C28 12.6528 27.56 11.9425 26.8467 11.5534C25.7833 10.9728 23.48 10 20 10C16.52 10 14.2167 10.9728 13.1533 11.5565C12.44 11.9425 12 12.6528 12 13.4094V18.9559L24.6667 22.3529V24.8235C24.6667 25.1571 24.44 25.3918 24.0533 25.5678L20 27.4485L15.9233 25.5585C15.56 25.3918 15.3333 25.1571 15.3333 24.8235V22.9706L12 22.0441V24.8235C12 26.3491 12.9433 27.6462 14.4433 28.3225L20 31L25.5333 28.3349C27.0567 27.6462 28 26.3491 28 24.8235V20.1912L15.3333 16.7941V13.9746C16.24 13.57 17.78 13.0882 20 13.0882C22.22 13.0882 23.76 13.57 24.6667 13.9746V16.1765L28 17.1029Z' fill='url(#paint1_radial)'/>
<path d='M0 20C0 12.1746 0 8.26188 1.80534 5.41094C2.72586 3.95728 3.95728 2.72586 5.41094 1.80534C8.26188 0 12.1746 0 20 0C27.8254 0 31.7381 0 34.5891 1.80534C36.0427 2.72586 37.2741 3.95728 38.1947 5.41094C40 8.26188 40 12.1746 40 20C40 27.8254 40 31.7381 38.1947 34.5891C37.2741 36.0427 36.0427 37.2741 34.5891 38.1947C31.7381 40 27.8254 40 20 40C12.1746 40 8.26188 40 5.41094 38.1947C3.95728 37.2741 2.72586 36.0427 1.80534 34.5891C0 31.7381 0 27.8254 0 20Z' fill='#000028' />
<path d='M0 20C0 12.1746 0 8.26188 1.80534 5.41094C2.72586 3.95728 3.95728 2.72586 5.41094 1.80534C8.26188 0 12.1746 0 20 0C27.8254 0 31.7381 0 34.5891 1.80534C36.0427 2.72586 37.2741 3.95728 38.1947 5.41094C40 8.26188 40 12.1746 40 20C40 27.8254 40 31.7381 38.1947 34.5891C37.2741 36.0427 36.0427 37.2741 34.5891 38.1947C31.7381 40 27.8254 40 20 40C12.1746 40 8.26188 40 5.41094 38.1947C3.95728 37.2741 2.72586 36.0427 1.80534 34.5891C0 31.7381 0 27.8254 0 20Z' fill='url(#paint0_linear)' />
<path d='M28 17.1029V13.4094C28 12.6528 27.56 11.9425 26.8467 11.5534C25.7833 10.9728 23.48 10 20 10C16.52 10 14.2167 10.9728 13.1533 11.5565C12.44 11.9425 12 12.6528 12 13.4094V18.9559L24.6667 22.3529V24.8235C24.6667 25.1571 24.44 25.3918 24.0533 25.5678L20 27.4485L15.9233 25.5585C15.56 25.3918 15.3333 25.1571 15.3333 24.8235V22.9706L12 22.0441V24.8235C12 26.3491 12.9433 27.6462 14.4433 28.3225L20 31L25.5333 28.3349C27.0567 27.6462 28 26.3491 28 24.8235V20.1912L15.3333 16.7941V13.9746C16.24 13.57 17.78 13.0882 20 13.0882C22.22 13.0882 23.76 13.57 24.6667 13.9746V16.1765L28 17.1029Z' fill='#0098FF' />
<path d='M28 17.1029V13.4094C28 12.6528 27.56 11.9425 26.8467 11.5534C25.7833 10.9728 23.48 10 20 10C16.52 10 14.2167 10.9728 13.1533 11.5565C12.44 11.9425 12 12.6528 12 13.4094V18.9559L24.6667 22.3529V24.8235C24.6667 25.1571 24.44 25.3918 24.0533 25.5678L20 27.4485L15.9233 25.5585C15.56 25.3918 15.3333 25.1571 15.3333 24.8235V22.9706L12 22.0441V24.8235C12 26.3491 12.9433 27.6462 14.4433 28.3225L20 31L25.5333 28.3349C27.0567 27.6462 28 26.3491 28 24.8235V20.1912L15.3333 16.7941V13.9746C16.24 13.57 17.78 13.0882 20 13.0882C22.22 13.0882 23.76 13.57 24.6667 13.9746V16.1765L28 17.1029Z' fill='url(#paint1_radial)' />
<defs>
<linearGradient id='paint0_linear' x1='20' y1='0' x2='20' y2='40' gradientUnits='userSpaceOnUse'>
<stop stopColor='white' stopOpacity='0.2'/>
<stop offset='1' stopColor='white' stopOpacity='0'/>
<stop stopColor='white' stopOpacity='0.2' />
<stop offset='1' stopColor='white' stopOpacity='0' />
</linearGradient>
<radialGradient id='paint1_radial' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(20.0278 10) rotate(90) scale(14.2187 20.1481)'>
<stop stopColor='white' stopOpacity='0.4'/>
<stop offset='1' stopColor='white' stopOpacity='0'/>
<stop stopColor='white' stopOpacity='0.4' />
<stop offset='1' stopColor='white' stopOpacity='0' />
</radialGradient>
</defs>
</svg>
@ -115,8 +115,8 @@ export default class NotificationComp extends React.Component<{}, IState> {
{this.renderProfile(icon, image, title)}
<div className='close' title={i18n.t('Close')()} onClick={this.eventHandlers.onClose(id)}>
<svg width='8' height='8' viewBox='0 0 8 8' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M1.35355 0.646447C1.15829 0.451184 0.841709 0.451184 0.646447 0.646447C0.451184 0.841709 0.451184 1.15829 0.646447 1.35355L3.29289 4L0.646447 6.64645C0.451185 6.84171 0.451185 7.15829 0.646447 7.35356C0.841709 7.54882 1.15829 7.54882 1.35355 7.35356L4 4.70711L6.64645 7.35355C6.84171 7.54882 7.15829 7.54882 7.35355 7.35355C7.54882 7.15829 7.54882 6.84171 7.35355 6.64645L4.70711 4L7.35355 1.35356C7.54882 1.15829 7.54882 0.84171 7.35355 0.646448C7.15829 0.451186 6.84171 0.451186 6.64645 0.646448L4 3.29289L1.35355 0.646447Z' fill='#525760'/>
<path d='M1.35355 0.646447C1.15829 0.451184 0.841709 0.451184 0.646447 0.646447C0.451184 0.841709 0.451184 1.15829 0.646447 1.35355L3.29289 4L0.646447 6.64645C0.451185 6.84171 0.451185 7.15829 0.646447 7.35356C0.841709 7.54882 1.15829 7.54882 1.35355 7.35356L4 4.70711L6.64645 7.35355C6.84171 7.54882 7.15829 7.54882 7.35355 7.35355C7.54882 7.15829 7.54882 6.84171 7.35355 6.64645L4.70711 4L7.35355 1.35356C7.54882 1.15829 7.54882 0.84171 7.35355 0.646448C7.15829 0.451186 6.84171 0.451186 6.64645 0.646448L4 3.29289L1.35355 0.646447Z' fill='white' fill-opacity='0.96'/>
<path d='M1.35355 0.646447C1.15829 0.451184 0.841709 0.451184 0.646447 0.646447C0.451184 0.841709 0.451184 1.15829 0.646447 1.35355L3.29289 4L0.646447 6.64645C0.451185 6.84171 0.451185 7.15829 0.646447 7.35356C0.841709 7.54882 1.15829 7.54882 1.35355 7.35356L4 4.70711L6.64645 7.35355C6.84171 7.54882 7.15829 7.54882 7.35355 7.35355C7.54882 7.15829 7.54882 6.84171 7.35355 6.64645L4.70711 4L7.35355 1.35356C7.54882 1.15829 7.54882 0.84171 7.35355 0.646448C7.15829 0.451186 6.84171 0.451186 6.64645 0.646448L4 3.29289L1.35355 0.646447Z' fill='#525760' />
<path d='M1.35355 0.646447C1.15829 0.451184 0.841709 0.451184 0.646447 0.646447C0.451184 0.841709 0.451184 1.15829 0.646447 1.35355L3.29289 4L0.646447 6.64645C0.451185 6.84171 0.451185 7.15829 0.646447 7.35356C0.841709 7.54882 1.15829 7.54882 1.35355 7.35356L4 4.70711L6.64645 7.35355C6.84171 7.54882 7.15829 7.54882 7.35355 7.35355C7.54882 7.15829 7.54882 6.84171 7.35355 6.64645L4.70711 4L7.35355 1.35356C7.54882 1.15829 7.54882 0.84171 7.35355 0.646448C7.15829 0.451186 6.84171 0.451186 6.64645 0.646448L4 3.29289L1.35355 0.646447Z' fill='white' fill-opacity='0.96' />
</svg>
</div>
</div>
@ -134,10 +134,10 @@ export default class NotificationComp extends React.Component<{}, IState> {
return (
<div className='ext-badge-container'>
<svg width='32' height='16' viewBox='0 0 32 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='32' height='16' rx='8' fill='#F6B202'/>
<rect width='32' height='16' rx='8' fill='white' fill-opacity='0.24'/>
<path d='M11.4414 13H6.72461V4.59766H11.2539V5.78125H8.11914V8.16016H11.0078V9.33789H8.11914V11.8223H11.4414V13ZM19.3574 13H17.6875L15.9648 9.91797C15.9141 9.82422 15.8574 9.69141 15.7949 9.51953H15.7715C15.7363 9.60547 15.6777 9.73828 15.5957 9.91797L13.8203 13H12.1387L14.8926 8.77539L12.3613 4.59766H14.0664L15.584 7.43359C15.6816 7.62109 15.7695 7.80859 15.8477 7.99609H15.8652C15.9785 7.75 16.0762 7.55469 16.1582 7.41016L17.7344 4.59766H19.3047L16.7148 8.76367L19.3574 13ZM26.1191 5.78125H23.7051V13H22.3105V5.78125H19.9023V4.59766H26.1191V5.78125Z' fill='#525760'/>
<path d='M11.4414 13H6.72461V4.59766H11.2539V5.78125H8.11914V8.16016H11.0078V9.33789H8.11914V11.8223H11.4414V13ZM19.3574 13H17.6875L15.9648 9.91797C15.9141 9.82422 15.8574 9.69141 15.7949 9.51953H15.7715C15.7363 9.60547 15.6777 9.73828 15.5957 9.91797L13.8203 13H12.1387L14.8926 8.77539L12.3613 4.59766H14.0664L15.584 7.43359C15.6816 7.62109 15.7695 7.80859 15.8477 7.99609H15.8652C15.9785 7.75 16.0762 7.55469 16.1582 7.41016L17.7344 4.59766H19.3047L16.7148 8.76367L19.3574 13ZM26.1191 5.78125H23.7051V13H22.3105V5.78125H19.9023V4.59766H26.1191V5.78125Z' fill='black' fill-opacity='0.24'/>
<rect width='32' height='16' rx='8' fill='#F6B202' />
<rect width='32' height='16' rx='8' fill='white' fill-opacity='0.24' />
<path d='M11.4414 13H6.72461V4.59766H11.2539V5.78125H8.11914V8.16016H11.0078V9.33789H8.11914V11.8223H11.4414V13ZM19.3574 13H17.6875L15.9648 9.91797C15.9141 9.82422 15.8574 9.69141 15.7949 9.51953H15.7715C15.7363 9.60547 15.6777 9.73828 15.5957 9.91797L13.8203 13H12.1387L14.8926 8.77539L12.3613 4.59766H14.0664L15.584 7.43359C15.6816 7.62109 15.7695 7.80859 15.8477 7.99609H15.8652C15.9785 7.75 16.0762 7.55469 16.1582 7.41016L17.7344 4.59766H19.3047L16.7148 8.76367L19.3574 13ZM26.1191 5.78125H23.7051V13H22.3105V5.78125H19.9023V4.59766H26.1191V5.78125Z' fill='#525760' />
<path d='M11.4414 13H6.72461V4.59766H11.2539V5.78125H8.11914V8.16016H11.0078V9.33789H8.11914V11.8223H11.4414V13ZM19.3574 13H17.6875L15.9648 9.91797C15.9141 9.82422 15.8574 9.69141 15.7949 9.51953H15.7715C15.7363 9.60547 15.6777 9.73828 15.5957 9.91797L13.8203 13H12.1387L14.8926 8.77539L12.3613 4.59766H14.0664L15.584 7.43359C15.6816 7.62109 15.7695 7.80859 15.8477 7.99609H15.8652C15.9785 7.75 16.0762 7.55469 16.1582 7.41016L17.7344 4.59766H19.3047L16.7148 8.76367L19.3574 13ZM26.1191 5.78125H23.7051V13H22.3105V5.78125H19.9023V4.59766H26.1191V5.78125Z' fill='black' fill-opacity='0.24' />
</svg>
</div>
);
@ -155,7 +155,7 @@ export default class NotificationComp extends React.Component<{}, IState> {
if (icon || image) {
return (
<div className='user-profile-pic-container'>
<img src={image || icon || '../renderer/assets/symphony-default-profile-pic.png'} className='user-profile-pic' alt='user profile picture'/>
<img src={image || icon || '../renderer/assets/symphony-default-profile-pic.png'} className='user-profile-pic' alt='user profile picture' />
</div>
);
}
@ -223,7 +223,7 @@ export default class NotificationComp extends React.Component<{}, IState> {
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object}

View File

@ -118,7 +118,7 @@ export default class NotificationSettings extends React.Component<{}, IState> {
type='radio'
name='position'
checked={this.state.position === id}
value={id}/>
value={id} />
</div>
);
}
@ -174,7 +174,7 @@ export default class NotificationSettings extends React.Component<{}, IState> {
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object} { buildNumber, clientVersion, version }

View File

@ -148,7 +148,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
id={source.id}
onClick={() => this.eventHandlers.onSelect(source)}>
<div className='ScreenPicker-screen-section-box'>
<img className='ScreenPicker-img-wrapper' src={source.thumbnail as any} alt='thumbnail image'/>
<img className='ScreenPicker-img-wrapper' src={source.thumbnail as any} alt='thumbnail image' />
</div>
<div className='ScreenPicker-screen-source-title'>{screenName}</div>
</div>,
@ -161,7 +161,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
id={source.id}
onClick={() => this.eventHandlers.onSelect(source)}>
<div className='ScreenPicker-screen-section-box'>
<img className='ScreenPicker-img-wrapper' src={source.thumbnail as any} alt='thumbnail image'/>
<img className='ScreenPicker-img-wrapper' src={source.thumbnail as any} alt='thumbnail image' />
</div>
<div className='ScreenPicker-screen-source-title'>{source.name}</div>
</div>,
@ -372,14 +372,14 @@ export default class ScreenPicker extends React.Component<{}, IState> {
// Find the next item to be selected
const nextIndex = (this.currentIndex + index + sources.length) % sources.length;
if (sources[ nextIndex ] && sources[ nextIndex ].id) {
if (sources[nextIndex] && sources[nextIndex].id) {
// Updates the selected source
this.setState({ selectedSource: sources[ nextIndex ] });
this.setState({ selectedSource: sources[nextIndex] });
}
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object}

View File

@ -40,7 +40,7 @@ export default class ScreenSharingIndicator extends React.Component<{}, IState>
return (
<div className={classNames('ScreenSharingIndicator', { mac: isMac })}>
<span className='text-label'>{(i18n.t(`You are sharing your screen on {appName}`, namespace)({ appName: remote.app.getName()})).replace(remote.app.getName(), '')}
<span className='text-label'>{(i18n.t(`You are sharing your screen on {appName}`, namespace)({ appName: remote.app.getName() })).replace(remote.app.getName(), '')}
<span className='text-label2'>&nbsp;{remote.app.getName()}</span>
</span>
<span className='buttons'>
@ -82,7 +82,7 @@ export default class ScreenSharingIndicator extends React.Component<{}, IState>
}
/**
* Sets the About app state
* Sets the component state
*
* @param _event
* @param data {Object} { buildNumber, clientVersion, version }

View File

@ -0,0 +1,124 @@
import { ipcRenderer } from 'electron';
import * as React from 'react';
import { i18n } from '../../common/i18n-preload';
const { useState, useEffect } = React;
interface IProps {
drawEnabled: boolean;
highlightEnabled: boolean;
eraseEnabled: boolean;
}
const SNIPPING_TOOL_NAMESPACE = 'ScreenSnippet';
const SnippingTool: React.FunctionComponent<IProps> = ({drawEnabled, highlightEnabled, eraseEnabled}) => {
const [screenSnippet, setScreenSnippet] = useState('Screen-Snippet');
const [imageDimensions, setImageDimensions] = useState({height: 600, width: 800});
const getSnipImageData = (_event, {snipImage, height, width}) => {
setScreenSnippet(snipImage);
setImageDimensions({height, width});
};
ipcRenderer.on('snipping-tool-data', getSnipImageData);
useEffect(() => {
return () => {
ipcRenderer.removeListener('snipping-tool-data', getSnipImageData);
};
}, []);
const usePen = () => {
// setTool("pen");
// setShouldRenderPenColorPicker(shouldRenderPenColorPicker => !shouldRenderPenColorPicker);
// setShouldRenderHighlightColorPicker(false);
};
const useHighlight = () => {
// setTool("highlight");
// setShouldRenderHighlightColorPicker(shouldRenderHighlightColorPicker => !shouldRenderHighlightColorPicker);
// setShouldRenderPenColorPicker(false);
};
const useEraser = () => {
// setTool("eraser");
};
const clear = () => {
// const updPaths = [...paths];
// updPaths.map((p) => {
// p.shouldShow = false;
// return p;
// });
// setPaths(updPaths);
};
const done = () => {
ipcRenderer.send('upload-snippet', screenSnippet);
};
return (
<div className='SnippingTool' lang={i18n.getLocale()}>
<header>
<div className='DrawActions'>
<button
className={
drawEnabled ? 'ActionButtonSelected' : 'ActionButton'
}
onClick={usePen}
>
<img src='../renderer/assets/snip-draw.svg' />
</button>
<button
className={
highlightEnabled
? 'ActionButtonSelected'
: 'ActionButton'
}
onClick={useHighlight}
>
<img src='../renderer/assets/snip-highlight.svg' />
</button>
<button
className={
eraseEnabled
? 'ActionButtonSelected'
: 'ActionButton'
}
onClick={useEraser}
>
<img src='../renderer/assets/snip-erase.svg' />
</button>
</div>
<div className='ClearActions'>
<button
className='ClearButton'
onClick={clear}
>
{i18n.t('Clear', SNIPPING_TOOL_NAMESPACE)()}
</button>
</div>
</header>
<main>
<img
src={screenSnippet}
width={imageDimensions.width}
height={imageDimensions.height}
className='SnippetImage'
alt={i18n.t('Screen snippet', SNIPPING_TOOL_NAMESPACE)()}
/>
</main>
<footer>
<button onClick={done}>
{i18n.t('Done', SNIPPING_TOOL_NAMESPACE)()}
</button>
</footer>
</div>
);
};
export default SnippingTool;

View File

@ -10,6 +10,7 @@ import NotificationSettings from './components/notification-settings';
import ScreenPicker from './components/screen-picker';
import ScreenSharingFrame from './components/screen-sharing-frame';
import ScreenSharingIndicator from './components/screen-sharing-indicator';
import SnippingTool from './components/snipping-tool';
import Welcome from './components/welcome';
const enum components {
@ -21,6 +22,7 @@ const enum components {
notification = 'notification-comp',
notificationSettings = 'notification-settings',
welcome = 'welcome',
snippingTool = 'snipping-tool',
}
const loadStyle = (style) => {
@ -47,26 +49,31 @@ const load = () => {
break;
case components.screenPicker:
loadStyle(components.screenPicker);
document.title = 'Screen Picker - Symphony';
document.title = i18n.t('Screen Picker - Symphony')();
component = ScreenPicker;
break;
case components.screenSharingIndicator:
loadStyle(components.screenSharingIndicator);
document.title = 'Screen Sharing Indicator - Symphony';
document.title = i18n.t('Screen Sharing Indicator - Symphony')();
component = ScreenSharingIndicator;
break;
case components.screenSharingFrame:
loadStyle(components.screenSharingFrame);
component = ScreenSharingFrame;
break;
case components.snippingTool:
loadStyle(components.snippingTool);
document.title = i18n.t('Symphony')();
component = SnippingTool;
break;
case components.basicAuth:
loadStyle(components.basicAuth);
document.title = 'Basic Authentication - Symphony';
document.title = i18n.t('Basic Authentication - Symphony')();
component = BasicAuth;
break;
case components.notification:
loadStyle(components.notification);
document.title = 'Notification - Symphony';
document.title = i18n.t('Notification - Symphony')();
component = NotificationComp;
break;
case components.notificationSettings:

View File

@ -0,0 +1,182 @@
@import 'theme';
@white: rgb(255, 255, 255, 1);
@version-text-color: rgb(47, 47, 47, 1);
@text-padding: 10px;
::-webkit-scrollbar {
display: none;
}
body {
background-color: white;
margin: 0;
height: 100%;
width: 100%;
}
.SnippingTool:lang(ja-JP) {
font-family: @font-family-ja;
h4 {
margin: 3px 0;
}
.SnippingTool-symphony-section {
padding-left: 10px;
}
}
.SnippingTool:lang(fr-FR) {
.SnippingTool-symphony-section {
padding-left: 10px;
}
}
.SnippingTool {
display: flex;
flex-direction: column;
font-family: @font-family;
header {
display: flex;
flex-direction: row;
justify-content: center;
text-align: center;
padding: 4px 0;
max-height: 48px;
.ActionButton {
margin-left: 24px;
background: white;
border-radius: 4px;
cursor: pointer;
padding: 4px 0;
border: none;
&:first-child {
margin-left: 0;
}
img {
width: 24px;
height: 24px;
}
}
.ActionButtonSelected {
margin-left: 24px;
background: white;
border-radius: 4px;
cursor: pointer;
padding: 4px 0;
border: 2px solid #008eff;
box-sizing: border-box;
border-radius: 4px;
&:first-child {
margin-left: 0;
}
img {
width: 24px;
height: 24px;
}
}
.ClearButton {
display: block;
padding: 4px 10px;
border: 2px solid #7c7f86;
border-radius: 16px;
color: #7c7f86;
font-weight: 600;
font-size: 12px;
line-height: 16px;
margin-left: 24px;
text-transform: uppercase;
cursor: pointer;
background-color: #ffffff;
}
.DrawActions {
display: flex;
flex-direction: row;
justify-content: center;
text-align: center;
}
.ClearActions {
display: flex;
flex-direction: row;
justify-content: center;
text-align: center;
position: absolute;
right: 24px;
align-items: center;
.ActionButton {
img {
width: 16px;
}
}
}
}
main {
text-align: center;
margin: 0;
background: linear-gradient(
0deg,
rgba(255, 255, 255, 0.96),
rgba(255, 255, 255, 0.96)
),
#525760;
.SnippetImage {
width: 100%;
height: 100%;
object-fit: contain;
}
}
footer {
display: flex;
justify-content: flex-end;
padding: 2px 32px 4px 0;
max-height: 72px;
button {
box-shadow: none;
border: none;
border-radius: 16px;
font-size: 0.75rem;
font-weight: 600;
text-align: center;
padding: 8px 24px;
display: inline-block;
text-decoration: none;
line-height: 16px;
background-color: #008eff;
color: rgba(255, 255, 255, 0.96);
cursor: pointer;
text-transform: uppercase;
margin: 10px 30px 4px 0;
&:focus {
box-shadow: 0 0 10px rgba(61, 162, 253, 1);
outline: none;
}
}
}
@media only screen and (max-width: 400px) {
header {
.ClearActions {
position: relative;
right: auto;
margin-left: 24px;
}
}
}
}

View File

@ -1,5 +1,6 @@
@font-family: "Segoe UI", "Helvetica Neue", "Verdana", "Arial", sans-serif;
@font-family-ja: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Meiryo, " Pゴシック", "MS PGothic", sans-serif;
@text-color-primary: #FFFFFF;
@text-color-primary-dark: #2F3237;
@text-color-secondary: #6A707C;
@font-family: 'Segoe UI', 'Helvetica Neue', 'Verdana', 'Arial', sans-serif;
@font-family-ja: 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo,
' Pゴシック', 'MS PGothic', sans-serif;
@text-color-primary: #ffffff;
@text-color-primary-dark: #2f3237;
@text-color-secondary: #6a707c;

View File

@ -26,7 +26,7 @@
"no-trailing-whitespace": true,
"no-duplicate-variable": true,
"no-var-keyword": true,
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"],
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
"no-empty": true,
"no-unused-expression": true,
"no-use-before-declare": true,