1
0
mirror of https://github.com/pavlobu/deskreen.git synced 2025-06-01 23:30:09 -07:00

Merge pull request #12 from pavlobu/client-ui

Client and Host UI improvements
This commit is contained in:
Pavlo Buidenkov 2020-12-09 14:18:26 +00:00 committed by GitHub
commit 6230424192
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 1918 additions and 578 deletions

View File

@ -12,12 +12,15 @@
"@types/react": "^16.9.0", "@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0", "@types/react-dom": "^16.9.0",
"@types/ua-parser-js": "^0.7.33", "@types/ua-parser-js": "^0.7.33",
"i18next": "^19.8.4",
"i18next-http-backend": "^1.0.21",
"jest-sonar-reporter": "^2.0.0", "jest-sonar-reporter": "^2.0.0",
"node-forge": "^0.9.1", "node-forge": "^0.9.1",
"pixelmatch": "^5.2.1", "pixelmatch": "^5.2.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-flexbox-grid": "^2.1.2", "react-flexbox-grid": "^2.1.2",
"react-i18next": "^11.7.4",
"react-player": "^2.6.2", "react-player": "^2.6.2",
"react-reveal": "^1.2.2", "react-reveal": "^1.2.2",
"react-scripts": "3.4.3", "react-scripts": "3.4.3",

View File

@ -0,0 +1,4 @@
{
"Waiting for user to click ALLOW button on screen sharing device...": "Waiting for user to click ALLOW button on screen sharing device...",
"Wating for user to select source to share from screen sharing device...": "Wating for user to select source to share from screen sharing device..."
}

View File

@ -0,0 +1,4 @@
{
"Waiting for user to click ALLOW button on screen sharing device...": "Жду когда пользователь нажмет кнопку РАЗРЕШИТЬ доступ к экрану компьютера...",
"Wating for user to select source to share from screen sharing device...": "Жду когда пользователь выберет весь экран или окно приложения для демонстрации..."
}

View File

@ -0,0 +1,4 @@
{
"Waiting for user to click ALLOW button on screen sharing device...": "Чекаэмо коли користувач натисне кнопку ДОЗВОЛИТИ доступ до екрану комп'ютера...",
"Wating for user to select source to share from screen sharing device...": "Чекаю коли користувач вибере весь екран або вікно програми для демонстрації..."
}

View File

@ -1,12 +1,20 @@
import React, { useEffect, useState, useRef, useContext } from 'react'; import React, {
import { H3, Button } from '@blueprintjs/core'; useEffect,
useState,
useRef,
useContext,
useCallback,
} from 'react';
import { useTranslation } from 'react-i18next';
import i18n from './config/i18n';
import { H3, Position, Toaster } from '@blueprintjs/core';
import { Grid, Row, Col } from 'react-flexbox-grid'; import { Grid, Row, Col } from 'react-flexbox-grid';
import { findDOMNode } from 'react-dom'; import { findDOMNode } from 'react-dom';
import ReactPlayer from 'react-player'; import ReactPlayer from 'react-player';
import screenfull from 'screenfull'; import screenfull from 'screenfull';
import Crypto from './utils/crypto'; import Crypto from './utils/crypto';
import './App.css'; import './App.css';
import LocalTestPeer from './features/PeerConnection'; import PeerConnection from './features/PeerConnection';
import VideoAutoQualityOptimizer from './features/VideoAutoQualityOptimizer'; import VideoAutoQualityOptimizer from './features/VideoAutoQualityOptimizer';
import ConnectingIndicator from './components/ConnectingIndicator'; import ConnectingIndicator from './components/ConnectingIndicator';
import MyDeviceInfoCard from './components/MyDeviceInfoCard'; import MyDeviceInfoCard from './components/MyDeviceInfoCard';
@ -15,24 +23,34 @@ import {
LIGHT_UI_BACKGROUND, LIGHT_UI_BACKGROUND,
} from './constants/styleConstants'; } from './constants/styleConstants';
import { AppContext } from './providers/AppContextProvider'; import { AppContext } from './providers/AppContextProvider';
import ToggleDarkModeSwitch from './components/ToggleDarkModeSwitch'; import PlayerControlPanel from './components/PlayerControlPanel';
import { VideoQuality } from './features/PeerConnection/VideoQualityEnum';
import { REACT_PLAYER_WRAPPER_ID } from './constants/appConstants';
import { TFunction } from 'i18next';
import ErrorDialog from './components/ErrorDialog';
import { ErrorMessage } from './components/ErrorDialog/ErrorMessageEnum';
const Fade = require('react-reveal/Fade'); const Fade = require('react-reveal/Fade');
const Slide = require('react-reveal/Slide'); const Slide = require('react-reveal/Slide');
function getPromptContent(step: number) { function getPromptContent(step: number, t: TFunction) {
switch (step) { switch (step) {
case 1: case 1:
return ( return (
<H3>Waiting for user to click "Allow" on screen sharing device...</H3> <H3>
{t(
'Waiting for user to click ALLOW button on screen sharing device...'
)}
</H3>
); );
case 2: case 2:
return <H3>Connected!</H3>; return <H3>Connected!</H3>;
case 3: case 3:
return ( return (
<H3> <H3>
Wating for user to select source to share from screen sharing {t(
device... 'Wating for user to select source to share from screen sharing device...'
)}
</H3> </H3>
); );
default: default:
@ -41,10 +59,23 @@ function getPromptContent(step: number) {
} }
function App() { function App() {
const { t } = useTranslation();
const { isDarkTheme, setIsDarkThemeHook } = useContext(AppContext); const { isDarkTheme, setIsDarkThemeHook } = useContext(AppContext);
const [isErrorDialogOpen, setIsErrorDialogOpen] = useState(false);
const [toaster, setToaster] = useState<undefined | Toaster>();
const refHandlers = {
toaster: (ref: Toaster) => {
setToaster(ref);
},
};
const player = useRef(null); const player = useRef(null);
const [promptStep, setPromptStep] = useState(1); const [promptStep, setPromptStep] = useState(1);
const [dialogErrorMessage, setDialogErrorMessage] = useState<ErrorMessage>(
ErrorMessage.UNKNOWN_ERROR
);
const [connectionIconType, setConnectionIconType] = useState< const [connectionIconType, setConnectionIconType] = useState<
'feed' | 'feed-subscribed' 'feed' | 'feed-subscribed'
>('feed'); >('feed');
@ -56,43 +87,73 @@ function App() {
}); });
const [playing, setPlaying] = useState(true); const [playing, setPlaying] = useState(true);
const [isFullScreenOn, setIsFullScreenOn] = useState(false);
const [url, setUrl] = useState(); const [url, setUrl] = useState();
const [screenSharingSourceType, setScreenSharingSourceType] = useState<
'screen' | 'window'
>('screen');
const [isWithControls, setIsWithControls] = useState(!screenfull.isEnabled); const [isWithControls, setIsWithControls] = useState(!screenfull.isEnabled);
const [isShownTextPrompt, setIsShownTextPrompt] = useState(false); const [isShownTextPrompt, setIsShownTextPrompt] = useState(false);
const [isShownSpinnerIcon, setIsShownSpinnerIcon] = useState(false); const [isShownSpinnerIcon, setIsShownSpinnerIcon] = useState(false);
const [spinnerIconType, setSpinnerIconType] = useState< const [spinnerIconType, setSpinnerIconType] = useState<
'desktop' | 'application' 'desktop' | 'application'
>('desktop'); >('desktop');
const [videoQuality, setVideoQuality] = useState<VideoQuality>(
VideoQuality.Q_AUTO
);
const [peer, setPeer] = useState<undefined | PeerConnection>();
const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
};
useEffect(() => {
if (!peer) return;
if (!peer.isStreamStarted) return;
peer.setVideoQuality(videoQuality);
}, [videoQuality, peer]);
useEffect(() => { useEffect(() => {
document.body.style.backgroundColor = isDarkTheme document.body.style.backgroundColor = isDarkTheme
? DARK_UI_BACKGROUND ? DARK_UI_BACKGROUND
: LIGHT_UI_BACKGROUND; : LIGHT_UI_BACKGROUND;
}, [isDarkTheme]);
const peer = new LocalTestPeer( useEffect(() => {
setUrl, if (!peer) {
new Crypto(), const _peer = new PeerConnection(
new VideoAutoQualityOptimizer(), setUrl,
setMyDeviceDetails, new Crypto(),
() => { new VideoAutoQualityOptimizer(),
setConnectionIconType('feed-subscribed'); isDarkTheme,
setMyDeviceDetails,
() => {
setConnectionIconType('feed-subscribed');
setIsShownTextPrompt(false);
setIsShownTextPrompt(true);
setPromptStep(2);
setTimeout(() => {
setIsShownTextPrompt(false); setIsShownTextPrompt(false);
setIsShownTextPrompt(true); setIsShownTextPrompt(true);
setPromptStep(3); setPromptStep(2);
}, 2000);
}
);
setTimeout(() => { setTimeout(() => {
setIsShownTextPrompt(true); setIsShownTextPrompt(false);
}, 100); setIsShownTextPrompt(true);
}, []); setPromptStep(3);
}, 2000);
},
setScreenSharingSourceType,
setIsDarkThemeHook,
changeLanguage,
setDialogErrorMessage,
setIsErrorDialogOpen
);
setPeer(_peer);
setTimeout(() => {
setIsShownTextPrompt(true);
}, 100);
}
}, [setIsDarkThemeHook, isDarkTheme, peer]);
useEffect(() => { useEffect(() => {
// infinite use effect // infinite use effect
@ -102,22 +163,11 @@ function App() {
spinnerIconType === 'desktop' ? 'application' : 'desktop' spinnerIconType === 'desktop' ? 'application' : 'desktop'
); );
}, 1500); }, 1500);
}, [isShownSpinnerIcon]); }, [isShownSpinnerIcon, spinnerIconType]);
const handleClickFullscreen = () => { const handlePlayPause = useCallback(() => {
// @ts-ignore Property 'request' does not exist on type '{ isEnabled: false; }'.
screenfull.request(findDOMNode(player.current));
};
const handlePlayPause = () => {
setPlaying(!playing); setPlaying(!playing);
}; }, [playing]);
useEffect(() => {
document.body.style.backgroundColor = isDarkTheme
? DARK_UI_BACKGROUND
: LIGHT_UI_BACKGROUND;
}, [isDarkTheme]);
useEffect(() => { useEffect(() => {
if (url !== undefined) { if (url !== undefined) {
@ -157,7 +207,6 @@ function App() {
: LIGHT_UI_BACKGROUND, : LIGHT_UI_BACKGROUND,
}} }}
> >
<ToggleDarkModeSwitch />
<Row <Row
bottom="xs" bottom="xs"
style={{ style={{
@ -189,7 +238,7 @@ function App() {
style={{ width: '100%' }} style={{ width: '100%' }}
> >
<div id="prompt-text" style={{ fontSize: '20px' }}> <div id="prompt-text" style={{ fontSize: '20px' }}>
{getPromptContent(promptStep)} {getPromptContent(promptStep, t)}
</div> </div>
</Fade> </Fade>
</div> </div>
@ -221,22 +270,22 @@ function App() {
height: '100vh', height: '100vh',
}} }}
> >
<Button <PlayerControlPanel
onClick={() => setIsWithControls(true)} onSwitchChangedCallback={(isEnabled) => setIsWithControls(isEnabled)}
onTouchStart={() => setIsWithControls(true)} isDefaultPlayerTurnedOn={isWithControls}
> handleClickFullscreen={() => {
Help! I'm having troubles to zoom in on my device if (!screenfull.isEnabled) return;
</Button> // @ts-ignore Property 'request' does not exist on type '{ isEnabled: false; }'.
<Button onClick={handlePlayPause} onTouchStart={handlePlayPause}> screenfull.request(findDOMNode(player.current));
PLAY! setIsFullScreenOn(!isFullScreenOn);
</Button> }}
<Button handleClickPlayPause={handlePlayPause}
onClick={handleClickFullscreen} isPlaying={playing}
onTouchStart={handleClickFullscreen} setVideoQuality={setVideoQuality}
> selectedVideoQuality={videoQuality}
ENTER FULL SCREEN screenSharingSourceType={screenSharingSourceType}
</Button> toaster={toaster}
<ToggleDarkModeSwitch /> />
<div <div
id="video-container" id="video-container"
style={{ style={{
@ -245,6 +294,7 @@ function App() {
}} }}
> >
<div <div
id="player-wrapper-id"
className="player-wrapper" className="player-wrapper"
style={{ style={{
position: 'relative', position: 'relative',
@ -253,19 +303,12 @@ function App() {
> >
<ReactPlayer <ReactPlayer
ref={player} ref={player}
id="video-local-test-peer-sees" id={REACT_PLAYER_WRAPPER_ID}
playing={playing} playing={playing}
playsinline={true} playsinline={true}
controls={isWithControls} controls={isWithControls}
muted={true} muted={true}
// url={mergedStream}
url={(url as unknown) as MediaStream} url={(url as unknown) as MediaStream}
// width={url.getVideoTracks()[0].getSettings().width}
// height={url.getVideoTracks()[0].getSettings().height}
// onPlay={setProperCanvasWidth}
// wrapper="div"
// width={window.screen.width}
// height={window.screen.height}
width="100%" width="100%"
height="100%" height="100%"
style={{ style={{
@ -278,6 +321,11 @@ function App() {
<canvas id="comparison-canvas" style={{ display: 'none' }}></canvas> <canvas id="comparison-canvas" style={{ display: 'none' }}></canvas>
</div> </div>
</div> </div>
<Toaster ref={refHandlers.toaster} position={Position.TOP_LEFT} />
<ErrorDialog
errorMessage={dialogErrorMessage}
isOpen={isErrorDialogOpen}
/>
</Grid> </Grid>
); );
} }

View File

@ -0,0 +1,6 @@
export enum ErrorMessage {
UNKNOWN_ERROR = 'An unknonw error uccured.',
DENY_TO_CONNECT = 'You were not allowed to connect.',
DICONNECTED = 'You were disconnected.',
NOT_ALLOWED = 'You were not allowed to connect.',
}

View File

@ -0,0 +1,3 @@
.error-dialog-backdrop {
backdrop-filter: blur(2px);
}

View File

@ -0,0 +1,70 @@
import React from 'react';
import {
Classes,
Dialog,
Divider,
H1,
H2,
H3,
Icon,
} from '@blueprintjs/core';
import { Col, Row } from 'react-flexbox-grid';
import './index.css';
import { ErrorMessage } from './ErrorMessageEnum';
interface ErrorDialogProps {
isOpen: boolean;
errorMessage: ErrorMessage;
}
function ErrorDialog(props: ErrorDialogProps) {
const { errorMessage, isOpen } = props;
return (
<Dialog
className="error-dialog"
autoFocus
canEscapeKeyClose
canOutsideClickClose
enforceFocus
isOpen={isOpen}
usePortal
style={{
width: '90%',
maxWidth: '1200px',
}}
backdropClassName="error-dialog-backdrop"
>
<Row center="xs" style={{ marginTop: '10px' }}>
<Col xs={12}>
<H3 className={Classes.TEXT_MUTED}>Descreen Error Dialog</H3>
</Col>
</Row>
<Row middle="xs" center="xs" style={{ padding: '20px', width: '100%' }}>
<Col xs={12} md={10} lg={6}>
<Row middle="xs" center="xs">
<Col xs={1}>
<Icon icon="error" iconSize={52} color="#8A9BA8" />
</Col>
<Col xs={11}>
<H1
className={Classes.TEXT_DISABLED}
style={{ marginBottom: '0px' }}
>
Something wrong happened :(
</H1>
</Col>
</Row>
</Col>
</Row>
<Divider />
<div className={Classes.DIALOG_BODY}>
<H3 className={Classes.TEXT_MUTED}>{errorMessage}</H3>
<Divider />
<H2>You may close this browser window then try to connect again.</H2>
</div>
</Dialog>
);
}
export default ErrorDialog;

View File

@ -1,9 +1,10 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Callout, Card, H3, Text, Tooltip, Position } from '@blueprintjs/core'; import { Callout, Card, H3, Text, Tooltip, Position } from '@blueprintjs/core';
import { AppContext } from '../../providers/AppContextProvider'; import { AppContext } from '../../providers/AppContextProvider';
import {
const LIGHT_UI_BACKGROUND = 'rgba(240, 248, 250, 1)'; DARK_UI_BACKGROUND,
const DARK_UI_BACKGROUND = '#293742'; LIGHT_UI_BACKGROUND,
} from '../../constants/styleConstants';
interface MyDeviceDetailsCardProps { interface MyDeviceDetailsCardProps {
deviceDetails: DeviceDetails; deviceDetails: DeviceDetails;
@ -27,10 +28,18 @@ function MyDeviceInfoCard(props: MyDeviceDetailsCardProps) {
<Callout> <Callout>
<Text>Device Type: {myDeviceType}</Text> <Text>Device Type: {myDeviceType}</Text>
<Tooltip <Tooltip
content="This should match with 'Device IP' in alert displayed on your computer, where Deskreen is running." content="This should match with 'Device IP' in alert popup appeared on your computer, where Deskreen is running."
position={Position.TOP} position={Position.TOP}
> >
<div style={{ fontWeight: 900, backgroundColor: '#00f99273' }}> <div
style={{
fontWeight: 900,
backgroundColor: '#00f99273',
paddingLeft: '10px',
paddingRight: '10px',
borderRadius: '20px',
}}
>
<Text>Device IP: {myIP}</Text> <Text>Device IP: {myIP}</Text>
</div> </div>
</Tooltip> </Tooltip>
@ -38,8 +47,8 @@ function MyDeviceInfoCard(props: MyDeviceDetailsCardProps) {
<Text>Device OS: {myOS}</Text> <Text>Device OS: {myOS}</Text>
</Callout> </Callout>
<Text className="bp3-text-muted"> <Text className="bp3-text-muted">
These details should match with the ones that you see in alert on These details should match with the ones that you see in alert popup on
sharing device. screen sharing device.
</Text> </Text>
</Card> </Card>
); );

View File

@ -0,0 +1,326 @@
import React, {
useEffect,
useMemo,
useState,
useCallback,
} from 'react';
import {
Alignment,
Button,
Card,
H5,
Switch,
Divider,
Text,
Icon,
Tooltip,
Position,
Popover,
Classes,
Toaster,
Intent,
} from '@blueprintjs/core';
import FullScreenEnter from '../../images/fullscreen_24px.svg';
import FullScreenExit from '../../images/fullscreen_exit-24px.svg';
import DeskreenIconPNG from '../../images/deskreen_logo_128x128.png';
import RedHeartTwemojiPNG from '../../images/red_heart_2764_twemoji_120x120.png';
import { Col, Row } from 'react-flexbox-grid';
import screenfull from 'screenfull';
import { VideoQuality } from '../../features/PeerConnection/VideoQualityEnum';
import { REACT_PLAYER_WRAPPER_ID } from '../../constants/appConstants';
const videoQualityButtonStyle: React.CSSProperties = {
width: '100%',
textAlign: 'center',
};
interface PlayerControlPanelProps {
onSwitchChangedCallback: (isEnabled: boolean) => void;
isPlaying: boolean;
isDefaultPlayerTurnedOn: boolean;
handleClickFullscreen: () => void;
handleClickPlayPause: () => void;
setVideoQuality: (q: VideoQuality) => void;
selectedVideoQuality: VideoQuality;
screenSharingSourceType: 'screen' | 'window';
toaster: undefined | Toaster;
}
function PlayerControlPanel(props: PlayerControlPanelProps) {
const {
isPlaying,
onSwitchChangedCallback,
isDefaultPlayerTurnedOn,
handleClickPlayPause,
handleClickFullscreen,
selectedVideoQuality,
setVideoQuality,
screenSharingSourceType,
toaster,
} = props;
const isFullScreenAPIAvailable = useMemo(() => screenfull.isEnabled, []);
const [isFullScreenOn, setIsFullScreenOn] = useState(false);
useEffect(() => {
if (!screenfull.isEnabled) return;
// @ts-ignore
screenfull.on('change', () => {
// @ts-ignore
setIsFullScreenOn(screenfull.isFullscreen);
});
}, []);
const handleClickFullscreenWhenDefaultPlayerIsOn = useCallback(() => {
const player = document.querySelector(
`#${REACT_PLAYER_WRAPPER_ID} > video`
);
if (!player) return;
// @ts-ignore
if (player.requestFullScreen) {
// @ts-ignore
player.requestFullScreen();
// @ts-ignore
} else if (player.webkitRequestFullScreen) {
// @ts-ignore
player.webkitRequestFullScreen();
// @ts-ignore
} else if (player.mozRequestFullScreen) {
// @ts-ignore
player.mozRequestFullScreen();
// @ts-ignore
} else if (player.msRequestFullscreen) {
// @ts-ignore
player.msRequestFullscreen();
// @ts-ignore
} else if (player.webkitEnterFullscreen) {
// @ts-ignore
player.webkitEnterFullscreen(); //for iphone this code worked
}
}, []);
return (
<>
<Card elevation={4}>
<Row between="xs" middle="xs">
<Col xs={12} md={3}>
<Tooltip
content="Click to visit our website"
position={Position.BOTTOM}
>
<Button minimal>
<Row middle="xs" style={{ opacity: '0.75' }}>
<Col xs={4}>
<img src={DeskreenIconPNG} width={42} height={42} alt="logo" />
</Col>
<Col xs={8}>
<H5 style={{ marginBottom: '0px' }}>Deskreen</H5>
</Col>
</Row>
</Button>
</Tooltip>
<Tooltip
content="If you like Deskreen, consider donating! Deskreen is free and opensource forever! You can help us to make Deskreen even better!"
position={Position.BOTTOM}
>
<Button>
<Row start="xs">
<Col xs>
<img
src={RedHeartTwemojiPNG}
width={16}
height={16}
style={{ transform: 'translateY(2px)' }}
alt="heart"
/>
</Col>
<Col xs>
<div
style={{ transform: 'translateY(2px) translateX(-5px)' }}
>
<Text>Donate!</Text>
</div>
</Col>
</Row>
</Button>
</Tooltip>
</Col>
<Col xs={12} md={6}>
<Row center="xs" style={{ height: '42px' }}>
<Row
center="xs"
middle="xs"
style={{
borderRadius: '20px',
backgroundColor: '#137CBD',
width: '190px',
height: '100%',
}}
>
<Row style={{ width: '100%' }} middle="xs" center="xs">
<Button
minimal
onClick={() => {
handleClickPlayPause();
toaster?.show({
icon: isPlaying ? 'pause' : 'play',
intent: Intent.PRIMARY,
message: isPlaying
? 'Video stream is paused.'
: 'Video stream is playing',
});
}}
style={{
width: '85px',
minWidth: '70px',
color: 'white',
}}
>
<Row>
<Col xs>
<Icon
icon={isPlaying ? 'pause' : 'play'}
color="white"
/>
</Col>
<Col xs>
<Text className="bp3-text-large">
{isPlaying ? 'Pause' : 'Play'}
</Text>
</Col>
</Row>
</Button>
<Divider
style={{
height: '20px',
borderRight: '1px solid #ffffffa8',
borderBottom: '1px solid #ffffffa8',
}}
/>
{screenSharingSourceType === 'window' ? (
<Tooltip
content="You can't change video quality when sharing a window. You can change quality only when shering entire screen."
position={Position.BOTTOM}
>
<Button minimal disabled>
<Icon icon="cog" color="#A7B6C2" />
</Button>
</Tooltip>
) : (
<Popover
content={
<>
<H5>Video Quality:</H5>
<Divider />
{Object.values(VideoQuality).map(
(q: VideoQuality) => {
return (
<Row>
<Button
minimal
active={selectedVideoQuality === q}
style={videoQualityButtonStyle}
onClick={() => {
setVideoQuality(q);
toaster?.show({
icon: 'clean',
intent: Intent.PRIMARY,
message: `Video quality has been changed to ${q}`,
});
}}
>
{q}
</Button>
</Row>
);
}
)}
</>
}
position={Position.BOTTOM}
popoverClassName={Classes.POPOVER_CONTENT_SIZING}
>
<Tooltip
content="Video Quality"
position={Position.BOTTOM}
>
<Button minimal>
<Icon icon="cog" color="white" />
</Button>
</Tooltip>
</Popover>
)}
<Divider
style={{
height: '20px',
borderRight: '1px solid #ffffffa8',
borderBottom: '1px solid #ffffffa8',
}}
/>
<Tooltip
content="Enter Full Screen Mode"
position={Position.BOTTOM}
>
<Button
minimal
onClick={() => {
if (isDefaultPlayerTurnedOn) {
handleClickFullscreenWhenDefaultPlayerIsOn();
} else {
handleClickFullscreen();
}
}}
>
<img
src={isFullScreenOn ? FullScreenExit : FullScreenEnter}
width={16}
height={16}
style={{
transform: 'scale(1.5) translateY(1px)',
filter:
'invert(100%) sepia(100%) saturate(0%) hue-rotate(127deg) brightness(107%) contrast(102%)',
}}
alt="fullscreen-toggle"
/>
</Button>
</Tooltip>
</Row>
</Row>
</Row>
</Col>
<Col xs={12} md={3}>
<Row end="xs">
<Col xs={12}>
<Switch
onChange={() => {
onSwitchChangedCallback(!isDefaultPlayerTurnedOn);
toaster?.show({
icon: 'video',
intent: Intent.PRIMARY,
message: `Default video player has been turned ${
isDefaultPlayerTurnedOn ? 'OFF' : 'ON'
}`,
});
}}
innerLabel={isDefaultPlayerTurnedOn ? 'ON' : 'OFF'}
inline
label={`Default Video Player`}
alignIndicator={Alignment.RIGHT}
checked={isDefaultPlayerTurnedOn}
disabled={!isFullScreenAPIAvailable}
style={{
marginBottom: '0px',
}}
/>
</Col>
</Row>
</Col>
</Row>
</Card>
</>
);
}
export default PlayerControlPanel;

View File

@ -1,12 +1,10 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Icon, Text, Switch, Classes, Alignment } from '@blueprintjs/core'; import { Switch, Classes, Alignment } from '@blueprintjs/core';
import { Row, Col } from 'react-flexbox-grid';
import { AppContext } from '../../providers/AppContextProvider'; import { AppContext } from '../../providers/AppContextProvider';
function ToggleDarkModeSwitch() { function ToggleDarkModeSwitch() {
const { isDarkTheme, setIsDarkThemeHook } = useContext(AppContext) const { isDarkTheme, setIsDarkThemeHook } = useContext(AppContext)
return ( return (
<Switch <Switch
onChange={() => { onChange={() => {

View File

@ -0,0 +1,43 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
// don't want to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
.use(Backend)
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
// fallbackLng: 'ua',
lng: 'en',
saveMissing: true,
saveMissingTo: 'all',
fallbackLng: 'en', // TODO: to generate missing keys use false as value here, will be useful when custom nodejs server is created to store missing values
debug: true, // change to true to see debug message logs in browser console
whitelist: ['en', 'ru', 'ua'],
backend: {
// path where resources get loaded from
loadPath: '/locales/{{lng}}/{{ns}}.json',
// TODO: in future implement custom nodejs server that accepts missing translations POST requests and updates .missing.json files accordingly. Here is how to do so: https://www.robinwieruch.de/react-internationalization . it can be simple nodejs server that can be started when 'yarn dev' is running, need to ckagne package.json file then
// path to post missing resources
addPath: '/locales/{{lng}}/{{ns}}.json',
// jsonIndent to use when storing json files
jsonIndent: 2,
},
keySeparator: false, // we do not use keys in form messages.welcome
interpolation: {
escapeValue: false, // react already safes from xss
},
});
export default i18n;

View File

@ -0,0 +1,8 @@
export const REACT_PLAYER_WRAPPER_ID = 'react-player-wrapper-id';
export const VIDEO_QUALITY_TO_DECIMAL = {
'25%': 0.25, // Q_25_PERCENT
'40%': 0.40, // Q_40_PERCENT
'60%': 0.60, // Q_60_PERCENT
'80%': 0.80, // Q_80_PERCENT
'100%': 1, // Q_100_PERCENT
}

View File

@ -0,0 +1,8 @@
export enum VideoQuality {
Q_AUTO = 'Auto',
Q_25_PERCENT = '25%',
Q_40_PERCENT = '40%',
Q_60_PERCENT = '60%',
Q_80_PERCENT = '80%',
Q_100_PERCENT = '100%',
}

View File

@ -10,7 +10,16 @@ import {
import setSdpMediaBitrate from './setSdpMediaBitrate'; import setSdpMediaBitrate from './setSdpMediaBitrate';
import Crypto from '../../utils/crypto'; import Crypto from '../../utils/crypto';
import VideoAutoQualityOptimizer from '../VideoAutoQualityOptimizer'; import VideoAutoQualityOptimizer from '../VideoAutoQualityOptimizer';
import { getBrowserFromUAParser, getDeviceTypeFromUAParser, getOSFromUAParser } from '../../utils/userAgentParserHelpers'; import {
getBrowserFromUAParser,
getDeviceTypeFromUAParser,
getOSFromUAParser,
} from '../../utils/userAgentParserHelpers';
import { VideoQuality } from './VideoQualityEnum';
import prepareDataMessageToChangeQuality from './prepareDataMessageToChangeQuality';
import { VIDEO_QUALITY_TO_DECIMAL } from './../../constants/appConstants';
import prepareDataMessageToGetSharingSourceType from './prepareDataMessageToGetSharingSourceType';
import { ErrorMessage } from '../../components/ErrorDialog/ErrorMessageEnum';
interface LocalPeerUser { interface LocalPeerUser {
username: string; username: string;
@ -37,7 +46,7 @@ interface ReceiveEncryptedMessagePayload {
keys: { sessionKey: string; signingKey: string }[]; keys: { sessionKey: string; signingKey: string }[];
} }
export default class LocalTestPeer { export default class PeerConnection {
roomId: string; roomId: string;
socket: any; socket: any;
@ -48,7 +57,7 @@ export default class LocalTestPeer {
partner: PartnerPeerUser = nullUser; partner: PartnerPeerUser = nullUser;
peer: any; peer: null | SimplePeer.Instance = null;
myIP = ''; myIP = '';
@ -72,24 +81,49 @@ export default class LocalTestPeer {
largeMismatchFramesCount: number; largeMismatchFramesCount: number;
isRequestedHalfQuality: boolean; screenSharingSourceType: string | undefined = undefined;
videoQuality = VideoQuality.Q_AUTO;
videoAutoQualityOptimizer: VideoAutoQualityOptimizer; videoAutoQualityOptimizer: VideoAutoQualityOptimizer;
isDarkTheme: boolean;
isStreamStarted: boolean = false;
setMyDeviceDetails: (details: DeviceDetails) => void; setMyDeviceDetails: (details: DeviceDetails) => void;
hostAllowedToConnectCallback: () => void; hostAllowedToConnectCallback: () => void;
setScreenSharingSourceTypeCallback: (s: 'screen' | 'window') => void;
setIsDarkThemeCallback: (val: boolean) => void;
setAppLanguageCallback: (newLang: string) => void;
setDialogErrorMessageCallback: (message: ErrorMessage) => void;
setIsErrorDialogOpen: (val: boolean) => void;
errorDialogMessage = ErrorMessage.UNKNOWN_ERROR;
constructor( constructor(
setUrlCallback: any, setUrlCallback: any,
crypto: Crypto, crypto: Crypto,
videoAutoQualityOptimizer: VideoAutoQualityOptimizer, videoAutoQualityOptimizer: VideoAutoQualityOptimizer,
isDarkTheme: boolean,
setMyDeviceDetailsCallback: (details: DeviceDetails) => void, setMyDeviceDetailsCallback: (details: DeviceDetails) => void,
hostAllowedToConnectCallback: () => void hostAllowedToConnectCallback: () => void,
setScreenSharingSourceTypeCallback: (s: 'screen' | 'window') => void,
setIsDarkThemeCallback: (val: boolean) => void,
setAppLanguageCallback: (newLang: string) => void,
setDialogErrorMessageCallback: (message: ErrorMessage) => void,
setIsErrorDialogOpen: (val: boolean) => void
) { ) {
this.setUrlCallback = setUrlCallback; this.setUrlCallback = setUrlCallback;
this.crypto = crypto; this.crypto = crypto;
this.videoAutoQualityOptimizer = videoAutoQualityOptimizer; this.videoAutoQualityOptimizer = videoAutoQualityOptimizer;
this.isDarkTheme = isDarkTheme;
this.setMyDeviceDetails = setMyDeviceDetailsCallback; this.setMyDeviceDetails = setMyDeviceDetailsCallback;
this.hostAllowedToConnectCallback = hostAllowedToConnectCallback; this.hostAllowedToConnectCallback = hostAllowedToConnectCallback;
this.roomId = encodeURI(window.location.pathname.replace('/', '')); this.roomId = encodeURI(window.location.pathname.replace('/', ''));
@ -97,15 +131,51 @@ export default class LocalTestPeer {
this.uaParser = new UAParser(); this.uaParser = new UAParser();
this.createUserAndInitSocket(); this.createUserAndInitSocket();
this.createPeer(); this.createPeer();
this.setScreenSharingSourceTypeCallback = setScreenSharingSourceTypeCallback;
this.setIsDarkThemeCallback = setIsDarkThemeCallback;
this.setAppLanguageCallback = setAppLanguageCallback;
this.setDialogErrorMessageCallback = setDialogErrorMessageCallback;
this.setIsErrorDialogOpen = setIsErrorDialogOpen;
this.video = null; this.video = null;
this.canvas = null; this.canvas = null;
this.largeMismatchFramesCount = 0; this.largeMismatchFramesCount = 0;
this.isRequestedHalfQuality = false;
if (!this.roomId || this.roomId === '') {
setDialogErrorMessageCallback(ErrorMessage.UNKNOWN_ERROR);
setIsErrorDialogOpen(true);
}
setInterval(() => {
if (!this.socket.connected) {
if (this.errorDialogMessage === ErrorMessage.UNKNOWN_ERROR) {
this.setDialogErrorMessageCallback(ErrorMessage.DENY_TO_CONNECT);
this.setIsErrorDialogOpen(true);
this.errorDialogMessage = ErrorMessage.DENY_TO_CONNECT;
}
}
}, 2000);
} }
log(...toLog: any[]) { setVideoQuality(videoQuality: VideoQuality) {
console.log('LocalTestPeer - ', ...toLog); this.videoQuality = videoQuality;
this.videoQualityChangedCallback();
}
setErrorDialogMessage(message: ErrorMessage) {
this.errorDialogMessage = message;
}
videoQualityChangedCallback() {
if (this.videoQuality !== VideoQuality.Q_AUTO) {
this.peer?.send(
prepareDataMessageToChangeQuality(
VIDEO_QUALITY_TO_DECIMAL[this.videoQuality]
)
);
} else {
this.peer?.send(prepareDataMessageToChangeQuality(1));
}
} }
createPeer() { createPeer() {
@ -127,23 +197,26 @@ export default class LocalTestPeer {
}); });
peer.on('stream', (stream) => { peer.on('stream', (stream) => {
setTimeout(() => {
(document.querySelector(
'#video-local-test-peer-sees'
) as any).srcObject = stream;
}, 1000);
this.videoAutoQualityOptimizer.setGoodQualityCallback(() => { this.videoAutoQualityOptimizer.setGoodQualityCallback(() => {
this.peer.send('set good quality'); if (this.videoQuality === VideoQuality.Q_AUTO) {
this.peer?.send(prepareDataMessageToChangeQuality(1));
}
}); });
this.videoAutoQualityOptimizer.setHalfQualityCallbak(() => { this.videoAutoQualityOptimizer.setHalfQualityCallbak(() => {
this.peer.send('set half quality'); if (this.videoQuality === VideoQuality.Q_AUTO) {
this.peer?.send(prepareDataMessageToChangeQuality(0.5));
}
}); });
this.videoAutoQualityOptimizer.startOptimizationLoop(); this.videoAutoQualityOptimizer.startOptimizationLoop();
this.setUrlCallback(stream); this.setUrlCallback(stream);
setTimeout(() => {
this.peer?.send(prepareDataMessageToGetSharingSourceType());
}, 1000);
this.isStreamStarted = true;
}); });
peer.on('signal', (data) => { peer.on('signal', (data) => {
@ -156,6 +229,20 @@ export default class LocalTestPeer {
}); });
}); });
peer.on('data', (data) => {
const dataJSON = JSON.parse(data);
if (dataJSON.type === 'screen_sharing_source_type') {
this.screenSharingSourceType = dataJSON.payload.value;
if (
this.screenSharingSourceType === 'screen' ||
this.screenSharingSourceType === 'window'
) {
this.setScreenSharingSourceTypeCallback(this.screenSharingSourceType);
}
}
});
this.peer = peer; this.peer = peer;
} }
@ -193,7 +280,6 @@ export default class LocalTestPeer {
if (!this.user) return; if (!this.user) return;
if (!this.partner) return; if (!this.partner) return;
const msg = (await prepareMessage(payload, this.user, this.partner)) as any; const msg = (await prepareMessage(payload, this.user, this.partner)) as any;
this.log('encrypted message', msg);
this.socket.emit('ENCRYPTED_MESSAGE', msg.toSend); this.socket.emit('ENCRYPTED_MESSAGE', msg.toSend);
} }
@ -204,18 +290,34 @@ export default class LocalTestPeer {
this.user.privateKey this.user.privateKey
)) as any; )) as any;
if (message.type === 'CALL_USER') { if (message.type === 'CALL_USER') {
this.log('ACCEPTING CALL USER', message); this.peer?.signal(message.payload.signalData);
this.peer.signal(message.payload.signalData);
} }
if (message.type === 'DENY_TO_CONNECT') { if (message.type === 'DENY_TO_CONNECT') {
this.log('OH NO, deny to connect...'); if (this.errorDialogMessage === ErrorMessage.UNKNOWN_ERROR) {
this.setDialogErrorMessageCallback(ErrorMessage.DENY_TO_CONNECT);
this.setIsErrorDialogOpen(true);
this.errorDialogMessage = ErrorMessage.DENY_TO_CONNECT;
}
} }
if (message.type === 'DISCONNECT_BY_HOST_MACHINE_USER') { if (message.type === 'DISCONNECT_BY_HOST_MACHINE_USER') {
this.log('DAMN, you were disconnected by host machine user!'); if (this.errorDialogMessage === ErrorMessage.UNKNOWN_ERROR) {
this.setDialogErrorMessageCallback(ErrorMessage.DICONNECTED);
this.setIsErrorDialogOpen(true);
this.errorDialogMessage = ErrorMessage.DICONNECTED;
}
} }
if (message.type === 'ALLOWED_TO_CONNECT') { if (message.type === 'ALLOWED_TO_CONNECT') {
this.hostAllowedToConnectCallback(); this.hostAllowedToConnectCallback();
} }
if (message.type === 'APP_THEME') {
if (this.isDarkTheme !== message.payload.value) {
this.setIsDarkThemeCallback(message.payload.value);
this.isDarkTheme = message.payload.value;
}
}
if (message.type === 'APP_LANGUAGE') {
this.setAppLanguageCallback(message.payload.value);
}
} }
createUserAndInitSocket() { createUserAndInitSocket() {
@ -228,36 +330,33 @@ export default class LocalTestPeer {
this.socket.on('disconnect', () => { this.socket.on('disconnect', () => {
// this.props.toggleSocketConnected(false); // this.props.toggleSocketConnected(false);
if (this.errorDialogMessage === ErrorMessage.UNKNOWN_ERROR) {
this.setDialogErrorMessageCallback(ErrorMessage.DICONNECTED);
this.setIsErrorDialogOpen(true);
this.errorDialogMessage = ErrorMessage.DICONNECTED;
}
}); });
this.socket.on('connect', () => { this.socket.on('connect', () => {
this.socket.emit('GET_MY_IP', (ip: string) => { this.socket.emit('GET_MY_IP', (ip: string) => {
// TODO: use set ip callback here, that will change the UI of react component
// @ts-ignore
// document.querySelector('#my-ip')?.innerHTML = ip;
this.myIP = ip; this.myIP = ip;
this.uaParser.setUA(window.navigator.userAgent); this.uaParser.setUA(window.navigator.userAgent);
// const osFromUAParser = this.uaParser.getResult().os;
// const deviceTypeFromUAParser = this.uaParser.getResult().device;
// const browserFromUAParser = this.uaParser.getResult().browser;
// this.myOS = `${osFromUAParser.name ? osFromUAParser.name : ''} ${
// osFromUAParser.version ? osFromUAParser.version : ''
// }`;
// this.myDeviceType = deviceTypeFromUAParser.type
// ? deviceTypeFromUAParser.type.toString()
// : 'computer';
this.myOS = getOSFromUAParser(this.uaParser); this.myOS = getOSFromUAParser(this.uaParser);
this.myDeviceType = getDeviceTypeFromUAParser(this.uaParser); this.myDeviceType = getDeviceTypeFromUAParser(this.uaParser);
// this.myBrowser = `${browserFromUAParser.name ? browserFromUAParser.name : ''} ${
// browserFromUAParser.version ? browserFromUAParser.version : ''
// }`;
this.myBrowser = getBrowserFromUAParser(this.uaParser); this.myBrowser = getBrowserFromUAParser(this.uaParser);
this.initApp(createdUser, ip); this.initApp(createdUser, ip);
}); });
}); });
this.socket.on('NOT_ALLOWED', () => {
if (this.errorDialogMessage === ErrorMessage.UNKNOWN_ERROR) {
this.setDialogErrorMessageCallback(ErrorMessage.NOT_ALLOWED);
this.setIsErrorDialogOpen(true);
this.errorDialogMessage = ErrorMessage.NOT_ALLOWED;
}
});
this.socket.on('USER_ENTER', (payload: { users: PartnerPeerUser[] }) => { this.socket.on('USER_ENTER', (payload: { users: PartnerPeerUser[] }) => {
const filteredPartner = payload.users.filter((v) => { const filteredPartner = payload.users.filter((v) => {
return createdUser.publicKey !== v.publicKey; return createdUser.publicKey !== v.publicKey;
@ -288,6 +387,9 @@ export default class LocalTestPeer {
}, },
}); });
this.sendEncryptedMessage({ type: 'GET_APP_THEME', payload: {} });
this.sendEncryptedMessage({ type: 'GET_APP_LANGUAGE', payload: {} });
setTimeout(() => { setTimeout(() => {
this.setMyDeviceDetails({ this.setMyDeviceDetails({
myIP: this.myIP, myIP: this.myIP,
@ -313,6 +415,11 @@ export default class LocalTestPeer {
// TODO: call ROOM LOCKED callback to change react component contain ROOM LOCKED message // TODO: call ROOM LOCKED callback to change react component contain ROOM LOCKED message
// @ts-ignore // @ts-ignore
// document.querySelector('#my-ip')?.innerHTML = 'ROOM LOCKED'; // document.querySelector('#my-ip')?.innerHTML = 'ROOM LOCKED';
if (this.errorDialogMessage === ErrorMessage.UNKNOWN_ERROR) {
this.setDialogErrorMessageCallback(ErrorMessage.DENY_TO_CONNECT);
this.setIsErrorDialogOpen(true);
this.errorDialogMessage = ErrorMessage.UNKNOWN_ERROR;
}
}); });
window.addEventListener('beforeunload', (_) => { window.addEventListener('beforeunload', (_) => {

View File

@ -0,0 +1,10 @@
export default (q: number) => {
return `
{
"type": "set_video_quality",
"payload": {
"value": ${q}
}
}
`;
};

View File

@ -0,0 +1,9 @@
export default () => {
return `
{
"type": "get_sharing_source_type",
"payload": {
}
}
`;
};

View File

@ -1,4 +1,5 @@
import pixelmatch from 'pixelmatch'; import pixelmatch from 'pixelmatch';
import { REACT_PLAYER_WRAPPER_ID } from '../../constants/appConstants';
export default class VideoAutoQualityOptimizer { export default class VideoAutoQualityOptimizer {
video: any; video: any;
@ -15,8 +16,6 @@ export default class VideoAutoQualityOptimizer {
halfQualityCallbak = () => {}; halfQualityCallbak = () => {};
constructor() {}
setGoodQualityCallback(callback: () => void) { setGoodQualityCallback(callback: () => void) {
this.goodQualityCallback = callback; this.goodQualityCallback = callback;
} }
@ -66,13 +65,11 @@ export default class VideoAutoQualityOptimizer {
} else if (mismatchInPercent < 0.1 && this.isRequestedHalfQuality) { } else if (mismatchInPercent < 0.1 && this.isRequestedHalfQuality) {
this.largeMismatchFramesCount = 0; this.largeMismatchFramesCount = 0;
this.isRequestedHalfQuality = false; this.isRequestedHalfQuality = false;
// this.peer.send('set good quality');
this.goodQualityCallback(); this.goodQualityCallback();
} else if (mismatchInPercent >= 0.1 && !this.isRequestedHalfQuality) { } else if (mismatchInPercent >= 0.1 && !this.isRequestedHalfQuality) {
if (this.largeMismatchFramesCount < 3) { if (this.largeMismatchFramesCount < 3) {
this.largeMismatchFramesCount += 1; this.largeMismatchFramesCount += 1;
} else { } else {
// this.peer.send('set half quality');
this.halfQualityCallbak(); this.halfQualityCallbak();
this.isRequestedHalfQuality = true; this.isRequestedHalfQuality = true;
} }
@ -85,7 +82,7 @@ export default class VideoAutoQualityOptimizer {
imageData = null; imageData = null;
} else { } else {
this.video = document.querySelector( this.video = document.querySelector(
'#video-local-test-peer-sees > video' `#${REACT_PLAYER_WRAPPER_ID} > video`
); );
this.canvas = document.getElementById('comparison-canvas'); this.canvas = document.getElementById('comparison-canvas');
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>

After

Width:  |  Height:  |  Size: 216 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></svg>

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,4 +1,5 @@
import React from 'react'; import React, { Suspense } from 'react';
import './config/i18n';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
@ -8,7 +9,9 @@ import { AppContextProvider } from './providers/AppContextProvider';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<AppContextProvider> <AppContextProvider>
<App /> <Suspense fallback="loading">
<App />
</Suspense>
</AppContextProvider> </AppContextProvider>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')

View File

@ -1,7 +1,10 @@
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react'; import { Classes } from '@blueprintjs/core';
import React, { useState } from 'react';
// export const LIGHT_UI_BACKGROUND = 'rgba(240, 248, 250, 1)'; import {
DARK_UI_BACKGROUND,
LIGHT_UI_BACKGROUND,
} from '../../constants/styleConstants';
interface AppContextInterface { interface AppContextInterface {
isDarkTheme: boolean; isDarkTheme: boolean;
@ -19,34 +22,27 @@ export const AppContext = React.createContext<AppContextInterface>(
export const AppContextProvider: React.FC = ({ children }) => { export const AppContextProvider: React.FC = ({ children }) => {
const [isDarkTheme, setIsDarkTheme] = useState(false); const [isDarkTheme, setIsDarkTheme] = useState(false);
const [appLanguage, setAppLanguage] = useState('en');
const getThemeFromHost = () => {
// const gotIsDarkThemeFromSettings = settings.hasSync('appIsDarkTheme')
// ? settings.getSync('appIsDarkTheme') === 'true'
// : false;
// if (gotIsDarkThemeFromSettings) {
// document.body.classList.toggle(Classes.DARK);
// document.body.style.backgroundColor = LIGHT_UI_BACKGROUND;
// }
// setIsDarkTheme(gotIsDarkThemeFromSettings);
};
useEffect(() => {
getThemeFromHost();
}, []);
const setIsDarkThemeHook = (val: boolean) => { const setIsDarkThemeHook = (val: boolean) => {
// settings.setSync('appIsDarkTheme', `${val}`); document.body.classList.toggle(Classes.DARK);
setIsDarkTheme(val);
document.body.style.backgroundColor = val
? DARK_UI_BACKGROUND
: LIGHT_UI_BACKGROUND;
setIsDarkTheme(val);
}; };
const value = { isDarkTheme, setIsDarkThemeHook }; const setAppLanguageHook = (newLang: string) => {
setAppLanguage(newLang);
};
return ( const value = {
<AppContext.Provider value={value}> isDarkTheme,
{children} setIsDarkThemeHook,
</AppContext.Provider> appLanguage,
); setAppLanguageHook,
};
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}; };

View File

@ -1122,7 +1122,7 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.5.5": "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5":
version "7.12.5" version "7.12.5"
resolved "https://packages.deskreen.com/@babel%2fruntime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" resolved "https://packages.deskreen.com/@babel%2fruntime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
@ -5535,6 +5535,13 @@ html-minifier-terser@^5.0.1:
relateurl "^0.2.7" relateurl "^0.2.7"
terser "^4.6.3" terser "^4.6.3"
html-parse-stringify2@2.0.1:
version "2.0.1"
resolved "https://packages.deskreen.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a"
integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=
dependencies:
void-elements "^2.0.1"
html-webpack-plugin@4.0.0-beta.11: html-webpack-plugin@4.0.0-beta.11:
version "4.0.0-beta.11" version "4.0.0-beta.11"
resolved "https://packages.deskreen.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715" resolved "https://packages.deskreen.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715"
@ -5634,6 +5641,20 @@ https-browserify@^1.0.0:
resolved "https://packages.deskreen.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" resolved "https://packages.deskreen.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
i18next-http-backend@^1.0.21:
version "1.0.21"
resolved "https://packages.deskreen.com/i18next-http-backend/-/i18next-http-backend-1.0.21.tgz#cee901b3527dad5165fa91de973b6aa6404c1c37"
integrity sha512-UDeHoV2B+31Gr++0KFAVjM5l+SEwePpF6sfDyaDq5ennM9QNJ78PBEMPStwkreEm4h5C8sT7M1JdNQrLcU1Wdg==
dependencies:
node-fetch "2.6.1"
i18next@^19.8.4:
version "19.8.4"
resolved "https://packages.deskreen.com/i18next/-/i18next-19.8.4.tgz#447718f2a26319b8debdbcc6fbc1a9761be7316b"
integrity sha512-FfVPNWv+felJObeZ6DSXZkj9QM1Ivvh7NcFCgA8XPtJWHz0iXVa9BUy+QY8EPrCLE+vWgDfV/sc96BgXVo6HAA==
dependencies:
"@babel/runtime" "^7.12.0"
iconv-lite@0.4.24, iconv-lite@^0.4.24: iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24" version "0.4.24"
resolved "https://packages.deskreen.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://packages.deskreen.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -7418,6 +7439,11 @@ no-case@^3.0.3:
lower-case "^2.0.1" lower-case "^2.0.1"
tslib "^1.10.0" tslib "^1.10.0"
node-fetch@2.6.1:
version "2.6.1"
resolved "https://packages.deskreen.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-forge@0.9.0: node-forge@0.9.0:
version "0.9.0" version "0.9.0"
resolved "https://packages.deskreen.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" resolved "https://packages.deskreen.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
@ -9116,6 +9142,14 @@ react-flexbox-grid@^2.1.2:
flexboxgrid2 "^7.2.0" flexboxgrid2 "^7.2.0"
prop-types "^15.5.8" prop-types "^15.5.8"
react-i18next@^11.7.4:
version "11.7.4"
resolved "https://packages.deskreen.com/react-i18next/-/react-i18next-11.7.4.tgz#6c0142e15652d8dd80cd7d857e36efe2e9d4d09a"
integrity sha512-Aq0+QVW7NMYuAtk0Stcwp4jWeNTd1p5XefAfBPcjs/4c/2duG3v3G3zdtn8fC8L4EyA/coKLwdULHI+lYTbF8w==
dependencies:
"@babel/runtime" "^7.3.1"
html-parse-stringify2 "2.0.1"
react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4: react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4:
version "16.13.1" version "16.13.1"
resolved "https://packages.deskreen.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://packages.deskreen.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@ -10967,6 +11001,11 @@ vm-browserify@^1.0.1:
resolved "https://packages.deskreen.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" resolved "https://packages.deskreen.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
void-elements@^2.0.1:
version "2.0.1"
resolved "https://packages.deskreen.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
w3c-hr-time@^1.0.1: w3c-hr-time@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://packages.deskreen.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" resolved "https://packages.deskreen.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"

View File

@ -49,7 +49,15 @@ export default function DeviceInfoCallout(props: DeviceInfoCalloutProps) {
Device Type: <span>{deviceType}</span> Device Type: <span>{deviceType}</span>
</Text> </Text>
<Tooltip content={getContentOfTooltip()} position={Position.TOP}> <Tooltip content={getContentOfTooltip()} position={Position.TOP}>
<div style={{ fontWeight: 900, backgroundColor: '#00f99273' }}> <div
style={{
fontWeight: 900,
backgroundColor: '#00f99273',
paddingLeft: '10px',
paddingRight: '10px',
borderRadius: '20px',
}}
>
<Text className="bp3-text-large"> <Text className="bp3-text-large">
Device IP: <span className="device-ip-span">{deviceIP}</span> Device IP: <span className="device-ip-span">{deviceIP}</span>
</Text> </Text>

View File

@ -1,3 +1,4 @@
import { remote } from 'electron';
import React, { useContext, useCallback, useEffect, useState } from 'react'; import React, { useContext, useCallback, useEffect, useState } from 'react';
import { import {
Button, Button,
@ -17,13 +18,14 @@ import { useTranslation } from 'react-i18next';
import { Row } from 'react-flexbox-grid'; import { Row } from 'react-flexbox-grid';
import { createStyles, makeStyles } from '@material-ui/core/styles'; import { createStyles, makeStyles } from '@material-ui/core/styles';
import i18n from 'i18next'; import i18n from 'i18next';
import SharingSessionsService from '../../features/SharingSessionsService';
import { import {
DARK_UI_BACKGROUND, DARK_UI_BACKGROUND,
LIGHT_UI_BACKGROUND, LIGHT_UI_BACKGROUND,
SettingsContext, SettingsContext,
} from '../../containers/SettingsProvider'; } from '../../containers/SettingsProvider';
import CloseOverlayButton from '../CloseOverlayButton'; import CloseOverlayButton from '../CloseOverlayButton';
import { import i18n_client, {
getLangFullNameToLangISOKeyMap, getLangFullNameToLangISOKeyMap,
getLangISOKeyToLangFullNameMap, getLangISOKeyToLangFullNameMap,
} from '../../configs/i18next.config.client'; } from '../../configs/i18next.config.client';
@ -32,6 +34,10 @@ import SettingRowLabelAndInput from './SettingRowLabelAndInput';
const Fade = require('react-reveal/Fade'); const Fade = require('react-reveal/Fade');
const sharingSessionsService = remote.getGlobal(
'sharingSessionService'
) as SharingSessionsService;
interface SettingsOverlayProps { interface SettingsOverlayProps {
isSettingsOpen: boolean; isSettingsOpen: boolean;
handleClose: () => void; handleClose: () => void;
@ -57,7 +63,11 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
const { handleClose, isSettingsOpen } = props; const { handleClose, isSettingsOpen } = props;
const { isDarkTheme, setIsDarkThemeHook } = useContext(SettingsContext); const {
isDarkTheme,
setIsDarkThemeHook,
setCurrentLanguageHook,
} = useContext(SettingsContext);
const [languagesList, setLanguagesList] = useState([] as string[]); const [languagesList, setLanguagesList] = useState([] as string[]);
@ -67,7 +77,8 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
tmp.push(key); tmp.push(key);
}); });
setLanguagesList(tmp); setLanguagesList(tmp);
}, []); setCurrentLanguageHook(i18n_client.language);
}, [setCurrentLanguageHook]);
const getClassesCallback = useStylesWithTheme(isDarkTheme); const getClassesCallback = useStylesWithTheme(isDarkTheme);
@ -76,6 +87,11 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
document.body.classList.toggle(Classes.DARK); document.body.classList.toggle(Classes.DARK);
setIsDarkThemeHook(true); setIsDarkThemeHook(true);
} }
// TODO: call sharing sessions service here to notify all connected clients about theme change
sharingSessionsService.sharingSessions.forEach((sharingSession) => {
sharingSession?.appThemeChanged(true);
});
sharingSessionsService.setAppTheme(true);
}, [isDarkTheme, setIsDarkThemeHook]); }, [isDarkTheme, setIsDarkThemeHook]);
const handleToggleLightTheme = useCallback(() => { const handleToggleLightTheme = useCallback(() => {
@ -83,6 +99,11 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
document.body.classList.toggle(Classes.DARK); document.body.classList.toggle(Classes.DARK);
setIsDarkThemeHook(false); setIsDarkThemeHook(false);
} }
// TODO: call sharing sessions service here to notify all connected clients about theme change
sharingSessionsService.sharingSessions.forEach((sharingSession) => {
sharingSession?.appThemeChanged(false);
});
sharingSessionsService.setAppTheme(false);
}, [isDarkTheme, setIsDarkThemeHook]); }, [isDarkTheme, setIsDarkThemeHook]);
const onChangeLangueageHTMLSelectHandler = ( const onChangeLangueageHTMLSelectHandler = (
@ -92,10 +113,15 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
event.currentTarget && event.currentTarget &&
getLangFullNameToLangISOKeyMap().has(event.currentTarget.value) getLangFullNameToLangISOKeyMap().has(event.currentTarget.value)
) { ) {
i18n.changeLanguage( const newLang =
getLangFullNameToLangISOKeyMap().get(event.currentTarget.value) || getLangFullNameToLangISOKeyMap().get(event.currentTarget.value) ||
'English' 'English';
); i18n.changeLanguage(newLang);
// TODO: call sharing sessions service here to notify all connected clients about language change
sharingSessionsService.sharingSessions.forEach((sharingSession) => {
sharingSession?.appLanguageChanged(newLang);
});
sharingSessionsService.setAppLanguage(newLang);
} }
}; };

View File

@ -67,15 +67,15 @@ exports[`should match exact snapshot 1`] = `
tabindex="0" tabindex="0"
/> />
<div <div
class="makeStyles-overlayInnerRoot-8 bp3-overlay-content bp3-overlay-appear bp3-overlay-appear-active" class="makeStyles-overlayInnerRoot-21 bp3-overlay-content bp3-overlay-appear-active"
tabindex="0" tabindex="0"
> >
<div <div
class="react-reveal makeStyles-overlayInsideFade-9 bp3-card" class="react-reveal makeStyles-overlayInsideFade-22 bp3-card"
id="settings-overlay-inner" id="settings-overlay-inner"
> >
<button <button
class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10" class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button" id="close-overlay-button"
type="button" type="button"
> >
@ -112,7 +112,7 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="bp3-tab-indicator-wrapper" class="bp3-tab-indicator-wrapper"
style="height: 0px; transform: translateX(0px) translateY(0px); width: 0px; transition: none;" style="height: 0px; transform: translateX(0px) translateY(0px); width: 0px;"
> >
<div <div
class="bp3-tab-indicator" class="bp3-tab-indicator"
@ -131,10 +131,10 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="makeStyles-tabNavigationRowButton-11 row middle-xs" class="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<span <span
class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-12" class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-25"
icon="wrench" icon="wrench"
> >
<svg <svg
@ -172,10 +172,10 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="makeStyles-tabNavigationRowButton-11 row middle-xs" class="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<span <span
class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-12" class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-25"
icon="shield" icon="shield"
> >
<svg <svg
@ -212,10 +212,10 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="makeStyles-tabNavigationRowButton-11 row middle-xs" class="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<span <span
class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-12" class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-25"
icon="blocked-person" icon="blocked-person"
> >
<svg <svg
@ -267,13 +267,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6" class="col-xs-6"
> >
<div <div
class="makeStyles-oneSettingRow-20 row middle-xs" class="makeStyles-oneSettingRow-26 row middle-xs"
> >
<div <div
class="" class=""
> >
<span <span
class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-21" class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-27"
icon="style" icon="style"
> >
<svg <svg
@ -381,13 +381,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6" class="col-xs-6"
> >
<div <div
class="makeStyles-oneSettingRow-22 row middle-xs" class="makeStyles-oneSettingRow-28 row middle-xs"
> >
<div <div
class="" class=""
> >
<span <span
class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-23" class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-29"
icon="translate" icon="translate"
> >
<svg <svg
@ -473,13 +473,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6" class="col-xs-6"
> >
<div <div
class="makeStyles-oneSettingRow-24 row middle-xs" class="makeStyles-oneSettingRow-30 row middle-xs"
> >
<div <div
class="" class=""
> >
<span <span
class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-25" class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-31"
icon="automatic-updates" icon="automatic-updates"
> >
<svg <svg
@ -516,7 +516,7 @@ exports[`should match exact snapshot 1`] = `
class="row" class="row"
> >
<label <label
class="bp3-control bp3-checkbox makeStyles-checkboxSettings-7" class="bp3-control bp3-checkbox makeStyles-checkboxSettings-20"
> >
<input <input
checked="" checked=""
@ -552,15 +552,15 @@ exports[`should match exact snapshot 1`] = `
tabindex="0" tabindex="0"
/> />
<div <div
class="makeStyles-overlayInnerRoot-8 bp3-overlay-content bp3-overlay-appear bp3-overlay-appear-active" class="makeStyles-overlayInnerRoot-21 bp3-overlay-content bp3-overlay-appear-active"
tabindex="0" tabindex="0"
> >
<div <div
class="react-reveal makeStyles-overlayInsideFade-9 bp3-card" class="react-reveal makeStyles-overlayInsideFade-22 bp3-card"
id="settings-overlay-inner" id="settings-overlay-inner"
> >
<button <button
class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10" class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button" id="close-overlay-button"
type="button" type="button"
> >
@ -597,7 +597,7 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="bp3-tab-indicator-wrapper" class="bp3-tab-indicator-wrapper"
style="height: 0px; transform: translateX(0px) translateY(0px); width: 0px; transition: none;" style="height: 0px; transform: translateX(0px) translateY(0px); width: 0px;"
> >
<div <div
class="bp3-tab-indicator" class="bp3-tab-indicator"
@ -616,10 +616,10 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="makeStyles-tabNavigationRowButton-11 row middle-xs" class="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<span <span
class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-12" class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-25"
icon="wrench" icon="wrench"
> >
<svg <svg
@ -657,10 +657,10 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="makeStyles-tabNavigationRowButton-11 row middle-xs" class="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<span <span
class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-12" class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-25"
icon="shield" icon="shield"
> >
<svg <svg
@ -697,10 +697,10 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="makeStyles-tabNavigationRowButton-11 row middle-xs" class="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<span <span
class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-12" class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-25"
icon="blocked-person" icon="blocked-person"
> >
<svg <svg
@ -752,13 +752,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6" class="col-xs-6"
> >
<div <div
class="makeStyles-oneSettingRow-20 row middle-xs" class="makeStyles-oneSettingRow-26 row middle-xs"
> >
<div <div
class="" class=""
> >
<span <span
class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-21" class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-27"
icon="style" icon="style"
> >
<svg <svg
@ -866,13 +866,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6" class="col-xs-6"
> >
<div <div
class="makeStyles-oneSettingRow-22 row middle-xs" class="makeStyles-oneSettingRow-28 row middle-xs"
> >
<div <div
class="" class=""
> >
<span <span
class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-23" class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-29"
icon="translate" icon="translate"
> >
<svg <svg
@ -958,13 +958,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6" class="col-xs-6"
> >
<div <div
class="makeStyles-oneSettingRow-24 row middle-xs" class="makeStyles-oneSettingRow-30 row middle-xs"
> >
<div <div
class="" class=""
> >
<span <span
class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-25" class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-31"
icon="automatic-updates" icon="automatic-updates"
> >
<svg <svg
@ -1001,7 +1001,7 @@ exports[`should match exact snapshot 1`] = `
class="row" class="row"
> >
<label <label
class="bp3-control bp3-checkbox makeStyles-checkboxSettings-7" class="bp3-control bp3-checkbox makeStyles-checkboxSettings-20"
> >
<input <input
checked="" checked=""
@ -1035,7 +1035,6 @@ exports[`should match exact snapshot 1`] = `
onKeyDown={[Function]} onKeyDown={[Function]}
> >
<CSSTransition <CSSTransition
appear={true}
classNames="bp3-overlay" classNames="bp3-overlay"
in={true} in={true}
key=".$__backdrop" key=".$__backdrop"
@ -1043,7 +1042,7 @@ exports[`should match exact snapshot 1`] = `
timeout={0} timeout={0}
> >
<Transition <Transition
appear={true} appear={false}
enter={true} enter={true}
exit={true} exit={true}
in={true} in={true}
@ -1065,7 +1064,6 @@ exports[`should match exact snapshot 1`] = `
</Transition> </Transition>
</CSSTransition> </CSSTransition>
<CSSTransition <CSSTransition
appear={true}
classNames="bp3-overlay" classNames="bp3-overlay"
in={true} in={true}
key=".$.0" key=".$.0"
@ -1073,7 +1071,7 @@ exports[`should match exact snapshot 1`] = `
timeout={0} timeout={0}
> >
<Transition <Transition
appear={true} appear={false}
enter={true} enter={true}
exit={true} exit={true}
in={true} in={true}
@ -1088,7 +1086,7 @@ exports[`should match exact snapshot 1`] = `
unmountOnExit={false} unmountOnExit={false}
> >
<div <div
className="makeStyles-overlayInnerRoot-8 bp3-overlay-content" className="makeStyles-overlayInnerRoot-21 bp3-overlay-content"
tabIndex={0} tabIndex={0}
> >
<Fade <Fade
@ -1125,7 +1123,7 @@ exports[`should match exact snapshot 1`] = `
refProp="ref" refProp="ref"
> >
<div <div
className="react-reveal makeStyles-overlayInsideFade-9 bp3-card" className="react-reveal makeStyles-overlayInsideFade-22 bp3-card"
id="settings-overlay-inner" id="settings-overlay-inner"
style={ style={
Object { Object {
@ -1134,17 +1132,17 @@ exports[`should match exact snapshot 1`] = `
} }
> >
<CloseOverlayButton <CloseOverlayButton
className="makeStyles-absoluteCloseButton-10" className="makeStyles-absoluteCloseButton-23"
isDefaultStyles={true} isDefaultStyles={true}
onClick={[Function]} onClick={[Function]}
> >
<Blueprint3.Button <Blueprint3.Button
className="makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10" className="makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button" id="close-overlay-button"
onClick={[Function]} onClick={[Function]}
> >
<button <button
className="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10" className="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button" id="close-overlay-button"
onClick={[Function]} onClick={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@ -1213,7 +1211,6 @@ exports[`should match exact snapshot 1`] = `
Object { Object {
"height": 0, "height": 0,
"transform": "translateX(0px) translateY(0px)", "transform": "translateX(0px) translateY(0px)",
"transition": "none",
"width": 0, "width": 0,
} }
} }
@ -1245,18 +1242,18 @@ exports[`should match exact snapshot 1`] = `
tabIndex={0} tabIndex={0}
> >
<Row <Row
className="makeStyles-tabNavigationRowButton-11" className="makeStyles-tabNavigationRowButton-24"
middle="xs" middle="xs"
> >
<div <div
className="makeStyles-tabNavigationRowButton-11 row middle-xs" className="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-iconInTablLeftButton-12" className="makeStyles-iconInTablLeftButton-25"
icon="wrench" icon="wrench"
> >
<span <span
className="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-12" className="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-25"
icon="wrench" icon="wrench"
> >
<svg <svg
@ -1312,18 +1309,18 @@ exports[`should match exact snapshot 1`] = `
tabIndex={0} tabIndex={0}
> >
<Row <Row
className="makeStyles-tabNavigationRowButton-11" className="makeStyles-tabNavigationRowButton-24"
middle="xs" middle="xs"
> >
<div <div
className="makeStyles-tabNavigationRowButton-11 row middle-xs" className="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-iconInTablLeftButton-12" className="makeStyles-iconInTablLeftButton-25"
icon="shield" icon="shield"
> >
<span <span
className="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-12" className="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-25"
icon="shield" icon="shield"
> >
<svg <svg
@ -1377,18 +1374,18 @@ exports[`should match exact snapshot 1`] = `
role="tab" role="tab"
> >
<Row <Row
className="makeStyles-tabNavigationRowButton-11" className="makeStyles-tabNavigationRowButton-24"
middle="xs" middle="xs"
> >
<div <div
className="makeStyles-tabNavigationRowButton-11 row middle-xs" className="makeStyles-tabNavigationRowButton-24 row middle-xs"
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-iconInTablLeftButton-12" className="makeStyles-iconInTablLeftButton-25"
icon="blocked-person" icon="blocked-person"
> >
<span <span
className="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-12" className="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-25"
icon="blocked-person" icon="blocked-person"
> >
<svg <svg
@ -1492,23 +1489,23 @@ exports[`should match exact snapshot 1`] = `
className="col-xs-6" className="col-xs-6"
> >
<Row <Row
className="makeStyles-oneSettingRow-20" className="makeStyles-oneSettingRow-26"
middle="xs" middle="xs"
> >
<div <div
className="makeStyles-oneSettingRow-20 row middle-xs" className="makeStyles-oneSettingRow-26 row middle-xs"
> >
<Col> <Col>
<div <div
className="" className=""
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-settingRowIcon-21" className="makeStyles-settingRowIcon-27"
icon="style" icon="style"
iconSize={25} iconSize={25}
> >
<span <span
className="bp3-icon bp3-icon-style makeStyles-settingRowIcon-21" className="bp3-icon bp3-icon-style makeStyles-settingRowIcon-27"
icon="style" icon="style"
> >
<svg <svg
@ -1701,23 +1698,23 @@ exports[`should match exact snapshot 1`] = `
className="col-xs-6" className="col-xs-6"
> >
<Row <Row
className="makeStyles-oneSettingRow-22" className="makeStyles-oneSettingRow-28"
middle="xs" middle="xs"
> >
<div <div
className="makeStyles-oneSettingRow-22 row middle-xs" className="makeStyles-oneSettingRow-28 row middle-xs"
> >
<Col> <Col>
<div <div
className="" className=""
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-settingRowIcon-23" className="makeStyles-settingRowIcon-29"
icon="translate" icon="translate"
iconSize={25} iconSize={25}
> >
<span <span
className="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-23" className="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-29"
icon="translate" icon="translate"
> >
<svg <svg
@ -1840,7 +1837,7 @@ exports[`should match exact snapshot 1`] = `
input={ input={
<Blueprint3.Checkbox <Blueprint3.Checkbox
checked={true} checked={true}
className="makeStyles-checkboxSettings-7" className="makeStyles-checkboxSettings-20"
label="Enabled" label="Enabled"
/> />
} }
@ -1860,23 +1857,23 @@ exports[`should match exact snapshot 1`] = `
className="col-xs-6" className="col-xs-6"
> >
<Row <Row
className="makeStyles-oneSettingRow-24" className="makeStyles-oneSettingRow-30"
middle="xs" middle="xs"
> >
<div <div
className="makeStyles-oneSettingRow-24 row middle-xs" className="makeStyles-oneSettingRow-30 row middle-xs"
> >
<Col> <Col>
<div <div
className="" className=""
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-settingRowIcon-25" className="makeStyles-settingRowIcon-31"
icon="automatic-updates" icon="automatic-updates"
iconSize={25} iconSize={25}
> >
<span <span
className="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-25" className="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-31"
icon="automatic-updates" icon="automatic-updates"
> >
<svg <svg
@ -1927,12 +1924,12 @@ exports[`should match exact snapshot 1`] = `
> >
<Blueprint3.Checkbox <Blueprint3.Checkbox
checked={true} checked={true}
className="makeStyles-checkboxSettings-7" className="makeStyles-checkboxSettings-20"
label="Enabled" label="Enabled"
> >
<Control <Control
checked={true} checked={true}
className="makeStyles-checkboxSettings-7" className="makeStyles-checkboxSettings-20"
inputRef={[Function]} inputRef={[Function]}
label="Enabled" label="Enabled"
onChange={[Function]} onChange={[Function]}
@ -1940,7 +1937,7 @@ exports[`should match exact snapshot 1`] = `
typeClassName="bp3-checkbox" typeClassName="bp3-checkbox"
> >
<label <label
className="bp3-control bp3-checkbox makeStyles-checkboxSettings-7" className="bp3-control bp3-checkbox makeStyles-checkboxSettings-20"
> >
<input <input
checked={true} checked={true}

View File

@ -43,9 +43,8 @@ const useStyles = makeStyles(() =>
width: '40px', width: '40px',
borderRadius: '100px !important', borderRadius: '100px !important',
position: 'relative', position: 'relative',
top: '72px', top: '70px',
left: '-190px !important', left: '-190px !important',
zIndex: 9999,
cursor: 'default', cursor: 'default',
}, },
}) })
@ -119,7 +118,11 @@ export default function ShareEntireScreenOrAppWindowControlGroup(
/> />
<Text className="bp3-running-text">Application Window</Text> <Text className="bp3-running-text">Application Window</Text>
</Button> </Button>
<Button active className={classes.orDecorationButton}> <Button
active
className={classes.orDecorationButton}
style={{ zIndex: 999 }}
>
OR OR
</Button> </Button>
</ControlGroup> </ControlGroup>

View File

@ -1,13 +1,20 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-ts-comment */
import { clipboard, remote } from 'electron'; import { clipboard, remote } from 'electron';
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { Button, Text, Tooltip, Position, H2, Dialog } from '@blueprintjs/core'; import { useTranslation } from 'react-i18next';
import {
Button,
Text,
Tooltip,
Position,
Dialog,
Classes,
} from '@blueprintjs/core';
import QRCode from 'qrcode.react'; import QRCode from 'qrcode.react';
import { makeStyles, createStyles } from '@material-ui/core'; import { makeStyles, createStyles } from '@material-ui/core';
import { Row, Col } from 'react-flexbox-grid'; import { Row, Col } from 'react-flexbox-grid';
import { SettingsContext } from '../../containers/SettingsProvider'; import { SettingsContext } from '../../containers/SettingsProvider';
import isProduction from '../../utils/isProduction'; import isProduction from '../../utils/isProduction';
import CloseOverlayButton from '../CloseOverlayButton';
import SharingSessionService from '../../features/SharingSessionsService'; import SharingSessionService from '../../features/SharingSessionsService';
const sharingSessionService = remote.getGlobal( const sharingSessionService = remote.getGlobal(
@ -38,17 +45,21 @@ const useStyles = makeStyles(() =>
dialogQRWrapper: { dialogQRWrapper: {
backgroundColor: 'white', backgroundColor: 'white',
padding: '20px', padding: '20px',
// width: '95%',
// hieght: '95%',
borderRadius: '10px', borderRadius: '10px',
}, },
bigQRCodeDialogRoot: { bigQRCodeDialogRoot: {
'&:hover': { '&:hover': {
cursor: 'zoom-out', cursor: 'zoom-out',
}, },
paddingBottom: '0px',
}, },
}) })
); );
const ScanQRStep: React.FC = () => { const ScanQRStep: React.FC = () => {
const { t } = useTranslation();
const classes = useStyles(); const classes = useStyles();
const { isDarkTheme } = useContext(SettingsContext); const { isDarkTheme } = useContext(SettingsContext);
@ -73,10 +84,20 @@ const ScanQRStep: React.FC = () => {
return ( return (
<> <>
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<Text className="bp3-text">Scan the QR code</Text> <Text>
<Text className="bp3-text-muted"> <span
( make sure your computer and device are connected on same WiFi ) style={{
backgroundColor: '#00f99273',
fontWeight: 900,
paddingRight: '8px',
paddingLeft: '8px',
borderRadius: '20px',
}}
>
make sure your computer and device are connected to same WiFi
</span>
</Text> </Text>
<Text className="bp3-text">{t('Scan the QR code')}</Text>
</div> </div>
<div> <div>
<Tooltip content="Click to make bigger" position={Position.LEFT}> <Tooltip content="Click to make bigger" position={Position.LEFT}>
@ -133,50 +154,32 @@ const ScanQRStep: React.FC = () => {
canEscapeKeyClose canEscapeKeyClose
canOutsideClickClose canOutsideClickClose
transitionDuration={isProduction() ? 700 : 0} transitionDuration={isProduction() ? 700 : 0}
style={{ position: 'relative', top: '-30px' }} style={{ position: 'relative', top: '0px' }}
> >
<Button <Row
id="qr-code-dialog-inner" id="qr-code-dialog-inner"
className={Classes.DIALOG_BODY}
center="xs"
middle="xs"
onClick={() => setIsQRCodeMagnified(false)} onClick={() => setIsQRCodeMagnified(false)}
style={{ paddingTop: '20px', paddingBottom: '13px' }}
> >
<Row between="xs" middle="xs"> <Col xs={11} className={classes.dialogQRWrapper}>
<Col xs={10}> <QRCode
<H2 style={{ margin: '0px', padding: '0px', marginLeft: '35px' }}> value={`http://${LOCAL_LAN_IP}:${CLIENT_VIEWER_PORT}/${roomID}`}
Scan QR Code level="H"
</H2> renderAs="svg"
</Col> imageSettings={{
<Col xs={2}> // TODO: change image to app icon
<CloseOverlayButton src:
onClick={() => setIsQRCodeMagnified(false)} 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Electron_Software_Framework_Logo.svg/256px-Electron_Software_Framework_Logo.svg.png',
style={{ width: 25,
width: '40px', height: 25,
height: '40px', }}
position: 'relative', width="390px"
borderRadius: '100px', height="390px"
}} />
/> </Col>
</Col> </Row>
</Row>
<Row center="xs">
<div className={classes.dialogQRWrapper}>
<QRCode
value={`http://${LOCAL_LAN_IP}:${CLIENT_VIEWER_PORT}/${roomID}`}
level="H"
renderAs="svg"
imageSettings={{
// TODO: change image to app icon
src:
'https://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Electron_Software_Framework_Logo.svg/256px-Electron_Software_Framework_Logo.svg.png',
width: 25,
height: 25,
}}
width="390px"
height="390px"
/>
</div>
</Row>
</Button>
</Dialog> </Dialog>
</> </>
); );

View File

@ -237,11 +237,21 @@ exports[`should match exact snapshot 1`] = `
<Blueprint3.Button <Blueprint3.Button
active={true} active={true}
className="makeStyles-orDecorationButton-6" className="makeStyles-orDecorationButton-6"
style={
Object {
"zIndex": 999,
}
}
> >
<button <button
className="bp3-button bp3-active makeStyles-orDecorationButton-6" className="bp3-button bp3-active makeStyles-orDecorationButton-6"
onKeyDown={[Function]} onKeyDown={[Function]}
onKeyUp={[Function]} onKeyUp={[Function]}
style={
Object {
"zIndex": 999,
}
}
type="button" type="button"
> >
<Blueprint3.Icon <Blueprint3.Icon

View File

@ -236,7 +236,10 @@ exports[`should match exact snapshot 1`] = `
style={ style={
Object { Object {
"backgroundColor": "#00f99273", "backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900, "fontWeight": 900,
"paddingLeft": "10px",
"paddingRight": "10px",
} }
} }
tabIndex={0} tabIndex={0}

View File

@ -74,23 +74,31 @@ exports[`should match exact snapshot on each step 1`] = `
} }
} }
> >
<Blueprint3.Text>
<div
className=""
>
<span
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "8px",
"paddingRight": "8px",
}
}
>
make sure your computer and device are connected to same WiFi
</span>
</div>
</Blueprint3.Text>
<Blueprint3.Text <Blueprint3.Text
className="bp3-text" className="bp3-text"
> >
<div <div
className="bp3-text" className="bp3-text"
> />
Scan the QR code
</div>
</Blueprint3.Text>
<Blueprint3.Text
className="bp3-text-muted"
>
<div
className="bp3-text-muted"
>
( make sure your computer and device are connected on same WiFi )
</div>
</Blueprint3.Text> </Blueprint3.Text>
</div> </div>
<div> <div>
@ -423,7 +431,7 @@ exports[`should match exact snapshot on each step 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}
@ -443,7 +451,7 @@ exports[`should match exact snapshot on each step 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}
@ -840,11 +848,21 @@ exports[`should match exact snapshot on each step 2`] = `
<Blueprint3.Button <Blueprint3.Button
active={true} active={true}
className="makeStyles-orDecorationButton-9" className="makeStyles-orDecorationButton-9"
style={
Object {
"zIndex": 999,
}
}
> >
<button <button
className="bp3-button bp3-active makeStyles-orDecorationButton-9" className="bp3-button bp3-active makeStyles-orDecorationButton-9"
onKeyDown={[Function]} onKeyDown={[Function]}
onKeyUp={[Function]} onKeyUp={[Function]}
style={
Object {
"zIndex": 999,
}
}
type="button" type="button"
> >
<Blueprint3.Icon <Blueprint3.Icon
@ -1352,7 +1370,10 @@ exports[`should match exact snapshot on each step 3`] = `
style={ style={
Object { Object {
"backgroundColor": "#00f99273", "backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900, "fontWeight": 900,
"paddingLeft": "10px",
"paddingRight": "10px",
} }
} }
tabIndex={0} tabIndex={0}

View File

@ -9,16 +9,24 @@ exports[`<ScanQRStep /> when rendered should match exact snapshot 1`] = `
} }
} }
> >
<Blueprint3.Text>
<span
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "8px",
"paddingRight": "8px",
}
}
>
make sure your computer and device are connected to same WiFi
</span>
</Blueprint3.Text>
<Blueprint3.Text <Blueprint3.Text
className="bp3-text" className="bp3-text"
> />
Scan the QR code
</Blueprint3.Text>
<Blueprint3.Text
className="bp3-text-muted"
>
( make sure your computer and device are connected on same WiFi )
</Blueprint3.Text>
</div> </div>
<div> <div>
<Blueprint3.Tooltip <Blueprint3.Tooltip
@ -95,83 +103,42 @@ exports[`<ScanQRStep /> when rendered should match exact snapshot 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}
> >
<Blueprint3.Button <Row
center="xs"
className="bp3-dialog-body"
id="qr-code-dialog-inner" id="qr-code-dialog-inner"
middle="xs"
onClick={[Function]} onClick={[Function]}
style={
Object {
"paddingBottom": "13px",
"paddingTop": "20px",
}
}
> >
<Row <Col
between="xs" className="makeStyles-dialogQRWrapper-2"
middle="xs" xs={11}
> >
<Col <QRCode
xs={10} bgColor="#FFFFFF"
> fgColor="#000000"
<Component height="390px"
style={ imageSettings={
Object { Object {
"margin": "0px", "height": 25,
"marginLeft": "35px", "src": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Electron_Software_Framework_Logo.svg/256px-Electron_Software_Framework_Logo.svg.png",
"padding": "0px", "width": 25,
}
} }
> }
Scan QR Code includeMargin={false}
</Component> level="H"
</Col> renderAs="svg"
<Col size={128}
xs={2} value="http://255.255.255.255:3000/"
> width="390px"
<CloseOverlayButton />
onClick={[Function]} </Col>
style={ </Row>
Object {
"borderRadius": "100px",
"height": "40px",
"position": "relative",
"width": "40px",
}
}
/>
</Col>
</Row>
<Row
center="xs"
>
<div
className="makeStyles-dialogQRWrapper-2"
>
<QRCode
bgColor="#FFFFFF"
fgColor="#000000"
height="390px"
imageSettings={
Object {
"height": 25,
"src": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Electron_Software_Framework_Logo.svg/256px-Electron_Software_Framework_Logo.svg.png",
"width": 25,
}
}
includeMargin={false}
level="H"
renderAs="svg"
size={128}
value="http://255.255.255.255:3000/"
width="390px"
/>
</div>
</Row>
</Blueprint3.Button>
</Blueprint3.Dialog> </Blueprint3.Dialog>
</Fragment> </Fragment>
`; `;

View File

@ -2,12 +2,16 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/rules-of-hooks */ /* eslint-disable react-hooks/rules-of-hooks */
import React, { useCallback, useContext } from 'react'; import React, { useCallback, useContext } from 'react';
import { Button, Icon, Position, Tooltip } from '@blueprintjs/core'; import { Button, Text, Icon, Position, Tooltip } from '@blueprintjs/core';
import { createStyles, makeStyles } from '@material-ui/core/styles'; import { createStyles, makeStyles } from '@material-ui/core/styles';
import { Col, Row } from 'react-flexbox-grid';
import SettingsOverlay from './SettingsOverlay/SettingsOverlay'; import SettingsOverlay from './SettingsOverlay/SettingsOverlay';
import ConnectedDevicesListDrawer from './ConnectedDevicesListDrawer'; import ConnectedDevicesListDrawer from './ConnectedDevicesListDrawer';
import { SettingsContext } from '../containers/SettingsProvider'; import { SettingsContext } from '../containers/SettingsProvider';
import isProduction from '../utils/isProduction'; import isProduction from '../utils/isProduction';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore fine import here
import RedHeartTwemojiPNG from '../images/red_heart_2764_twemoji_120x120.png';
const Zoom = require('react-reveal/Zoom'); const Zoom = require('react-reveal/Zoom');
const Fade = require('react-reveal/Fade'); const Fade = require('react-reveal/Fade');
@ -72,13 +76,41 @@ export default function TopPanel(props: any) {
setIsDrawerOpen(!isDrawersOpen); setIsDrawerOpen(!isDrawersOpen);
}, [isDrawersOpen]); }, [isDrawersOpen]);
const renderDonateButton = useCallback(() => {
return (
<Tooltip
content="If you like Deskreen, consider donating! Deskreen is free and opensource forever! You can help us to make Deskreen even better!"
position={Position.BOTTOM}
>
<Button style={{ transform: 'translateY(2px)', marginRight: '10px' }}>
<Row start="xs">
<Col xs>
<img
src={RedHeartTwemojiPNG}
width={16}
height={16}
style={{ transform: 'translateY(2px)' }}
alt="heart"
/>
</Col>
<Col xs>
<div style={{ transform: 'translateY(2px) translateX(-5px)' }}>
<Text>Donate!</Text>
</div>
</Col>
</Row>
</Button>
</Tooltip>
);
}, []);
const renderConnectedDevicesListButton = useCallback(() => { const renderConnectedDevicesListButton = useCallback(() => {
return ( return (
<div className={getClassesCallback().topPanelControlButtonMargin}> <div className={getClassesCallback().topPanelControlButtonMargin}>
<Tooltip content="Connected Devices" position={Position.BOTTOM}> <Tooltip content="Connected Devices" position={Position.BOTTOM}>
<Button <Button
id="top-panel-connected-devices-list-button" id="top-panel-connected-devices-list-button"
intent="none" intent="primary"
className={getClassesCallback().topPanelControlButton} className={getClassesCallback().topPanelControlButton}
onClick={handleToggleConnectedDevicesListDrawer} onClick={handleToggleConnectedDevicesListDrawer}
> >
@ -96,7 +128,7 @@ export default function TopPanel(props: any) {
const renderHelpButton = useCallback(() => { const renderHelpButton = useCallback(() => {
return ( return (
<div className={getClassesCallback().topPanelControlButtonMargin}> <div className={getClassesCallback().topPanelControlButtonMargin}>
<Tooltip content="Help" position={Position.BOTTOM}> <Tooltip content="Tutorial" position={Position.BOTTOM}>
<Button <Button
id="top-panel-help-button" id="top-panel-help-button"
intent="none" intent="none"
@ -104,7 +136,7 @@ export default function TopPanel(props: any) {
> >
<Icon <Icon
className={getClassesCallback().topPanelIconOfControlButton} className={getClassesCallback().topPanelIconOfControlButton}
icon="help" icon="learning"
iconSize={22} iconSize={22}
/> />
</Button> </Button>
@ -140,12 +172,17 @@ export default function TopPanel(props: any) {
id="logo-with-popover-visit-website" id="logo-with-popover-visit-website"
className={getClassesCallback().logoWithAppName} className={getClassesCallback().logoWithAppName}
> >
<h4 <Tooltip
id="deskreen-top-app-name-header" content="Click to visit our website"
className={getClassesCallback().appNameHeader} position={Position.BOTTOM}
> >
Deskreen <h4
</h4> id="deskreen-top-app-name-header"
className={getClassesCallback().appNameHeader}
>
Deskreen
</h4>
</Tooltip>
</div> </div>
</Zoom> </Zoom>
); );
@ -154,7 +191,14 @@ export default function TopPanel(props: any) {
return ( return (
<> <>
<div className={getClassesCallback().topPanelRoot}> <div className={getClassesCallback().topPanelRoot}>
{renderLogoWithAppName()} <Row
middle="xs"
center="xs"
style={{ width: '100%', transform: 'translateX(-50px)' }}
>
<Col>{renderDonateButton()}</Col>
<Col>{renderLogoWithAppName()}</Col>
</Row>
<div className={getClassesCallback().topPanelControlButtonsRoot}> <div className={getClassesCallback().topPanelControlButtonsRoot}>
<Fade right duration={isProduction() ? 2000 : 0}> <Fade right duration={isProduction() ? 2000 : 0}>
{renderConnectedDevicesListButton()} {renderConnectedDevicesListButton()}

View File

@ -151,7 +151,7 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="" class=""
style="font-weight: 900; background-color: rgba(0, 249, 146, 0.451);" style="font-weight: 900; background-color: rgba(0, 249, 146, 0.451); padding-left: 10px; padding-right: 10px; border-radius: 20px;"
tabindex="0" tabindex="0"
> >
<div <div
@ -301,7 +301,7 @@ exports[`should match exact snapshot 1`] = `
> >
<div <div
class="" class=""
style="font-weight: 900; background-color: rgba(0, 249, 146, 0.451);" style="font-weight: 900; background-color: rgba(0, 249, 146, 0.451); padding-left: 10px; padding-right: 10px; border-radius: 20px;"
tabindex="0" tabindex="0"
> >
<div <div
@ -628,7 +628,10 @@ exports[`should match exact snapshot 1`] = `
style={ style={
Object { Object {
"backgroundColor": "#00f99273", "backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900, "fontWeight": 900,
"paddingLeft": "10px",
"paddingRight": "10px",
} }
} }
tabIndex={0} tabIndex={0}

View File

@ -175,11 +175,21 @@ exports[`should match exact snapshot 1`] = `
<Blueprint3.Button <Blueprint3.Button
active={true} active={true}
className="makeStyles-orDecorationButton-6" className="makeStyles-orDecorationButton-6"
style={
Object {
"zIndex": 999,
}
}
> >
<button <button
className="bp3-button bp3-active makeStyles-orDecorationButton-6" className="bp3-button bp3-active makeStyles-orDecorationButton-6"
onKeyDown={[Function]} onKeyDown={[Function]}
onKeyUp={[Function]} onKeyUp={[Function]}
style={
Object {
"zIndex": 999,
}
}
type="button" type="button"
> >
<Blueprint3.Icon <Blueprint3.Icon

View File

@ -5,22 +5,96 @@ exports[`<TopPanel /> should match exact snapshot 1`] = `
<div <div
className="makeStyles-topPanelRoot-1" className="makeStyles-topPanelRoot-1"
> >
<Zoom <Row
duration={0} center="xs"
top={true} middle="xs"
style={
Object {
"transform": "translateX(-50px)",
"width": "100%",
}
}
> >
<div <Col>
className="makeStyles-logoWithAppName-9" <Blueprint3.Tooltip
id="logo-with-popover-visit-website" content="If you like Deskreen, consider donating! Deskreen is free and opensource forever! You can help us to make Deskreen even better!"
> hoverCloseDelay={0}
<h4 hoverOpenDelay={100}
className="makeStyles-appNameHeader-17" position="bottom"
id="deskreen-top-app-name-header" transitionDuration={100}
> >
Deskreen <Blueprint3.Button
</h4> style={
</div> Object {
</Zoom> "marginRight": "10px",
"transform": "translateY(2px)",
}
}
>
<Row
start="xs"
>
<Col
xs={true}
>
<img
alt="heart"
height={16}
src="test-file-stub"
style={
Object {
"transform": "translateY(2px)",
}
}
width={16}
/>
</Col>
<Col
xs={true}
>
<div
style={
Object {
"transform": "translateY(2px) translateX(-5px)",
}
}
>
<Blueprint3.Text>
Donate!
</Blueprint3.Text>
</div>
</Col>
</Row>
</Blueprint3.Button>
</Blueprint3.Tooltip>
</Col>
<Col>
<Zoom
duration={0}
top={true}
>
<div
className="makeStyles-logoWithAppName-9"
id="logo-with-popover-visit-website"
>
<Blueprint3.Tooltip
content="Click to visit our website"
hoverCloseDelay={0}
hoverOpenDelay={100}
position="bottom"
transitionDuration={100}
>
<h4
className="makeStyles-appNameHeader-17"
id="deskreen-top-app-name-header"
>
Deskreen
</h4>
</Blueprint3.Tooltip>
</div>
</Zoom>
</Col>
</Row>
<div <div
className="makeStyles-topPanelControlButtonsRoot-25" className="makeStyles-topPanelControlButtonsRoot-25"
> >
@ -41,7 +115,7 @@ exports[`<TopPanel /> should match exact snapshot 1`] = `
<Blueprint3.Button <Blueprint3.Button
className="makeStyles-topPanelControlButton-40" className="makeStyles-topPanelControlButton-40"
id="top-panel-connected-devices-list-button" id="top-panel-connected-devices-list-button"
intent="none" intent="primary"
onClick={[Function]} onClick={[Function]}
> >
<Blueprint3.Icon <Blueprint3.Icon
@ -56,7 +130,7 @@ exports[`<TopPanel /> should match exact snapshot 1`] = `
className="makeStyles-topPanelControlButtonMargin-55" className="makeStyles-topPanelControlButtonMargin-55"
> >
<Blueprint3.Tooltip <Blueprint3.Tooltip
content="Help" content="Tutorial"
hoverCloseDelay={0} hoverCloseDelay={0}
hoverOpenDelay={100} hoverOpenDelay={100}
position="bottom" position="bottom"
@ -69,7 +143,7 @@ exports[`<TopPanel /> should match exact snapshot 1`] = `
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-70" className="makeStyles-topPanelIconOfControlButton-70"
icon="help" icon="learning"
iconSize={22} iconSize={22}
/> />
</Blueprint3.Button> </Blueprint3.Button>

View File

@ -8,6 +8,7 @@ import settings from 'electron-settings';
import config from './app.lang.config'; import config from './app.lang.config';
import isProduction from '../utils/isProduction'; import isProduction from '../utils/isProduction';
// TODO: move this outside this file!
export const getLangFullNameToLangISOKeyMap = (): Map<string, string> => { export const getLangFullNameToLangISOKeyMap = (): Map<string, string> => {
const res = new Map<string, string>(); const res = new Map<string, string>();
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
@ -19,6 +20,7 @@ export const getLangFullNameToLangISOKeyMap = (): Map<string, string> => {
return res; return res;
}; };
// TODO: move this outside this file!
export const getLangISOKeyToLangFullNameMap = (): Map<string, string> => { export const getLangISOKeyToLangFullNameMap = (): Map<string, string> => {
const res = new Map<string, string>(); const res = new Map<string, string>();
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax

View File

@ -23,6 +23,8 @@ jest.mock('electron', () => {
return { return {
createWaitingForConnectionSharingSession: () => createWaitingForConnectionSharingSession: () =>
new Promise(() => {}), new Promise(() => {}),
setAppLanguage: () => {},
setAppTheme: () => {},
}; };
} }
return {}; return {};

View File

@ -9,12 +9,16 @@ export const DARK_UI_BACKGROUND = '#293742';
interface SettingsContextInterface { interface SettingsContextInterface {
isDarkTheme: boolean; isDarkTheme: boolean;
currentLanguage: string;
setIsDarkThemeHook: (val: boolean) => void; setIsDarkThemeHook: (val: boolean) => void;
setCurrentLanguageHook: (newLang: string) => void;
} }
const defaultSettingsContextValue = { const defaultSettingsContextValue = {
isDarkTheme: false, isDarkTheme: false,
setIsDarkThemeHook: () => {}, setIsDarkThemeHook: () => {},
setCurrentLanguageHook: () => {},
currentLanguage: 'en',
}; };
export const SettingsContext = React.createContext<SettingsContextInterface>( export const SettingsContext = React.createContext<SettingsContextInterface>(
@ -23,6 +27,7 @@ export const SettingsContext = React.createContext<SettingsContextInterface>(
export const SettingsProvider: React.FC = ({ children }) => { export const SettingsProvider: React.FC = ({ children }) => {
const [isDarkTheme, setIsDarkTheme] = useState(false); const [isDarkTheme, setIsDarkTheme] = useState(false);
const [currentLanguage, setCurrentLanguage] = useState('en');
const loadDarkThemeFromSettings = () => { const loadDarkThemeFromSettings = () => {
const gotIsDarkThemeFromSettings = settings.hasSync('appIsDarkTheme') const gotIsDarkThemeFromSettings = settings.hasSync('appIsDarkTheme')
@ -46,7 +51,16 @@ export const SettingsProvider: React.FC = ({ children }) => {
setIsDarkTheme(val); setIsDarkTheme(val);
}; };
const value = { isDarkTheme, setIsDarkThemeHook }; const setCurrentLanguageHook = (newLang: string) => {
setCurrentLanguage(newLang);
};
const value = {
isDarkTheme,
setIsDarkThemeHook,
currentLanguage,
setCurrentLanguageHook,
};
return ( return (
<SettingsContext.Provider value={value}> <SettingsContext.Provider value={value}>

View File

@ -17,6 +17,8 @@ jest.mock('electron', () => {
return { return {
createWaitingForConnectionSharingSession: () => createWaitingForConnectionSharingSession: () =>
new Promise(() => {}), new Promise(() => {}),
setAppLanguage: () => {},
setAppTheme: () => {},
}; };
} }
if (globalName === 'connectedDevicesService') { if (globalName === 'connectedDevicesService') {

View File

@ -66,7 +66,7 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
const [isInterShow, setIsInterShow] = useState(false); const [isInterShow, setIsInterShow] = useState(false);
const { isDarkTheme } = useContext(SettingsContext); const { isDarkTheme, currentLanguage } = useContext(SettingsContext);
const { addToast } = useToasts(); const { addToast } = useToasts();
@ -104,8 +104,17 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
}, },
isProduction() ? 500 : 0 isProduction() ? 500 : 0
); );
// sharingSessionService.setAppLanguage(currentLanguage);
// sharingSessionService.setAppTheme(isDarkTheme ? 'dark' : 'light');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
useEffect(() => {
sharingSessionService.setAppLanguage(currentLanguage);
sharingSessionService.setAppTheme(isDarkTheme);
}, [currentLanguage, isDarkTheme]);
const [activeStep, setActiveStep] = useState(0); const [activeStep, setActiveStep] = useState(0);
const [isEntireScreenSelected, setIsEntireScreenSelected] = useState(false); const [isEntireScreenSelected, setIsEntireScreenSelected] = useState(false);
const [ const [
@ -153,7 +162,40 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
setActiveStep(0); setActiveStep(0);
setPendingConnectionDevice(null); setPendingConnectionDevice(null);
setIsUserAllowedConnection(false); setIsUserAllowedConnection(false);
// const sharingSession =
// sharingSessionService.waitingForConnectionSharingSession;
// sharingSession?.disconnectByHostMachineUser();
// sharingSession?.destory();
// sharingSessionService.sharingSessions.delete(sharingSession?.id as string);
// sharingSessionService.waitingForConnectionSharingSession = null;
sharingSessionService
.createWaitingForConnectionSharingSession()
// eslint-disable-next-line promise/always-return
.then((waitingForConnectionSharingSession) => {
waitingForConnectionSharingSession.setOnDeviceConnectedCallback(
(device: Device) => {
connectedDevicesService.setPendingConnectionDevice(device);
}
);
})
.catch((e) => log.error(e));
}, []);
const handleResetWithSharingSessionRestart = useCallback(() => {
makeSmoothIntermediateStepTransition();
setActiveStep(0);
setPendingConnectionDevice(null);
setIsUserAllowedConnection(false);
const sharingSession =
sharingSessionService.waitingForConnectionSharingSession;
sharingSession?.disconnectByHostMachineUser();
sharingSession?.destory();
sharingSessionService.sharingSessions.delete(sharingSession?.id as string);
sharingSessionService.waitingForConnectionSharingSession = null; sharingSessionService.waitingForConnectionSharingSession = null;
sharingSessionService sharingSessionService
.createWaitingForConnectionSharingSession() .createWaitingForConnectionSharingSession()
// eslint-disable-next-line promise/always-return // eslint-disable-next-line promise/always-return
@ -169,7 +211,7 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
React.useImperativeHandle(ref, () => ({ React.useImperativeHandle(ref, () => ({
handleReset() { handleReset() {
handleReset(); handleResetWithSharingSessionRestart();
}, },
})); }));
@ -215,7 +257,7 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
}, [handleNext]); }, [handleNext]);
const handleUserClickedDeviceDisconnectButton = useCallback(async () => { const handleUserClickedDeviceDisconnectButton = useCallback(async () => {
handleReset(); handleResetWithSharingSessionRestart();
addToast( addToast(
<Text> <Text>
@ -224,20 +266,11 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
{ {
appearance: 'info', appearance: 'info',
autoDismiss: true, autoDismiss: true,
// @ts-ignore: works fine here, ignore // @ts-ignore: works fine here
isdarktheme: `${isDarkTheme}`, isdarktheme: `${isDarkTheme}`,
} }
); );
}, [addToast, handleResetWithSharingSessionRestart, isDarkTheme]);
if (sharingSessionService.waitingForConnectionSharingSession !== null) {
const sharingSession =
sharingSessionService.waitingForConnectionSharingSession;
sharingSession.disconnectByHostMachineUser();
sharingSession.destory();
sharingSession.setStatus(SharingSessionStatusEnum.NOT_CONNECTED);
sharingSessionService.sharingSessions.delete(sharingSession.id);
}
}, [addToast, handleReset, isDarkTheme]);
const renderIntermediateOrSuccessStepContent = useCallback(() => { const renderIntermediateOrSuccessStepContent = useCallback(() => {
return activeStep === steps.length ? ( return activeStep === steps.length ? (

View File

@ -57,63 +57,339 @@ exports[`should match exact snapshot 1`] = `
} }
> >
<div <div
className="makeStyles-topPanelRoot-1" className="makeStyles-topPanelRoot-117"
> >
<Zoom <Row
duration={0} center="xs"
top={true} middle="xs"
style={
Object {
"transform": "translateX(-50px)",
"width": "100%",
}
}
> >
<RevealBase <div
fraction={0.2} className="row center-xs middle-xs"
inEffect={ style={
Object { Object {
"count": 1, "transform": "translateX(-50px)",
"delay": 0, "width": "100%",
"duration": 0,
"forever": undefined,
"make": [Function],
"reverse": undefined,
"style": Object {
"animationFillMode": "both",
},
} }
} }
outEffect={
Object {
"count": 1,
"delay": 0,
"duration": 0,
"forever": undefined,
"make": [Function],
"reverse": undefined,
"style": Object {
"animationFillMode": "both",
},
}
}
refProp="ref"
top={true}
> >
<div <Col>
className="react-reveal makeStyles-logoWithAppName-9" <div
id="logo-with-popover-visit-website" className=""
style={
Object {
"opacity": undefined,
}
}
>
<h4
className="makeStyles-appNameHeader-17"
id="deskreen-top-app-name-header"
> >
Deskreen <Blueprint3.Tooltip
</h4> content="If you like Deskreen, consider donating! Deskreen is free and opensource forever! You can help us to make Deskreen even better!"
</div> hoverCloseDelay={0}
</RevealBase> hoverOpenDelay={100}
</Zoom> position="bottom"
transitionDuration={100}
>
<Blueprint3.Popover
autoFocus={false}
boundary="scrollParent"
canEscapeKeyClose={false}
captureDismiss={false}
content="If you like Deskreen, consider donating! Deskreen is free and opensource forever! You can help us to make Deskreen even better!"
defaultIsOpen={false}
disabled={false}
enforceFocus={false}
fill={false}
hasBackdrop={false}
hoverCloseDelay={0}
hoverOpenDelay={100}
inheritDarkTheme={true}
interactionKind="hover-target"
lazy={true}
minimal={false}
modifiers={Object {}}
openOnTargetFocus={true}
popoverClassName="bp3-tooltip"
position="bottom"
targetTagName="span"
transitionDuration={100}
usePortal={true}
wrapperTagName="span"
>
<Manager>
<span
className="bp3-popover-wrapper"
>
<Reference
innerRef={[Function]}
>
<InnerReference
innerRef={[Function]}
setReferenceNode={[Function]}
>
<Blueprint3.ResizeSensor
onResize={[Function]}
>
<span
className="bp3-popover-target"
onBlur={[Function]}
onFocus={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<Blueprint3.Button
className=""
key=".0"
style={
Object {
"marginRight": "10px",
"transform": "translateY(2px)",
}
}
tabIndex={0}
>
<button
className="bp3-button"
onKeyDown={[Function]}
onKeyUp={[Function]}
style={
Object {
"marginRight": "10px",
"transform": "translateY(2px)",
}
}
tabIndex={0}
type="button"
>
<Blueprint3.Icon
key="leftIcon"
/>
<span
className="bp3-button-text"
key="text"
>
<Row
start="xs"
>
<div
className="row start-xs"
>
<Col
xs={true}
>
<div
className="col-xs"
>
<img
alt="heart"
height={16}
src="test-file-stub"
style={
Object {
"transform": "translateY(2px)",
}
}
width={16}
/>
</div>
</Col>
<Col
xs={true}
>
<div
className="col-xs"
>
<div
style={
Object {
"transform": "translateY(2px) translateX(-5px)",
}
}
>
<Blueprint3.Text>
<div
className=""
>
Donate!
</div>
</Blueprint3.Text>
</div>
</div>
</Col>
</div>
</Row>
</span>
<Blueprint3.Icon
key="rightIcon"
/>
</button>
</Blueprint3.Button>
</span>
</Blueprint3.ResizeSensor>
</InnerReference>
</Reference>
<Blueprint3.Overlay
autoFocus={false}
backdropClassName="bp3-popover-backdrop"
backdropProps={Object {}}
canEscapeKeyClose={false}
canOutsideClickClose={false}
enforceFocus={false}
hasBackdrop={false}
isOpen={false}
lazy={true}
onClose={[Function]}
transitionDuration={100}
transitionName="bp3-popover"
usePortal={true}
/>
</span>
</Manager>
</Blueprint3.Popover>
</Blueprint3.Tooltip>
</div>
</Col>
<Col>
<div
className=""
>
<Zoom
duration={0}
top={true}
>
<RevealBase
fraction={0.2}
inEffect={
Object {
"count": 1,
"delay": 0,
"duration": 0,
"forever": undefined,
"make": [Function],
"reverse": undefined,
"style": Object {
"animationFillMode": "both",
},
}
}
outEffect={
Object {
"count": 1,
"delay": 0,
"duration": 0,
"forever": undefined,
"make": [Function],
"reverse": undefined,
"style": Object {
"animationFillMode": "both",
},
}
}
refProp="ref"
top={true}
>
<div
className="react-reveal makeStyles-logoWithAppName-125"
id="logo-with-popover-visit-website"
style={
Object {
"opacity": undefined,
}
}
>
<Blueprint3.Tooltip
content="Click to visit our website"
hoverCloseDelay={0}
hoverOpenDelay={100}
position="bottom"
transitionDuration={100}
>
<Blueprint3.Popover
autoFocus={false}
boundary="scrollParent"
canEscapeKeyClose={false}
captureDismiss={false}
content="Click to visit our website"
defaultIsOpen={false}
disabled={false}
enforceFocus={false}
fill={false}
hasBackdrop={false}
hoverCloseDelay={0}
hoverOpenDelay={100}
inheritDarkTheme={true}
interactionKind="hover-target"
lazy={true}
minimal={false}
modifiers={Object {}}
openOnTargetFocus={true}
popoverClassName="bp3-tooltip"
position="bottom"
targetTagName="span"
transitionDuration={100}
usePortal={true}
wrapperTagName="span"
>
<Manager>
<span
className="bp3-popover-wrapper"
>
<Reference
innerRef={[Function]}
>
<InnerReference
innerRef={[Function]}
setReferenceNode={[Function]}
>
<Blueprint3.ResizeSensor
onResize={[Function]}
>
<span
className="bp3-popover-target"
onBlur={[Function]}
onFocus={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<h4
className="makeStyles-appNameHeader-133"
id="deskreen-top-app-name-header"
key=".0"
tabIndex={0}
>
Deskreen
</h4>
</span>
</Blueprint3.ResizeSensor>
</InnerReference>
</Reference>
<Blueprint3.Overlay
autoFocus={false}
backdropClassName="bp3-popover-backdrop"
backdropProps={Object {}}
canEscapeKeyClose={false}
canOutsideClickClose={false}
enforceFocus={false}
hasBackdrop={false}
isOpen={false}
lazy={true}
onClose={[Function]}
transitionDuration={100}
transitionName="bp3-popover"
usePortal={true}
/>
</span>
</Manager>
</Blueprint3.Popover>
</Blueprint3.Tooltip>
</div>
</RevealBase>
</Zoom>
</div>
</Col>
</div>
</Row>
<div <div
className="makeStyles-topPanelControlButtonsRoot-25" className="makeStyles-topPanelControlButtonsRoot-141"
> >
<Fade <Fade
duration={0} duration={0}
@ -152,7 +428,7 @@ exports[`should match exact snapshot 1`] = `
right={true} right={true}
> >
<div <div
className="react-reveal makeStyles-topPanelControlButtonMargin-34" className="react-reveal makeStyles-topPanelControlButtonMargin-150"
style={ style={
Object { Object {
"opacity": undefined, "opacity": undefined,
@ -214,15 +490,15 @@ exports[`should match exact snapshot 1`] = `
onMouseLeave={[Function]} onMouseLeave={[Function]}
> >
<Blueprint3.Button <Blueprint3.Button
className="makeStyles-topPanelControlButton-40" className="makeStyles-topPanelControlButton-156"
id="top-panel-connected-devices-list-button" id="top-panel-connected-devices-list-button"
intent="none" intent="primary"
key=".0" key=".0"
onClick={[Function]} onClick={[Function]}
tabIndex={0} tabIndex={0}
> >
<button <button
className="bp3-button makeStyles-topPanelControlButton-40" className="bp3-button bp3-intent-primary makeStyles-topPanelControlButton-156"
id="top-panel-connected-devices-list-button" id="top-panel-connected-devices-list-button"
onClick={[Function]} onClick={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@ -238,12 +514,12 @@ exports[`should match exact snapshot 1`] = `
key="text" key="text"
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-49" className="makeStyles-topPanelIconOfControlButton-165"
icon="th-list" icon="th-list"
iconSize={20} iconSize={20}
> >
<span <span
className="bp3-icon bp3-icon-th-list makeStyles-topPanelIconOfControlButton-49" className="bp3-icon bp3-icon-th-list makeStyles-topPanelIconOfControlButton-165"
icon="th-list" icon="th-list"
> >
<svg <svg
@ -327,7 +603,7 @@ exports[`should match exact snapshot 1`] = `
right={true} right={true}
> >
<div <div
className="react-reveal makeStyles-topPanelControlButtonMargin-55" className="react-reveal makeStyles-topPanelControlButtonMargin-171"
style={ style={
Object { Object {
"opacity": undefined, "opacity": undefined,
@ -335,7 +611,7 @@ exports[`should match exact snapshot 1`] = `
} }
> >
<Blueprint3.Tooltip <Blueprint3.Tooltip
content="Help" content="Tutorial"
hoverCloseDelay={0} hoverCloseDelay={0}
hoverOpenDelay={100} hoverOpenDelay={100}
position="bottom" position="bottom"
@ -346,7 +622,7 @@ exports[`should match exact snapshot 1`] = `
boundary="scrollParent" boundary="scrollParent"
canEscapeKeyClose={false} canEscapeKeyClose={false}
captureDismiss={false} captureDismiss={false}
content="Help" content="Tutorial"
defaultIsOpen={false} defaultIsOpen={false}
disabled={false} disabled={false}
enforceFocus={false} enforceFocus={false}
@ -389,14 +665,14 @@ exports[`should match exact snapshot 1`] = `
onMouseLeave={[Function]} onMouseLeave={[Function]}
> >
<Blueprint3.Button <Blueprint3.Button
className="makeStyles-topPanelControlButton-61" className="makeStyles-topPanelControlButton-177"
id="top-panel-help-button" id="top-panel-help-button"
intent="none" intent="none"
key=".0" key=".0"
tabIndex={0} tabIndex={0}
> >
<button <button
className="bp3-button makeStyles-topPanelControlButton-61" className="bp3-button makeStyles-topPanelControlButton-177"
id="top-panel-help-button" id="top-panel-help-button"
onKeyDown={[Function]} onKeyDown={[Function]}
onKeyUp={[Function]} onKeyUp={[Function]}
@ -411,28 +687,33 @@ exports[`should match exact snapshot 1`] = `
key="text" key="text"
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-70" className="makeStyles-topPanelIconOfControlButton-186"
icon="help" icon="learning"
iconSize={22} iconSize={22}
> >
<span <span
className="bp3-icon bp3-icon-help makeStyles-topPanelIconOfControlButton-70" className="bp3-icon bp3-icon-learning makeStyles-topPanelIconOfControlButton-186"
icon="help" icon="learning"
> >
<svg <svg
data-icon="help" data-icon="learning"
height={22} height={22}
viewBox="0 0 20 20" viewBox="0 0 20 20"
width={22} width={22}
> >
<desc> <desc>
help learning
</desc> </desc>
<path <path
d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zM7.41 4.62c.65-.54 1.51-.82 2.56-.82.54 0 1.03.08 1.48.25.44.17.83.39 1.14.68.32.29.56.63.74 1.02.17.39.26.82.26 1.27s-.08.87-.24 1.23c-.16.37-.4.73-.71 1.11l-1.21 1.58c-.14.17-.28.33-.32.48-.05.15-.11.35-.11.6v.97H9v-2s.06-.58.24-.81l1.21-1.64c.25-.3.41-.56.51-.77s.14-.44.14-.67c0-.35-.11-.63-.32-.85s-.5-.33-.88-.33c-.37 0-.67.11-.89.33-.22.23-.37.54-.46.94-.03.12-.11.17-.23.16l-1.95-.29c-.12-.01-.16-.08-.14-.22.13-.93.52-1.67 1.18-2.22zM9 14h2.02L11 16H9v-2z" d="M10.551 1.127a1.256 1.256 0 00-1.102 0L.456 5.89c-.608.309-.608.913 0 1.222l8.993 4.762c.334.17.767.17 1.102 0l8.992-4.762c.61-.309.61-.913 0-1.222l-8.992-4.762z"
fillRule="evenodd" fillRule="evenodd"
key="0" key="0"
/> />
<path
d="M18 6.5l.016 4.514c.002.548.447.99.994.99a.99.99 0 00.99-.99V6.5h-2zM3.366 10.033l6.401 3.358a.5.5 0 00.465 0l6.406-3.358a.25.25 0 01.366.221v5.109a.25.25 0 01-.139.224l-6.64 3.302a.5.5 0 01-.446 0l-6.64-3.302A.25.25 0 013 15.363v-5.108a.25.25 0 01.366-.222z"
fillRule="evenodd"
key="1"
/>
</svg> </svg>
</span> </span>
</Blueprint3.Icon> </Blueprint3.Icon>
@ -500,7 +781,7 @@ exports[`should match exact snapshot 1`] = `
right={true} right={true}
> >
<div <div
className="react-reveal makeStyles-topPanelControlButtonMargin-76" className="react-reveal makeStyles-topPanelControlButtonMargin-192"
style={ style={
Object { Object {
"opacity": undefined, "opacity": undefined,
@ -562,14 +843,14 @@ exports[`should match exact snapshot 1`] = `
onMouseLeave={[Function]} onMouseLeave={[Function]}
> >
<Blueprint3.Button <Blueprint3.Button
className="makeStyles-topPanelControlButton-82" className="makeStyles-topPanelControlButton-198"
id="top-panel-settings-button" id="top-panel-settings-button"
key=".0" key=".0"
onClick={[Function]} onClick={[Function]}
tabIndex={0} tabIndex={0}
> >
<button <button
className="bp3-button makeStyles-topPanelControlButton-82" className="bp3-button makeStyles-topPanelControlButton-198"
id="top-panel-settings-button" id="top-panel-settings-button"
onClick={[Function]} onClick={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@ -585,12 +866,12 @@ exports[`should match exact snapshot 1`] = `
key="text" key="text"
> >
<Blueprint3.Icon <Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-91" className="makeStyles-topPanelIconOfControlButton-207"
icon="cog" icon="cog"
iconSize={22} iconSize={22}
> >
<span <span
className="bp3-icon bp3-icon-cog makeStyles-topPanelIconOfControlButton-91" className="bp3-icon bp3-icon-cog makeStyles-topPanelIconOfControlButton-207"
icon="cog" icon="cog"
> >
<svg <svg
@ -1773,23 +2054,31 @@ exports[`should match exact snapshot 1`] = `
} }
} }
> >
<Blueprint3.Text>
<div
className=""
>
<span
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "8px",
"paddingRight": "8px",
}
}
>
make sure your computer and device are connected to same WiFi
</span>
</div>
</Blueprint3.Text>
<Blueprint3.Text <Blueprint3.Text
className="bp3-text" className="bp3-text"
> >
<div <div
className="bp3-text" className="bp3-text"
> />
Scan the QR code
</div>
</Blueprint3.Text>
<Blueprint3.Text
className="bp3-text-muted"
>
<div
className="bp3-text-muted"
>
( make sure your computer and device are connected on same WiFi )
</div>
</Blueprint3.Text> </Blueprint3.Text>
</div> </div>
<div> <div>
@ -2122,7 +2411,7 @@ exports[`should match exact snapshot 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}
@ -2142,7 +2431,7 @@ exports[`should match exact snapshot 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}

View File

@ -1059,23 +1059,31 @@ exports[`should match exact snapshot 1`] = `
} }
} }
> >
<Blueprint3.Text>
<div
className=""
>
<span
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "8px",
"paddingRight": "8px",
}
}
>
make sure your computer and device are connected to same WiFi
</span>
</div>
</Blueprint3.Text>
<Blueprint3.Text <Blueprint3.Text
className="bp3-text" className="bp3-text"
> >
<div <div
className="bp3-text" className="bp3-text"
> />
Scan the QR code
</div>
</Blueprint3.Text>
<Blueprint3.Text
className="bp3-text-muted"
>
<div
className="bp3-text-muted"
>
( make sure your computer and device are connected on same WiFi )
</div>
</Blueprint3.Text> </Blueprint3.Text>
</div> </div>
<div> <div>
@ -1408,7 +1416,7 @@ exports[`should match exact snapshot 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}
@ -1428,7 +1436,7 @@ exports[`should match exact snapshot 1`] = `
style={ style={
Object { Object {
"position": "relative", "position": "relative",
"top": "-30px", "top": "0px",
} }
} }
transitionDuration={0} transitionDuration={0}

View File

@ -2,8 +2,8 @@ export default async (
sourceID: string, sourceID: string,
width: number | null | undefined = undefined, width: number | null | undefined = undefined,
height: number | null | undefined = undefined, height: number | null | undefined = undefined,
minSizeDivisor = 1, minSizeMultiplier = 1,
maxSizeDivisor = 1, maxSizeMultiplier = 1,
minFrameRate = 15, minFrameRate = 15,
maxFrameRate = 60 maxFrameRate = 60
) => { ) => {
@ -17,10 +17,10 @@ export default async (
chromeMediaSource: 'desktop', chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceID, chromeMediaSourceId: sourceID,
minWidth: width / minSizeDivisor, minWidth: width * minSizeMultiplier,
maxWidth: width / maxSizeDivisor, maxWidth: width * maxSizeMultiplier,
minHeight: height / minSizeDivisor, minHeight: height * minSizeMultiplier,
maxHeight: height / maxSizeDivisor, maxHeight: height * maxSizeMultiplier,
minFrameRate, minFrameRate,
maxFrameRate, maxFrameRate,

View File

@ -18,6 +18,7 @@ import Logger from '../../utils/logger';
import DesktopCapturerSources from '../DesktopCapturerSourcesService'; import DesktopCapturerSources from '../DesktopCapturerSourcesService';
import setSdpMediaBitrate from './setSdpMediaBitrate'; import setSdpMediaBitrate from './setSdpMediaBitrate';
import getDesktopSourceStreamBySourceID from './getDesktopSourceStreamBySourceID'; import getDesktopSourceStreamBySourceID from './getDesktopSourceStreamBySourceID';
import prepareDataMessageToSendScreenSourceType from './prepareDataMessageToSendScreenSourceType';
const log = new Logger(__filename); const log = new Logger(__filename);
@ -69,11 +70,15 @@ export default class PeerConnection {
prevStreamHeight: number; prevStreamHeight: number;
displayID: string; displayID: string;
sourceDisplaySize: DisplaySize | undefined; sourceDisplaySize: DisplaySize | undefined;
appLanguage: string;
appColorTheme: boolean;
constructor( constructor(
roomID: string, roomID: string,
sharingSessionID: string, sharingSessionID: string,
user: LocalPeerUser, user: LocalPeerUser,
appColorTheme: boolean,
appLanguage: string,
roomIDService: RoomIDService, roomIDService: RoomIDService,
connectedDevicesService: ConnectedDevicesService, connectedDevicesService: ConnectedDevicesService,
sharingSessionsService: SharingSessionsService sharingSessionsService: SharingSessionsService
@ -96,11 +101,37 @@ export default class PeerConnection {
this.prevStreamHeight = -1; this.prevStreamHeight = -1;
this.displayID = ''; this.displayID = '';
this.sourceDisplaySize = undefined; this.sourceDisplaySize = undefined;
this.appLanguage = appLanguage;
this.appColorTheme = appColorTheme;
this.onDeviceConnectedCallback = () => {}; this.onDeviceConnectedCallback = () => {};
this.initSocketWhenUserCreatedCallback(); this.initSocketWhenUserCreatedCallback();
} }
setAppLanguage(lang: string) {
this.appLanguage = lang;
this.notifyClientWithNewLanguage();
}
setAppTheme(theme: boolean) {
this.appColorTheme = theme;
this.notifyClientWithNewColorTheme();
}
notifyClientWithNewLanguage() {
this.sendEncryptedMessage({
type: 'APP_LANGUAGE',
payload: { value: this.appLanguage },
});
}
notifyClientWithNewColorTheme() {
this.sendEncryptedMessage({
type: 'APP_THEME',
payload: { value: this.appColorTheme },
});
}
setDesktopCapturerSourceID(id: string) { setDesktopCapturerSourceID(id: string) {
this.desktopCapturerSourceID = id; this.desktopCapturerSourceID = id;
if (process.env.RUN_MODE === 'test') return; if (process.env.RUN_MODE === 'test') return;
@ -137,9 +168,14 @@ export default class PeerConnection {
this.sendEncryptedMessage({ this.sendEncryptedMessage({
type: 'DENY_TO_CONNECT', type: 'DENY_TO_CONNECT',
payload: {}, payload: {},
}); })
// eslint-disable-next-line promise/always-return
this.disconnectPartner(); .then(() => {
this.disconnectPartner();
})
.catch((e) => {
log.error(e);
});
} }
sendUserAllowedToConnect() { sendUserAllowedToConnect() {
@ -153,10 +189,15 @@ export default class PeerConnection {
this.sendEncryptedMessage({ this.sendEncryptedMessage({
type: 'DISCONNECT_BY_HOST_MACHINE_USER', type: 'DISCONNECT_BY_HOST_MACHINE_USER',
payload: {}, payload: {},
}); })
// eslint-disable-next-line promise/always-return
this.disconnectPartner(); .then(() => {
this.selfDestrory(); this.disconnectPartner();
this.selfDestrory();
})
.catch((e) => {
log.error(e);
});
} }
disconnectPartner() { disconnectPartner() {
@ -284,13 +325,10 @@ export default class PeerConnection {
async receiveEncryptedMessage(payload: ReceiveEncryptedMessagePayload) { async receiveEncryptedMessage(payload: ReceiveEncryptedMessagePayload) {
if (!this.user) return; if (!this.user) return;
const message = await processMessage(payload, this.user.privateKey); const message = await processMessage(payload, this.user.privateKey);
log.info(message);
if (message.type === 'CALL_ACCEPTED') { if (message.type === 'CALL_ACCEPTED') {
this.peer.signal(message.payload.signalData); this.peer.signal(message.payload.signalData);
} }
if (message.type === 'DEVICE_DETAILS') { if (message.type === 'DEVICE_DETAILS') {
log.info(message);
log.info(message.payload.browser);
this.socket.emit( this.socket.emit(
'GET_IP_BY_SOCKET_ID', 'GET_IP_BY_SOCKET_ID',
message.payload.socketID, message.payload.socketID,
@ -310,6 +348,18 @@ export default class PeerConnection {
} }
); );
} }
if (message.type === 'GET_APP_THEME') {
this.sendEncryptedMessage({
type: 'APP_THEME',
payload: { value: this.appColorTheme },
});
}
if (message.type === 'GET_APP_LANGUAGE') {
this.sendEncryptedMessage({
type: 'APP_LANGUAGE',
payload: { value: this.appLanguage },
});
}
} }
callPeer() { callPeer() {
@ -358,18 +408,21 @@ export default class PeerConnection {
this.peer = peer; this.peer = peer;
this.peer.on('data', async (data) => { this.peer.on('data', async (data) => {
if (`${data}` === 'set half quality') { const dataJSON = JSON.parse(data);
// TODO: later on change to more sophisticated quality change for app window
if (dataJSON.type === 'set_video_quality') {
const maxVideoQualityMultiplier = dataJSON.payload.value;
const minVideoQualityMultiplier =
maxVideoQualityMultiplier === 1 ? 0.5 : maxVideoQualityMultiplier;
if (!this.desktopCapturerSourceID.includes('screen')) return; if (!this.desktopCapturerSourceID.includes('screen')) return;
const newStream = await getDesktopSourceStreamBySourceID( const newStream = await getDesktopSourceStreamBySourceID(
this.desktopCapturerSourceID, this.desktopCapturerSourceID,
this.sourceDisplaySize?.width, this.sourceDisplaySize?.width,
this.sourceDisplaySize?.height, this.sourceDisplaySize?.height,
2, minVideoQualityMultiplier,
2, maxVideoQualityMultiplier
15,
30
); );
const newVideoTrack = newStream.getVideoTracks()[0]; const newVideoTrack = newStream.getVideoTracks()[0];
const oldTrack = this.localStream?.getVideoTracks()[0]; const oldTrack = this.localStream?.getVideoTracks()[0];
@ -378,23 +431,14 @@ export default class PeerConnection {
peer.replaceTrack(oldTrack, newVideoTrack, this.localStream); peer.replaceTrack(oldTrack, newVideoTrack, this.localStream);
oldTrack.stop(); oldTrack.stop();
} }
} else if (`${data}` === 'set good quality') { }
// TODO: later on change to more sophisticated quality change for app window
if (!this.desktopCapturerSourceID.includes('screen')) return;
const newStream = await getDesktopSourceStreamBySourceID( if (dataJSON.type === 'get_sharing_source_type') {
this.desktopCapturerSourceID, const sourceType = this.desktopCapturerSourceID.includes('screen')
this.sourceDisplaySize?.width, ? 'screen'
this.sourceDisplaySize?.height, : 'window';
2,
1 this.peer.send(prepareDataMessageToSendScreenSourceType(sourceType));
);
const newVideoTrack = newStream.getVideoTracks()[0];
const oldTrack = this.localStream?.getVideoTracks()[0];
if (oldTrack && this.localStream) {
peer.replaceTrack(oldTrack, newVideoTrack, this.localStream);
oldTrack.stop();
}
} }
}); });
return peer; return peer;
@ -419,7 +463,7 @@ export default class PeerConnection {
sourceID, sourceID,
this.sourceDisplaySize?.width, this.sourceDisplaySize?.width,
this.sourceDisplaySize?.height, this.sourceDisplaySize?.height,
2, 0.5,
1 1
).then((stream) => { ).then((stream) => {
this.localStream = stream; this.localStream = stream;

View File

@ -0,0 +1,10 @@
export default (s: string) => {
return `
{
"type": "screen_sharing_source_type",
"payload": {
"value": "${s}"
}
}
`;
};

View File

@ -26,7 +26,9 @@ describe('SharingSession unit tests', () => {
}, },
}; };
}, },
} },
'',
''
); );
}); });

View File

@ -24,7 +24,9 @@ export default class SharingSession {
constructor( constructor(
_roomID: string, _roomID: string,
user: LocalPeerUser, user: LocalPeerUser,
peerConnectionHelperRendererService: PeerConnectionHelperRendererService peerConnectionHelperRendererService: PeerConnectionHelperRendererService,
appLanguage: string,
isDarkTheme: boolean
) { ) {
this.id = uuid.v4(); this.id = uuid.v4();
this.deviceID = ''; this.deviceID = '';
@ -49,6 +51,8 @@ export default class SharingSession {
roomID: this.roomID, roomID: this.roomID,
sharingSessionID: this.id, sharingSessionID: this.id,
user, user,
appTheme: isDarkTheme,
appLanguage,
} }
); );
}); });
@ -107,6 +111,20 @@ export default class SharingSession {
); );
} }
appLanguageChanged(newLang: string) {
this.peerConnectionHelperRenderer?.webContents.send(
'app-language-changed',
newLang
);
}
appThemeChanged(isDarkTheme: boolean) {
this.peerConnectionHelperRenderer?.webContents.send(
'app-color-theme-changed',
isDarkTheme
);
}
addStatusChangeListener(callback: SharingSessionStatusChangeListener): void { addStatusChangeListener(callback: SharingSessionStatusChangeListener): void {
this.statusChangeListeners.push(callback); this.statusChangeListeners.push(callback);
} }

View File

@ -17,6 +17,8 @@ export default class SharingSessionService {
connectedDevicesService: ConnectedDevicesService; connectedDevicesService: ConnectedDevicesService;
rendererWebrtcHelpersService: RendererWebrtcHelpersService; rendererWebrtcHelpersService: RendererWebrtcHelpersService;
isCreatingNewSharingSession: boolean; isCreatingNewSharingSession: boolean;
appLanguage = 'en';
isDarkTheme = false;
constructor( constructor(
_roomIDService: RoomIDService, _roomIDService: RoomIDService,
@ -38,6 +40,14 @@ export default class SharingSessionService {
}, 1000 * 60 * 60); // every hour }, 1000 * 60 * 60); // every hour
} }
setAppLanguage(newLang: string): void {
this.appLanguage = newLang;
}
setAppTheme(isDarkTheme: boolean): void {
this.isDarkTheme = isDarkTheme;
}
createUser(): Promise<undefined> { createUser(): Promise<undefined> {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
@ -75,10 +85,13 @@ export default class SharingSessionService {
createNewSharingSession(_roomID: string): SharingSession { createNewSharingSession(_roomID: string): SharingSession {
const roomID = _roomID || this.roomIDService.getSimpleAvailableRoomID(); const roomID = _roomID || this.roomIDService.getSimpleAvailableRoomID();
this.roomIDService.markRoomIDAsTaken(roomID);
const sharingSession = new SharingSession( const sharingSession = new SharingSession(
roomID, roomID,
this.user as LocalPeerUser, this.user as LocalPeerUser,
this.rendererWebrtcHelpersService this.rendererWebrtcHelpersService,
this.appLanguage,
this.isDarkTheme
); );
this.sharingSessions.set(sharingSession.id, sharingSession); this.sharingSessions.set(sharingSession.id, sharingSession);
return sharingSession; return sharingSession;
@ -87,7 +100,6 @@ export default class SharingSessionService {
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
changeSharingSessionStatusToSharing(sharingSession: SharingSession) { changeSharingSessionStatusToSharing(sharingSession: SharingSession) {
sharingSession.status = SharingSessionStatusEnum.SHARING; sharingSession.status = SharingSessionStatusEnum.SHARING;
this.roomIDService.markRoomIDAsTaken(sharingSession.roomID);
} }
pollForInactiveSessions(): void { pollForInactiveSessions(): void {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -3,5 +3,6 @@
"Signaling server is running on port": "Signaling server is running on port ⚓", "Signaling server is running on port": "Signaling server is running on port ⚓",
"ru": "Русский", "ru": "Русский",
"en": "English", "en": "English",
"ua": "Українська" "ua": "Українська",
"Scan the QR code": "Scan the QR code"
} }

View File

@ -1,4 +0,0 @@
{
"ru": "ru",
"en": "en"
}

View File

@ -3,5 +3,6 @@
"Signaling server is running on port": "Сигнальный сервер работает на порте ⚓", "Signaling server is running on port": "Сигнальный сервер работает на порте ⚓",
"ru": "Русский", "ru": "Русский",
"en": "English", "en": "English",
"ua": "Українська" "ua": "Українська",
"Scan the QR code": "Отсканируйте QR код"
} }

View File

@ -3,5 +3,6 @@
"Signaling server is running on port": "Сигнальный сервер працює на порту ⚓", "Signaling server is running on port": "Сигнальный сервер працює на порту ⚓",
"ru": "Русский", "ru": "Русский",
"en": "English", "en": "English",
"ua": "Українська" "ua": "Українська",
"Scan the QR code": "Відскануйте QR код"
} }

View File

@ -19,6 +19,8 @@ ipcRenderer.on('create-peer-connection-with-data', (_, data) => {
data.roomID, data.roomID,
data.sharingSessionID, data.sharingSessionID,
data.user, data.user,
data.appTheme, // TODO getAppTheme
data.appLanguage, // TODO getLanguage
roomIDService, roomIDService,
connectedDevicesService, connectedDevicesService,
sharingSessionsService sharingSessionsService
@ -48,3 +50,11 @@ ipcRenderer.on('deny-connection-for-partner', () => {
ipcRenderer.on('send-user-allowed-to-connect', () => { ipcRenderer.on('send-user-allowed-to-connect', () => {
peerConnection.sendUserAllowedToConnect(); peerConnection.sendUserAllowedToConnect();
}); });
ipcRenderer.on('app-color-theme-changed', (_, newTheme: boolean) => {
peerConnection.setAppTheme(newTheme);
});
ipcRenderer.on('app-language-changed', (_, newLang: string) => {
peerConnection.setAppLanguage(newLang);
});

View File

@ -33,4 +33,8 @@ export default class RoomIDService {
public unmarkRoomIDAsTaken(id: string) { public unmarkRoomIDAsTaken(id: string) {
this.takenRoomIDs.delete(id); this.takenRoomIDs.delete(id);
} }
public isRoomIDTaken(id: string) {
return this.takenRoomIDs.has(id);
}
} }

View File

@ -12,6 +12,8 @@ import { getIO } from '.';
import socketsIPService from './socketsIPService'; import socketsIPService from './socketsIPService';
import getStore from './store'; import getStore from './store';
const LOCALHOST_SOCKET_IP = '::1';
interface User { interface User {
socketId: string; socketId: string;
publicKey: string; publicKey: string;
@ -152,7 +154,8 @@ export default class Socket implements SocketOPTS {
{ {
socketId: socket.id, socketId: socket.id,
publicKey: payload.publicKey, publicKey: payload.publicKey,
isOwner: (room.users || []).length === 0, isOwner:
LOCALHOST_SOCKET_IP === socket.request.connection.remoteAddress,
ip: payload.ip ? payload.ip : '', ip: payload.ip ? payload.ip : '',
}, },
], ],

View File

@ -22,6 +22,7 @@ import getStore from './store';
import Logger from '../utils/logger'; import Logger from '../utils/logger';
import isProduction from '../utils/isProduction'; import isProduction from '../utils/isProduction';
import SocketsIPService from './socketsIPService'; import SocketsIPService from './socketsIPService';
import getDeskreenGlobal from '../mainProcessHelpers/getDeskreenGlobal';
const log = new Logger('app/server/index.ts'); const log = new Logger('app/server/index.ts');
@ -90,25 +91,34 @@ io.sockets.on('connection', (socket) => {
io.on('connection', async (socket) => { io.on('connection', async (socket) => {
const { roomId } = socket.handshake.query; const { roomId } = socket.handshake.query;
const roomIdHash = getRoomIdHash(roomId); setTimeout(async () => {
if (!getDeskreenGlobal().roomIDService.isRoomIDTaken(roomId)) {
socket.emit('NOT_ALLOWED');
setTimeout(() => {
socket.disconnect();
}, 1000);
} else {
const roomIdHash = getRoomIdHash(roomId);
let room = await store.get('rooms', roomIdHash); let room = await store.get('rooms', roomIdHash);
room = JSON.parse(room || '{}'); room = JSON.parse(room || '{}');
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new DarkwireSocket({ new DarkwireSocket({
roomIdOriginal: roomId, roomIdOriginal: roomId,
roomId: roomIdHash, roomId: roomIdHash,
socket, socket,
room, room,
}); });
}
}, 1000); // timeout 1 second for throttling malitios connections
}); });
const init = async (PORT: number) => { const init = async (PORT: number) => {
pollForInactiveRooms(); pollForInactiveRooms();
return server.listen(PORT, () => { return server.listen(PORT, () => {
log.info(`Signaling server is online at port ${PORT}`); log.info(`Deskreen signaling server is online at port ${PORT}`);
}); });
}; };

View File

@ -17,6 +17,18 @@ type DeviceDetailsMessageWithPayload = {
}; };
}; };
type GetAppThemeMessageWithPayload = {
type: 'GET_APP_THEME';
payload: Record<string, unknown>;
};
type GetAppLanguageMessageWithPayload = {
type: 'GET_APP_LANGUAGE';
payload: Record<string, unknown>;
};
type ProcessedMessage = type ProcessedMessage =
| CallAcceptedMessageWithPayload | CallAcceptedMessageWithPayload
| DeviceDetailsMessageWithPayload; | DeviceDetailsMessageWithPayload
| GetAppThemeMessageWithPayload
| GetAppLanguageMessageWithPayload;

View File

@ -108,8 +108,8 @@ async function openLargeQRCodeDialog(t) {
await t.click(magnifyQRCodeButton()); await t.click(magnifyQRCodeButton());
} }
async function clickCrossButtonToCloseDialog(t) { async function clickOnLargeQRCodeDialog(t) {
await t.click(crossCloseDialogButton()); await t.click(largeQRCodeDialog());
} }
async function openConnectedDeviceInfoPopover(t) { async function openConnectedDeviceInfoPopover(t) {
@ -239,7 +239,7 @@ test(`when large QR overflow is opened,
it should close large QR code overflow`, async (t) => { it should close large QR code overflow`, async (t) => {
await openLargeQRCodeDialog(t); await openLargeQRCodeDialog(t);
await clickCrossButtonToCloseDialog(t); await clickOnLargeQRCodeDialog(t);
const largeQrCodeDialogExists = largeQRCodeDialog().exists; const largeQrCodeDialogExists = largeQRCodeDialog().exists;
await t.expect(largeQrCodeDialogExists).notOk(); await t.expect(largeQrCodeDialogExists).notOk();