1
0
mirror of https://github.com/pavlobu/deskreen.git synced 2025-05-28 05:10: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-dom": "^16.9.0",
"@types/ua-parser-js": "^0.7.33",
"i18next": "^19.8.4",
"i18next-http-backend": "^1.0.21",
"jest-sonar-reporter": "^2.0.0",
"node-forge": "^0.9.1",
"pixelmatch": "^5.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-flexbox-grid": "^2.1.2",
"react-i18next": "^11.7.4",
"react-player": "^2.6.2",
"react-reveal": "^1.2.2",
"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 { H3, Button } from '@blueprintjs/core';
import React, {
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 { findDOMNode } from 'react-dom';
import ReactPlayer from 'react-player';
import screenfull from 'screenfull';
import Crypto from './utils/crypto';
import './App.css';
import LocalTestPeer from './features/PeerConnection';
import PeerConnection from './features/PeerConnection';
import VideoAutoQualityOptimizer from './features/VideoAutoQualityOptimizer';
import ConnectingIndicator from './components/ConnectingIndicator';
import MyDeviceInfoCard from './components/MyDeviceInfoCard';
@ -15,24 +23,34 @@ import {
LIGHT_UI_BACKGROUND,
} from './constants/styleConstants';
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 Slide = require('react-reveal/Slide');
function getPromptContent(step: number) {
function getPromptContent(step: number, t: TFunction) {
switch (step) {
case 1:
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:
return <H3>Connected!</H3>;
case 3:
return (
<H3>
Wating for user to select source to share from screen sharing
device...
{t(
'Wating for user to select source to share from screen sharing device...'
)}
</H3>
);
default:
@ -41,10 +59,23 @@ function getPromptContent(step: number) {
}
function App() {
const { t } = useTranslation();
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 [promptStep, setPromptStep] = useState(1);
const [dialogErrorMessage, setDialogErrorMessage] = useState<ErrorMessage>(
ErrorMessage.UNKNOWN_ERROR
);
const [connectionIconType, setConnectionIconType] = useState<
'feed' | 'feed-subscribed'
>('feed');
@ -56,43 +87,73 @@ function App() {
});
const [playing, setPlaying] = useState(true);
const [isFullScreenOn, setIsFullScreenOn] = useState(false);
const [url, setUrl] = useState();
const [screenSharingSourceType, setScreenSharingSourceType] = useState<
'screen' | 'window'
>('screen');
const [isWithControls, setIsWithControls] = useState(!screenfull.isEnabled);
const [isShownTextPrompt, setIsShownTextPrompt] = useState(false);
const [isShownSpinnerIcon, setIsShownSpinnerIcon] = useState(false);
const [spinnerIconType, setSpinnerIconType] = useState<
'desktop' | 'application'
>('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(() => {
document.body.style.backgroundColor = isDarkTheme
? DARK_UI_BACKGROUND
: LIGHT_UI_BACKGROUND;
}, [isDarkTheme]);
const peer = new LocalTestPeer(
setUrl,
new Crypto(),
new VideoAutoQualityOptimizer(),
setMyDeviceDetails,
() => {
setConnectionIconType('feed-subscribed');
useEffect(() => {
if (!peer) {
const _peer = new PeerConnection(
setUrl,
new Crypto(),
new VideoAutoQualityOptimizer(),
isDarkTheme,
setMyDeviceDetails,
() => {
setConnectionIconType('feed-subscribed');
setIsShownTextPrompt(false);
setIsShownTextPrompt(true);
setPromptStep(2);
setTimeout(() => {
setIsShownTextPrompt(false);
setIsShownTextPrompt(true);
setPromptStep(3);
}, 2000);
}
);
setPromptStep(2);
setTimeout(() => {
setIsShownTextPrompt(true);
}, 100);
}, []);
setTimeout(() => {
setIsShownTextPrompt(false);
setIsShownTextPrompt(true);
setPromptStep(3);
}, 2000);
},
setScreenSharingSourceType,
setIsDarkThemeHook,
changeLanguage,
setDialogErrorMessage,
setIsErrorDialogOpen
);
setPeer(_peer);
setTimeout(() => {
setIsShownTextPrompt(true);
}, 100);
}
}, [setIsDarkThemeHook, isDarkTheme, peer]);
useEffect(() => {
// infinite use effect
@ -102,22 +163,11 @@ function App() {
spinnerIconType === 'desktop' ? 'application' : 'desktop'
);
}, 1500);
}, [isShownSpinnerIcon]);
}, [isShownSpinnerIcon, spinnerIconType]);
const handleClickFullscreen = () => {
// @ts-ignore Property 'request' does not exist on type '{ isEnabled: false; }'.
screenfull.request(findDOMNode(player.current));
};
const handlePlayPause = () => {
const handlePlayPause = useCallback(() => {
setPlaying(!playing);
};
useEffect(() => {
document.body.style.backgroundColor = isDarkTheme
? DARK_UI_BACKGROUND
: LIGHT_UI_BACKGROUND;
}, [isDarkTheme]);
}, [playing]);
useEffect(() => {
if (url !== undefined) {
@ -157,7 +207,6 @@ function App() {
: LIGHT_UI_BACKGROUND,
}}
>
<ToggleDarkModeSwitch />
<Row
bottom="xs"
style={{
@ -189,7 +238,7 @@ function App() {
style={{ width: '100%' }}
>
<div id="prompt-text" style={{ fontSize: '20px' }}>
{getPromptContent(promptStep)}
{getPromptContent(promptStep, t)}
</div>
</Fade>
</div>
@ -221,22 +270,22 @@ function App() {
height: '100vh',
}}
>
<Button
onClick={() => setIsWithControls(true)}
onTouchStart={() => setIsWithControls(true)}
>
Help! I'm having troubles to zoom in on my device
</Button>
<Button onClick={handlePlayPause} onTouchStart={handlePlayPause}>
PLAY!
</Button>
<Button
onClick={handleClickFullscreen}
onTouchStart={handleClickFullscreen}
>
ENTER FULL SCREEN
</Button>
<ToggleDarkModeSwitch />
<PlayerControlPanel
onSwitchChangedCallback={(isEnabled) => setIsWithControls(isEnabled)}
isDefaultPlayerTurnedOn={isWithControls}
handleClickFullscreen={() => {
if (!screenfull.isEnabled) return;
// @ts-ignore Property 'request' does not exist on type '{ isEnabled: false; }'.
screenfull.request(findDOMNode(player.current));
setIsFullScreenOn(!isFullScreenOn);
}}
handleClickPlayPause={handlePlayPause}
isPlaying={playing}
setVideoQuality={setVideoQuality}
selectedVideoQuality={videoQuality}
screenSharingSourceType={screenSharingSourceType}
toaster={toaster}
/>
<div
id="video-container"
style={{
@ -245,6 +294,7 @@ function App() {
}}
>
<div
id="player-wrapper-id"
className="player-wrapper"
style={{
position: 'relative',
@ -253,19 +303,12 @@ function App() {
>
<ReactPlayer
ref={player}
id="video-local-test-peer-sees"
id={REACT_PLAYER_WRAPPER_ID}
playing={playing}
playsinline={true}
controls={isWithControls}
muted={true}
// url={mergedStream}
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%"
height="100%"
style={{
@ -278,6 +321,11 @@ function App() {
<canvas id="comparison-canvas" style={{ display: 'none' }}></canvas>
</div>
</div>
<Toaster ref={refHandlers.toaster} position={Position.TOP_LEFT} />
<ErrorDialog
errorMessage={dialogErrorMessage}
isOpen={isErrorDialogOpen}
/>
</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 { Callout, Card, H3, Text, Tooltip, Position } from '@blueprintjs/core';
import { AppContext } from '../../providers/AppContextProvider';
const LIGHT_UI_BACKGROUND = 'rgba(240, 248, 250, 1)';
const DARK_UI_BACKGROUND = '#293742';
import {
DARK_UI_BACKGROUND,
LIGHT_UI_BACKGROUND,
} from '../../constants/styleConstants';
interface MyDeviceDetailsCardProps {
deviceDetails: DeviceDetails;
@ -27,10 +28,18 @@ function MyDeviceInfoCard(props: MyDeviceDetailsCardProps) {
<Callout>
<Text>Device Type: {myDeviceType}</Text>
<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}
>
<div style={{ fontWeight: 900, backgroundColor: '#00f99273' }}>
<div
style={{
fontWeight: 900,
backgroundColor: '#00f99273',
paddingLeft: '10px',
paddingRight: '10px',
borderRadius: '20px',
}}
>
<Text>Device IP: {myIP}</Text>
</div>
</Tooltip>
@ -38,8 +47,8 @@ function MyDeviceInfoCard(props: MyDeviceDetailsCardProps) {
<Text>Device OS: {myOS}</Text>
</Callout>
<Text className="bp3-text-muted">
These details should match with the ones that you see in alert on
sharing device.
These details should match with the ones that you see in alert popup on
screen sharing device.
</Text>
</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 { Icon, Text, Switch, Classes, Alignment } from '@blueprintjs/core';
import { Row, Col } from 'react-flexbox-grid';
import { Switch, Classes, Alignment } from '@blueprintjs/core';
import { AppContext } from '../../providers/AppContextProvider';
function ToggleDarkModeSwitch() {
const { isDarkTheme, setIsDarkThemeHook } = useContext(AppContext)
return (
<Switch
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 Crypto from '../../utils/crypto';
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 {
username: string;
@ -37,7 +46,7 @@ interface ReceiveEncryptedMessagePayload {
keys: { sessionKey: string; signingKey: string }[];
}
export default class LocalTestPeer {
export default class PeerConnection {
roomId: string;
socket: any;
@ -48,7 +57,7 @@ export default class LocalTestPeer {
partner: PartnerPeerUser = nullUser;
peer: any;
peer: null | SimplePeer.Instance = null;
myIP = '';
@ -72,24 +81,49 @@ export default class LocalTestPeer {
largeMismatchFramesCount: number;
isRequestedHalfQuality: boolean;
screenSharingSourceType: string | undefined = undefined;
videoQuality = VideoQuality.Q_AUTO;
videoAutoQualityOptimizer: VideoAutoQualityOptimizer;
isDarkTheme: boolean;
isStreamStarted: boolean = false;
setMyDeviceDetails: (details: DeviceDetails) => 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(
setUrlCallback: any,
crypto: Crypto,
videoAutoQualityOptimizer: VideoAutoQualityOptimizer,
isDarkTheme: boolean,
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.crypto = crypto;
this.videoAutoQualityOptimizer = videoAutoQualityOptimizer;
this.isDarkTheme = isDarkTheme;
this.setMyDeviceDetails = setMyDeviceDetailsCallback;
this.hostAllowedToConnectCallback = hostAllowedToConnectCallback;
this.roomId = encodeURI(window.location.pathname.replace('/', ''));
@ -97,15 +131,51 @@ export default class LocalTestPeer {
this.uaParser = new UAParser();
this.createUserAndInitSocket();
this.createPeer();
this.setScreenSharingSourceTypeCallback = setScreenSharingSourceTypeCallback;
this.setIsDarkThemeCallback = setIsDarkThemeCallback;
this.setAppLanguageCallback = setAppLanguageCallback;
this.setDialogErrorMessageCallback = setDialogErrorMessageCallback;
this.setIsErrorDialogOpen = setIsErrorDialogOpen;
this.video = null;
this.canvas = null;
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[]) {
console.log('LocalTestPeer - ', ...toLog);
setVideoQuality(videoQuality: VideoQuality) {
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() {
@ -127,23 +197,26 @@ export default class LocalTestPeer {
});
peer.on('stream', (stream) => {
setTimeout(() => {
(document.querySelector(
'#video-local-test-peer-sees'
) as any).srcObject = stream;
}, 1000);
this.videoAutoQualityOptimizer.setGoodQualityCallback(() => {
this.peer.send('set good quality');
if (this.videoQuality === VideoQuality.Q_AUTO) {
this.peer?.send(prepareDataMessageToChangeQuality(1));
}
});
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.setUrlCallback(stream);
setTimeout(() => {
this.peer?.send(prepareDataMessageToGetSharingSourceType());
}, 1000);
this.isStreamStarted = true;
});
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;
}
@ -193,7 +280,6 @@ export default class LocalTestPeer {
if (!this.user) return;
if (!this.partner) return;
const msg = (await prepareMessage(payload, this.user, this.partner)) as any;
this.log('encrypted message', msg);
this.socket.emit('ENCRYPTED_MESSAGE', msg.toSend);
}
@ -204,18 +290,34 @@ export default class LocalTestPeer {
this.user.privateKey
)) as any;
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') {
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') {
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') {
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() {
@ -228,36 +330,33 @@ export default class LocalTestPeer {
this.socket.on('disconnect', () => {
// 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.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.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.myDeviceType = getDeviceTypeFromUAParser(this.uaParser);
// this.myBrowser = `${browserFromUAParser.name ? browserFromUAParser.name : ''} ${
// browserFromUAParser.version ? browserFromUAParser.version : ''
// }`;
this.myBrowser = getBrowserFromUAParser(this.uaParser);
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[] }) => {
const filteredPartner = payload.users.filter((v) => {
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(() => {
this.setMyDeviceDetails({
myIP: this.myIP,
@ -313,6 +415,11 @@ export default class LocalTestPeer {
// TODO: call ROOM LOCKED callback to change react component contain ROOM LOCKED message
// @ts-ignore
// 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', (_) => {

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 { REACT_PLAYER_WRAPPER_ID } from '../../constants/appConstants';
export default class VideoAutoQualityOptimizer {
video: any;
@ -15,8 +16,6 @@ export default class VideoAutoQualityOptimizer {
halfQualityCallbak = () => {};
constructor() {}
setGoodQualityCallback(callback: () => void) {
this.goodQualityCallback = callback;
}
@ -66,13 +65,11 @@ export default class VideoAutoQualityOptimizer {
} else if (mismatchInPercent < 0.1 && this.isRequestedHalfQuality) {
this.largeMismatchFramesCount = 0;
this.isRequestedHalfQuality = false;
// this.peer.send('set good quality');
this.goodQualityCallback();
} else if (mismatchInPercent >= 0.1 && !this.isRequestedHalfQuality) {
if (this.largeMismatchFramesCount < 3) {
this.largeMismatchFramesCount += 1;
} else {
// this.peer.send('set half quality');
this.halfQualityCallbak();
this.isRequestedHalfQuality = true;
}
@ -85,7 +82,7 @@ export default class VideoAutoQualityOptimizer {
imageData = null;
} else {
this.video = document.querySelector(
'#video-local-test-peer-sees > video'
`#${REACT_PLAYER_WRAPPER_ID} > video`
);
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 './index.css';
import App from './App';
@ -8,7 +9,9 @@ import { AppContextProvider } from './providers/AppContextProvider';
ReactDOM.render(
<React.StrictMode>
<AppContextProvider>
<App />
<Suspense fallback="loading">
<App />
</Suspense>
</AppContextProvider>
</React.StrictMode>,
document.getElementById('root')

View File

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

View File

@ -1122,7 +1122,7 @@
dependencies:
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"
resolved "https://packages.deskreen.com/@babel%2fruntime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
@ -5535,6 +5535,13 @@ html-minifier-terser@^5.0.1:
relateurl "^0.2.7"
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:
version "4.0.0-beta.11"
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"
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:
version "0.4.24"
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"
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:
version "0.9.0"
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"
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:
version "16.13.1"
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"
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:
version "1.0.2"
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>
</Text>
<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">
Device IP: <span className="device-ip-span">{deviceIP}</span>
</Text>

View File

@ -1,3 +1,4 @@
import { remote } from 'electron';
import React, { useContext, useCallback, useEffect, useState } from 'react';
import {
Button,
@ -17,13 +18,14 @@ import { useTranslation } from 'react-i18next';
import { Row } from 'react-flexbox-grid';
import { createStyles, makeStyles } from '@material-ui/core/styles';
import i18n from 'i18next';
import SharingSessionsService from '../../features/SharingSessionsService';
import {
DARK_UI_BACKGROUND,
LIGHT_UI_BACKGROUND,
SettingsContext,
} from '../../containers/SettingsProvider';
import CloseOverlayButton from '../CloseOverlayButton';
import {
import i18n_client, {
getLangFullNameToLangISOKeyMap,
getLangISOKeyToLangFullNameMap,
} from '../../configs/i18next.config.client';
@ -32,6 +34,10 @@ import SettingRowLabelAndInput from './SettingRowLabelAndInput';
const Fade = require('react-reveal/Fade');
const sharingSessionsService = remote.getGlobal(
'sharingSessionService'
) as SharingSessionsService;
interface SettingsOverlayProps {
isSettingsOpen: boolean;
handleClose: () => void;
@ -57,7 +63,11 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
const { handleClose, isSettingsOpen } = props;
const { isDarkTheme, setIsDarkThemeHook } = useContext(SettingsContext);
const {
isDarkTheme,
setIsDarkThemeHook,
setCurrentLanguageHook,
} = useContext(SettingsContext);
const [languagesList, setLanguagesList] = useState([] as string[]);
@ -67,7 +77,8 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
tmp.push(key);
});
setLanguagesList(tmp);
}, []);
setCurrentLanguageHook(i18n_client.language);
}, [setCurrentLanguageHook]);
const getClassesCallback = useStylesWithTheme(isDarkTheme);
@ -76,6 +87,11 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
document.body.classList.toggle(Classes.DARK);
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]);
const handleToggleLightTheme = useCallback(() => {
@ -83,6 +99,11 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
document.body.classList.toggle(Classes.DARK);
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]);
const onChangeLangueageHTMLSelectHandler = (
@ -92,10 +113,15 @@ export default function SettingsOverlay(props: SettingsOverlayProps) {
event.currentTarget &&
getLangFullNameToLangISOKeyMap().has(event.currentTarget.value)
) {
i18n.changeLanguage(
const newLang =
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"
/>
<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"
>
<div
class="react-reveal makeStyles-overlayInsideFade-9 bp3-card"
class="react-reveal makeStyles-overlayInsideFade-22 bp3-card"
id="settings-overlay-inner"
>
<button
class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10"
class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button"
type="button"
>
@ -112,7 +112,7 @@ exports[`should match exact snapshot 1`] = `
>
<div
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
class="bp3-tab-indicator"
@ -131,10 +131,10 @@ exports[`should match exact snapshot 1`] = `
>
<div
class="makeStyles-tabNavigationRowButton-11 row middle-xs"
class="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<span
class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-12"
class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-25"
icon="wrench"
>
<svg
@ -172,10 +172,10 @@ exports[`should match exact snapshot 1`] = `
>
<div
class="makeStyles-tabNavigationRowButton-11 row middle-xs"
class="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<span
class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-12"
class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-25"
icon="shield"
>
<svg
@ -212,10 +212,10 @@ exports[`should match exact snapshot 1`] = `
>
<div
class="makeStyles-tabNavigationRowButton-11 row middle-xs"
class="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<span
class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-12"
class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-25"
icon="blocked-person"
>
<svg
@ -267,13 +267,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6"
>
<div
class="makeStyles-oneSettingRow-20 row middle-xs"
class="makeStyles-oneSettingRow-26 row middle-xs"
>
<div
class=""
>
<span
class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-21"
class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-27"
icon="style"
>
<svg
@ -381,13 +381,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6"
>
<div
class="makeStyles-oneSettingRow-22 row middle-xs"
class="makeStyles-oneSettingRow-28 row middle-xs"
>
<div
class=""
>
<span
class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-23"
class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-29"
icon="translate"
>
<svg
@ -473,13 +473,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6"
>
<div
class="makeStyles-oneSettingRow-24 row middle-xs"
class="makeStyles-oneSettingRow-30 row middle-xs"
>
<div
class=""
>
<span
class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-25"
class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-31"
icon="automatic-updates"
>
<svg
@ -516,7 +516,7 @@ exports[`should match exact snapshot 1`] = `
class="row"
>
<label
class="bp3-control bp3-checkbox makeStyles-checkboxSettings-7"
class="bp3-control bp3-checkbox makeStyles-checkboxSettings-20"
>
<input
checked=""
@ -552,15 +552,15 @@ exports[`should match exact snapshot 1`] = `
tabindex="0"
/>
<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"
>
<div
class="react-reveal makeStyles-overlayInsideFade-9 bp3-card"
class="react-reveal makeStyles-overlayInsideFade-22 bp3-card"
id="settings-overlay-inner"
>
<button
class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10"
class="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button"
type="button"
>
@ -597,7 +597,7 @@ exports[`should match exact snapshot 1`] = `
>
<div
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
class="bp3-tab-indicator"
@ -616,10 +616,10 @@ exports[`should match exact snapshot 1`] = `
>
<div
class="makeStyles-tabNavigationRowButton-11 row middle-xs"
class="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<span
class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-12"
class="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-25"
icon="wrench"
>
<svg
@ -657,10 +657,10 @@ exports[`should match exact snapshot 1`] = `
>
<div
class="makeStyles-tabNavigationRowButton-11 row middle-xs"
class="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<span
class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-12"
class="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-25"
icon="shield"
>
<svg
@ -697,10 +697,10 @@ exports[`should match exact snapshot 1`] = `
>
<div
class="makeStyles-tabNavigationRowButton-11 row middle-xs"
class="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<span
class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-12"
class="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-25"
icon="blocked-person"
>
<svg
@ -752,13 +752,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6"
>
<div
class="makeStyles-oneSettingRow-20 row middle-xs"
class="makeStyles-oneSettingRow-26 row middle-xs"
>
<div
class=""
>
<span
class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-21"
class="bp3-icon bp3-icon-style makeStyles-settingRowIcon-27"
icon="style"
>
<svg
@ -866,13 +866,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6"
>
<div
class="makeStyles-oneSettingRow-22 row middle-xs"
class="makeStyles-oneSettingRow-28 row middle-xs"
>
<div
class=""
>
<span
class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-23"
class="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-29"
icon="translate"
>
<svg
@ -958,13 +958,13 @@ exports[`should match exact snapshot 1`] = `
class="col-xs-6"
>
<div
class="makeStyles-oneSettingRow-24 row middle-xs"
class="makeStyles-oneSettingRow-30 row middle-xs"
>
<div
class=""
>
<span
class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-25"
class="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-31"
icon="automatic-updates"
>
<svg
@ -1001,7 +1001,7 @@ exports[`should match exact snapshot 1`] = `
class="row"
>
<label
class="bp3-control bp3-checkbox makeStyles-checkboxSettings-7"
class="bp3-control bp3-checkbox makeStyles-checkboxSettings-20"
>
<input
checked=""
@ -1035,7 +1035,6 @@ exports[`should match exact snapshot 1`] = `
onKeyDown={[Function]}
>
<CSSTransition
appear={true}
classNames="bp3-overlay"
in={true}
key=".$__backdrop"
@ -1043,7 +1042,7 @@ exports[`should match exact snapshot 1`] = `
timeout={0}
>
<Transition
appear={true}
appear={false}
enter={true}
exit={true}
in={true}
@ -1065,7 +1064,6 @@ exports[`should match exact snapshot 1`] = `
</Transition>
</CSSTransition>
<CSSTransition
appear={true}
classNames="bp3-overlay"
in={true}
key=".$.0"
@ -1073,7 +1071,7 @@ exports[`should match exact snapshot 1`] = `
timeout={0}
>
<Transition
appear={true}
appear={false}
enter={true}
exit={true}
in={true}
@ -1088,7 +1086,7 @@ exports[`should match exact snapshot 1`] = `
unmountOnExit={false}
>
<div
className="makeStyles-overlayInnerRoot-8 bp3-overlay-content"
className="makeStyles-overlayInnerRoot-21 bp3-overlay-content"
tabIndex={0}
>
<Fade
@ -1125,7 +1123,7 @@ exports[`should match exact snapshot 1`] = `
refProp="ref"
>
<div
className="react-reveal makeStyles-overlayInsideFade-9 bp3-card"
className="react-reveal makeStyles-overlayInsideFade-22 bp3-card"
id="settings-overlay-inner"
style={
Object {
@ -1134,17 +1132,17 @@ exports[`should match exact snapshot 1`] = `
}
>
<CloseOverlayButton
className="makeStyles-absoluteCloseButton-10"
className="makeStyles-absoluteCloseButton-23"
isDefaultStyles={true}
onClick={[Function]}
>
<Blueprint3.Button
className="makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10"
className="makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button"
onClick={[Function]}
>
<button
className="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-10"
className="bp3-button makeStyles-closeButton-13 makeStyles-absoluteCloseButton-23"
id="close-overlay-button"
onClick={[Function]}
onKeyDown={[Function]}
@ -1213,7 +1211,6 @@ exports[`should match exact snapshot 1`] = `
Object {
"height": 0,
"transform": "translateX(0px) translateY(0px)",
"transition": "none",
"width": 0,
}
}
@ -1245,18 +1242,18 @@ exports[`should match exact snapshot 1`] = `
tabIndex={0}
>
<Row
className="makeStyles-tabNavigationRowButton-11"
className="makeStyles-tabNavigationRowButton-24"
middle="xs"
>
<div
className="makeStyles-tabNavigationRowButton-11 row middle-xs"
className="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<Blueprint3.Icon
className="makeStyles-iconInTablLeftButton-12"
className="makeStyles-iconInTablLeftButton-25"
icon="wrench"
>
<span
className="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-12"
className="bp3-icon bp3-icon-wrench makeStyles-iconInTablLeftButton-25"
icon="wrench"
>
<svg
@ -1312,18 +1309,18 @@ exports[`should match exact snapshot 1`] = `
tabIndex={0}
>
<Row
className="makeStyles-tabNavigationRowButton-11"
className="makeStyles-tabNavigationRowButton-24"
middle="xs"
>
<div
className="makeStyles-tabNavigationRowButton-11 row middle-xs"
className="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<Blueprint3.Icon
className="makeStyles-iconInTablLeftButton-12"
className="makeStyles-iconInTablLeftButton-25"
icon="shield"
>
<span
className="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-12"
className="bp3-icon bp3-icon-shield makeStyles-iconInTablLeftButton-25"
icon="shield"
>
<svg
@ -1377,18 +1374,18 @@ exports[`should match exact snapshot 1`] = `
role="tab"
>
<Row
className="makeStyles-tabNavigationRowButton-11"
className="makeStyles-tabNavigationRowButton-24"
middle="xs"
>
<div
className="makeStyles-tabNavigationRowButton-11 row middle-xs"
className="makeStyles-tabNavigationRowButton-24 row middle-xs"
>
<Blueprint3.Icon
className="makeStyles-iconInTablLeftButton-12"
className="makeStyles-iconInTablLeftButton-25"
icon="blocked-person"
>
<span
className="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-12"
className="bp3-icon bp3-icon-blocked-person makeStyles-iconInTablLeftButton-25"
icon="blocked-person"
>
<svg
@ -1492,23 +1489,23 @@ exports[`should match exact snapshot 1`] = `
className="col-xs-6"
>
<Row
className="makeStyles-oneSettingRow-20"
className="makeStyles-oneSettingRow-26"
middle="xs"
>
<div
className="makeStyles-oneSettingRow-20 row middle-xs"
className="makeStyles-oneSettingRow-26 row middle-xs"
>
<Col>
<div
className=""
>
<Blueprint3.Icon
className="makeStyles-settingRowIcon-21"
className="makeStyles-settingRowIcon-27"
icon="style"
iconSize={25}
>
<span
className="bp3-icon bp3-icon-style makeStyles-settingRowIcon-21"
className="bp3-icon bp3-icon-style makeStyles-settingRowIcon-27"
icon="style"
>
<svg
@ -1701,23 +1698,23 @@ exports[`should match exact snapshot 1`] = `
className="col-xs-6"
>
<Row
className="makeStyles-oneSettingRow-22"
className="makeStyles-oneSettingRow-28"
middle="xs"
>
<div
className="makeStyles-oneSettingRow-22 row middle-xs"
className="makeStyles-oneSettingRow-28 row middle-xs"
>
<Col>
<div
className=""
>
<Blueprint3.Icon
className="makeStyles-settingRowIcon-23"
className="makeStyles-settingRowIcon-29"
icon="translate"
iconSize={25}
>
<span
className="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-23"
className="bp3-icon bp3-icon-translate makeStyles-settingRowIcon-29"
icon="translate"
>
<svg
@ -1840,7 +1837,7 @@ exports[`should match exact snapshot 1`] = `
input={
<Blueprint3.Checkbox
checked={true}
className="makeStyles-checkboxSettings-7"
className="makeStyles-checkboxSettings-20"
label="Enabled"
/>
}
@ -1860,23 +1857,23 @@ exports[`should match exact snapshot 1`] = `
className="col-xs-6"
>
<Row
className="makeStyles-oneSettingRow-24"
className="makeStyles-oneSettingRow-30"
middle="xs"
>
<div
className="makeStyles-oneSettingRow-24 row middle-xs"
className="makeStyles-oneSettingRow-30 row middle-xs"
>
<Col>
<div
className=""
>
<Blueprint3.Icon
className="makeStyles-settingRowIcon-25"
className="makeStyles-settingRowIcon-31"
icon="automatic-updates"
iconSize={25}
>
<span
className="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-25"
className="bp3-icon bp3-icon-automatic-updates makeStyles-settingRowIcon-31"
icon="automatic-updates"
>
<svg
@ -1927,12 +1924,12 @@ exports[`should match exact snapshot 1`] = `
>
<Blueprint3.Checkbox
checked={true}
className="makeStyles-checkboxSettings-7"
className="makeStyles-checkboxSettings-20"
label="Enabled"
>
<Control
checked={true}
className="makeStyles-checkboxSettings-7"
className="makeStyles-checkboxSettings-20"
inputRef={[Function]}
label="Enabled"
onChange={[Function]}
@ -1940,7 +1937,7 @@ exports[`should match exact snapshot 1`] = `
typeClassName="bp3-checkbox"
>
<label
className="bp3-control bp3-checkbox makeStyles-checkboxSettings-7"
className="bp3-control bp3-checkbox makeStyles-checkboxSettings-20"
>
<input
checked={true}

View File

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

View File

@ -1,13 +1,20 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { clipboard, remote } from 'electron';
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 { makeStyles, createStyles } from '@material-ui/core';
import { Row, Col } from 'react-flexbox-grid';
import { SettingsContext } from '../../containers/SettingsProvider';
import isProduction from '../../utils/isProduction';
import CloseOverlayButton from '../CloseOverlayButton';
import SharingSessionService from '../../features/SharingSessionsService';
const sharingSessionService = remote.getGlobal(
@ -38,17 +45,21 @@ const useStyles = makeStyles(() =>
dialogQRWrapper: {
backgroundColor: 'white',
padding: '20px',
// width: '95%',
// hieght: '95%',
borderRadius: '10px',
},
bigQRCodeDialogRoot: {
'&:hover': {
cursor: 'zoom-out',
},
paddingBottom: '0px',
},
})
);
const ScanQRStep: React.FC = () => {
const { t } = useTranslation();
const classes = useStyles();
const { isDarkTheme } = useContext(SettingsContext);
@ -73,10 +84,20 @@ const ScanQRStep: React.FC = () => {
return (
<>
<div style={{ textAlign: 'center' }}>
<Text className="bp3-text">Scan the QR code</Text>
<Text className="bp3-text-muted">
( make sure your computer and device are connected on same WiFi )
<Text>
<span
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 className="bp3-text">{t('Scan the QR code')}</Text>
</div>
<div>
<Tooltip content="Click to make bigger" position={Position.LEFT}>
@ -133,50 +154,32 @@ const ScanQRStep: React.FC = () => {
canEscapeKeyClose
canOutsideClickClose
transitionDuration={isProduction() ? 700 : 0}
style={{ position: 'relative', top: '-30px' }}
style={{ position: 'relative', top: '0px' }}
>
<Button
<Row
id="qr-code-dialog-inner"
className={Classes.DIALOG_BODY}
center="xs"
middle="xs"
onClick={() => setIsQRCodeMagnified(false)}
style={{ paddingTop: '20px', paddingBottom: '13px' }}
>
<Row between="xs" middle="xs">
<Col xs={10}>
<H2 style={{ margin: '0px', padding: '0px', marginLeft: '35px' }}>
Scan QR Code
</H2>
</Col>
<Col xs={2}>
<CloseOverlayButton
onClick={() => setIsQRCodeMagnified(false)}
style={{
width: '40px',
height: '40px',
position: 'relative',
borderRadius: '100px',
}}
/>
</Col>
</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>
<Col xs={11} 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"
/>
</Col>
</Row>
</Dialog>
</>
);

View File

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

View File

@ -236,7 +236,10 @@ exports[`should match exact snapshot 1`] = `
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "10px",
"paddingRight": "10px",
}
}
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
className="bp3-text"
>
<div
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>
</div>
<div>
@ -423,7 +431,7 @@ exports[`should match exact snapshot on each step 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
transitionDuration={0}
@ -443,7 +451,7 @@ exports[`should match exact snapshot on each step 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
transitionDuration={0}
@ -840,11 +848,21 @@ exports[`should match exact snapshot on each step 2`] = `
<Blueprint3.Button
active={true}
className="makeStyles-orDecorationButton-9"
style={
Object {
"zIndex": 999,
}
}
>
<button
className="bp3-button bp3-active makeStyles-orDecorationButton-9"
onKeyDown={[Function]}
onKeyUp={[Function]}
style={
Object {
"zIndex": 999,
}
}
type="button"
>
<Blueprint3.Icon
@ -1352,7 +1370,10 @@ exports[`should match exact snapshot on each step 3`] = `
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "10px",
"paddingRight": "10px",
}
}
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
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>
<Blueprint3.Tooltip
@ -95,83 +103,42 @@ exports[`<ScanQRStep /> when rendered should match exact snapshot 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
transitionDuration={0}
>
<Blueprint3.Button
<Row
center="xs"
className="bp3-dialog-body"
id="qr-code-dialog-inner"
middle="xs"
onClick={[Function]}
style={
Object {
"paddingBottom": "13px",
"paddingTop": "20px",
}
}
>
<Row
between="xs"
middle="xs"
<Col
className="makeStyles-dialogQRWrapper-2"
xs={11}
>
<Col
xs={10}
>
<Component
style={
Object {
"margin": "0px",
"marginLeft": "35px",
"padding": "0px",
}
<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,
}
>
Scan QR Code
</Component>
</Col>
<Col
xs={2}
>
<CloseOverlayButton
onClick={[Function]}
style={
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>
}
includeMargin={false}
level="H"
renderAs="svg"
size={128}
value="http://255.255.255.255:3000/"
width="390px"
/>
</Col>
</Row>
</Blueprint3.Dialog>
</Fragment>
`;

View File

@ -2,12 +2,16 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/rules-of-hooks */
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 { Col, Row } from 'react-flexbox-grid';
import SettingsOverlay from './SettingsOverlay/SettingsOverlay';
import ConnectedDevicesListDrawer from './ConnectedDevicesListDrawer';
import { SettingsContext } from '../containers/SettingsProvider';
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 Fade = require('react-reveal/Fade');
@ -72,13 +76,41 @@ export default function TopPanel(props: any) {
setIsDrawerOpen(!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(() => {
return (
<div className={getClassesCallback().topPanelControlButtonMargin}>
<Tooltip content="Connected Devices" position={Position.BOTTOM}>
<Button
id="top-panel-connected-devices-list-button"
intent="none"
intent="primary"
className={getClassesCallback().topPanelControlButton}
onClick={handleToggleConnectedDevicesListDrawer}
>
@ -96,7 +128,7 @@ export default function TopPanel(props: any) {
const renderHelpButton = useCallback(() => {
return (
<div className={getClassesCallback().topPanelControlButtonMargin}>
<Tooltip content="Help" position={Position.BOTTOM}>
<Tooltip content="Tutorial" position={Position.BOTTOM}>
<Button
id="top-panel-help-button"
intent="none"
@ -104,7 +136,7 @@ export default function TopPanel(props: any) {
>
<Icon
className={getClassesCallback().topPanelIconOfControlButton}
icon="help"
icon="learning"
iconSize={22}
/>
</Button>
@ -140,12 +172,17 @@ export default function TopPanel(props: any) {
id="logo-with-popover-visit-website"
className={getClassesCallback().logoWithAppName}
>
<h4
id="deskreen-top-app-name-header"
className={getClassesCallback().appNameHeader}
<Tooltip
content="Click to visit our website"
position={Position.BOTTOM}
>
Deskreen
</h4>
<h4
id="deskreen-top-app-name-header"
className={getClassesCallback().appNameHeader}
>
Deskreen
</h4>
</Tooltip>
</div>
</Zoom>
);
@ -154,7 +191,14 @@ export default function TopPanel(props: any) {
return (
<>
<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}>
<Fade right duration={isProduction() ? 2000 : 0}>
{renderConnectedDevicesListButton()}

View File

@ -151,7 +151,7 @@ exports[`should match exact snapshot 1`] = `
>
<div
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"
>
<div
@ -301,7 +301,7 @@ exports[`should match exact snapshot 1`] = `
>
<div
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"
>
<div
@ -628,7 +628,10 @@ exports[`should match exact snapshot 1`] = `
style={
Object {
"backgroundColor": "#00f99273",
"borderRadius": "20px",
"fontWeight": 900,
"paddingLeft": "10px",
"paddingRight": "10px",
}
}
tabIndex={0}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -66,7 +66,7 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
const [isInterShow, setIsInterShow] = useState(false);
const { isDarkTheme } = useContext(SettingsContext);
const { isDarkTheme, currentLanguage } = useContext(SettingsContext);
const { addToast } = useToasts();
@ -104,8 +104,17 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
},
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 [isEntireScreenSelected, setIsEntireScreenSelected] = useState(false);
const [
@ -153,7 +162,40 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
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
.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
.createWaitingForConnectionSharingSession()
// eslint-disable-next-line promise/always-return
@ -169,7 +211,7 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
React.useImperativeHandle(ref, () => ({
handleReset() {
handleReset();
handleResetWithSharingSessionRestart();
},
}));
@ -215,7 +257,7 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
}, [handleNext]);
const handleUserClickedDeviceDisconnectButton = useCallback(async () => {
handleReset();
handleResetWithSharingSessionRestart();
addToast(
<Text>
@ -224,20 +266,11 @@ const DeskreenStepper = React.forwardRef((_props, ref) => {
{
appearance: 'info',
autoDismiss: true,
// @ts-ignore: works fine here, ignore
// @ts-ignore: works fine here
isdarktheme: `${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]);
}, [addToast, handleResetWithSharingSessionRestart, isDarkTheme]);
const renderIntermediateOrSuccessStepContent = useCallback(() => {
return activeStep === steps.length ? (

View File

@ -57,63 +57,339 @@ exports[`should match exact snapshot 1`] = `
}
>
<div
className="makeStyles-topPanelRoot-1"
className="makeStyles-topPanelRoot-117"
>
<Zoom
duration={0}
top={true}
<Row
center="xs"
middle="xs"
style={
Object {
"transform": "translateX(-50px)",
"width": "100%",
}
}
>
<RevealBase
fraction={0.2}
inEffect={
<div
className="row center-xs middle-xs"
style={
Object {
"count": 1,
"delay": 0,
"duration": 0,
"forever": undefined,
"make": [Function],
"reverse": undefined,
"style": Object {
"animationFillMode": "both",
},
"transform": "translateX(-50px)",
"width": "100%",
}
}
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-9"
id="logo-with-popover-visit-website"
style={
Object {
"opacity": undefined,
}
}
>
<h4
className="makeStyles-appNameHeader-17"
id="deskreen-top-app-name-header"
<Col>
<div
className=""
>
Deskreen
</h4>
</div>
</RevealBase>
</Zoom>
<Blueprint3.Tooltip
content="If you like Deskreen, consider donating! Deskreen is free and opensource forever! You can help us to make Deskreen even better!"
hoverCloseDelay={0}
hoverOpenDelay={100}
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
className="makeStyles-topPanelControlButtonsRoot-25"
className="makeStyles-topPanelControlButtonsRoot-141"
>
<Fade
duration={0}
@ -152,7 +428,7 @@ exports[`should match exact snapshot 1`] = `
right={true}
>
<div
className="react-reveal makeStyles-topPanelControlButtonMargin-34"
className="react-reveal makeStyles-topPanelControlButtonMargin-150"
style={
Object {
"opacity": undefined,
@ -214,15 +490,15 @@ exports[`should match exact snapshot 1`] = `
onMouseLeave={[Function]}
>
<Blueprint3.Button
className="makeStyles-topPanelControlButton-40"
className="makeStyles-topPanelControlButton-156"
id="top-panel-connected-devices-list-button"
intent="none"
intent="primary"
key=".0"
onClick={[Function]}
tabIndex={0}
>
<button
className="bp3-button makeStyles-topPanelControlButton-40"
className="bp3-button bp3-intent-primary makeStyles-topPanelControlButton-156"
id="top-panel-connected-devices-list-button"
onClick={[Function]}
onKeyDown={[Function]}
@ -238,12 +514,12 @@ exports[`should match exact snapshot 1`] = `
key="text"
>
<Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-49"
className="makeStyles-topPanelIconOfControlButton-165"
icon="th-list"
iconSize={20}
>
<span
className="bp3-icon bp3-icon-th-list makeStyles-topPanelIconOfControlButton-49"
className="bp3-icon bp3-icon-th-list makeStyles-topPanelIconOfControlButton-165"
icon="th-list"
>
<svg
@ -327,7 +603,7 @@ exports[`should match exact snapshot 1`] = `
right={true}
>
<div
className="react-reveal makeStyles-topPanelControlButtonMargin-55"
className="react-reveal makeStyles-topPanelControlButtonMargin-171"
style={
Object {
"opacity": undefined,
@ -335,7 +611,7 @@ exports[`should match exact snapshot 1`] = `
}
>
<Blueprint3.Tooltip
content="Help"
content="Tutorial"
hoverCloseDelay={0}
hoverOpenDelay={100}
position="bottom"
@ -346,7 +622,7 @@ exports[`should match exact snapshot 1`] = `
boundary="scrollParent"
canEscapeKeyClose={false}
captureDismiss={false}
content="Help"
content="Tutorial"
defaultIsOpen={false}
disabled={false}
enforceFocus={false}
@ -389,14 +665,14 @@ exports[`should match exact snapshot 1`] = `
onMouseLeave={[Function]}
>
<Blueprint3.Button
className="makeStyles-topPanelControlButton-61"
className="makeStyles-topPanelControlButton-177"
id="top-panel-help-button"
intent="none"
key=".0"
tabIndex={0}
>
<button
className="bp3-button makeStyles-topPanelControlButton-61"
className="bp3-button makeStyles-topPanelControlButton-177"
id="top-panel-help-button"
onKeyDown={[Function]}
onKeyUp={[Function]}
@ -411,28 +687,33 @@ exports[`should match exact snapshot 1`] = `
key="text"
>
<Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-70"
icon="help"
className="makeStyles-topPanelIconOfControlButton-186"
icon="learning"
iconSize={22}
>
<span
className="bp3-icon bp3-icon-help makeStyles-topPanelIconOfControlButton-70"
icon="help"
className="bp3-icon bp3-icon-learning makeStyles-topPanelIconOfControlButton-186"
icon="learning"
>
<svg
data-icon="help"
data-icon="learning"
height={22}
viewBox="0 0 20 20"
width={22}
>
<desc>
help
learning
</desc>
<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"
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>
</span>
</Blueprint3.Icon>
@ -500,7 +781,7 @@ exports[`should match exact snapshot 1`] = `
right={true}
>
<div
className="react-reveal makeStyles-topPanelControlButtonMargin-76"
className="react-reveal makeStyles-topPanelControlButtonMargin-192"
style={
Object {
"opacity": undefined,
@ -562,14 +843,14 @@ exports[`should match exact snapshot 1`] = `
onMouseLeave={[Function]}
>
<Blueprint3.Button
className="makeStyles-topPanelControlButton-82"
className="makeStyles-topPanelControlButton-198"
id="top-panel-settings-button"
key=".0"
onClick={[Function]}
tabIndex={0}
>
<button
className="bp3-button makeStyles-topPanelControlButton-82"
className="bp3-button makeStyles-topPanelControlButton-198"
id="top-panel-settings-button"
onClick={[Function]}
onKeyDown={[Function]}
@ -585,12 +866,12 @@ exports[`should match exact snapshot 1`] = `
key="text"
>
<Blueprint3.Icon
className="makeStyles-topPanelIconOfControlButton-91"
className="makeStyles-topPanelIconOfControlButton-207"
icon="cog"
iconSize={22}
>
<span
className="bp3-icon bp3-icon-cog makeStyles-topPanelIconOfControlButton-91"
className="bp3-icon bp3-icon-cog makeStyles-topPanelIconOfControlButton-207"
icon="cog"
>
<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
className="bp3-text"
>
<div
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>
</div>
<div>
@ -2122,7 +2411,7 @@ exports[`should match exact snapshot 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
transitionDuration={0}
@ -2142,7 +2431,7 @@ exports[`should match exact snapshot 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
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
className="bp3-text"
>
<div
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>
</div>
<div>
@ -1408,7 +1416,7 @@ exports[`should match exact snapshot 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
transitionDuration={0}
@ -1428,7 +1436,7 @@ exports[`should match exact snapshot 1`] = `
style={
Object {
"position": "relative",
"top": "-30px",
"top": "0px",
}
}
transitionDuration={0}

View File

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

View File

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

View File

@ -17,6 +17,8 @@ export default class SharingSessionService {
connectedDevicesService: ConnectedDevicesService;
rendererWebrtcHelpersService: RendererWebrtcHelpersService;
isCreatingNewSharingSession: boolean;
appLanguage = 'en';
isDarkTheme = false;
constructor(
_roomIDService: RoomIDService,
@ -38,6 +40,14 @@ export default class SharingSessionService {
}, 1000 * 60 * 60); // every hour
}
setAppLanguage(newLang: string): void {
this.appLanguage = newLang;
}
setAppTheme(isDarkTheme: boolean): void {
this.isDarkTheme = isDarkTheme;
}
createUser(): Promise<undefined> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
@ -75,10 +85,13 @@ export default class SharingSessionService {
createNewSharingSession(_roomID: string): SharingSession {
const roomID = _roomID || this.roomIDService.getSimpleAvailableRoomID();
this.roomIDService.markRoomIDAsTaken(roomID);
const sharingSession = new SharingSession(
roomID,
this.user as LocalPeerUser,
this.rendererWebrtcHelpersService
this.rendererWebrtcHelpersService,
this.appLanguage,
this.isDarkTheme
);
this.sharingSessions.set(sharingSession.id, sharingSession);
return sharingSession;
@ -87,7 +100,6 @@ export default class SharingSessionService {
// eslint-disable-next-line class-methods-use-this
changeSharingSessionStatusToSharing(sharingSession: SharingSession) {
sharingSession.status = SharingSessionStatusEnum.SHARING;
this.roomIDService.markRoomIDAsTaken(sharingSession.roomID);
}
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 ⚓",
"ru": "Русский",
"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": "Сигнальный сервер работает на порте ⚓",
"ru": "Русский",
"en": "English",
"ua": "Українська"
"ua": "Українська",
"Scan the QR code": "Отсканируйте QR код"
}

View File

@ -3,5 +3,6 @@
"Signaling server is running on port": "Сигнальный сервер працює на порту ⚓",
"ru": "Русский",
"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.sharingSessionID,
data.user,
data.appTheme, // TODO getAppTheme
data.appLanguage, // TODO getLanguage
roomIDService,
connectedDevicesService,
sharingSessionsService
@ -48,3 +50,11 @@ ipcRenderer.on('deny-connection-for-partner', () => {
ipcRenderer.on('send-user-allowed-to-connect', () => {
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) {
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 getStore from './store';
const LOCALHOST_SOCKET_IP = '::1';
interface User {
socketId: string;
publicKey: string;
@ -152,7 +154,8 @@ export default class Socket implements SocketOPTS {
{
socketId: socket.id,
publicKey: payload.publicKey,
isOwner: (room.users || []).length === 0,
isOwner:
LOCALHOST_SOCKET_IP === socket.request.connection.remoteAddress,
ip: payload.ip ? payload.ip : '',
},
],

View File

@ -22,6 +22,7 @@ import getStore from './store';
import Logger from '../utils/logger';
import isProduction from '../utils/isProduction';
import SocketsIPService from './socketsIPService';
import getDeskreenGlobal from '../mainProcessHelpers/getDeskreenGlobal';
const log = new Logger('app/server/index.ts');
@ -90,25 +91,34 @@ io.sockets.on('connection', (socket) => {
io.on('connection', async (socket) => {
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);
room = JSON.parse(room || '{}');
let room = await store.get('rooms', roomIdHash);
room = JSON.parse(room || '{}');
// eslint-disable-next-line no-new
new DarkwireSocket({
roomIdOriginal: roomId,
roomId: roomIdHash,
socket,
room,
});
// eslint-disable-next-line no-new
new DarkwireSocket({
roomIdOriginal: roomId,
roomId: roomIdHash,
socket,
room,
});
}
}, 1000); // timeout 1 second for throttling malitios connections
});
const init = async (PORT: number) => {
pollForInactiveRooms();
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 =
| CallAcceptedMessageWithPayload
| DeviceDetailsMessageWithPayload;
| DeviceDetailsMessageWithPayload
| GetAppThemeMessageWithPayload
| GetAppLanguageMessageWithPayload;

View File

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