From ff238702ede94d4b5e2b5aeb61451be0ff1e25b6 Mon Sep 17 00:00:00 2001 From: Pavlo Buidenkov Date: Tue, 1 Sep 2020 20:45:56 +0300 Subject: [PATCH 1/2] massive UI changes and improvements --- .eslintignore | 2 +- .github/workflows/codecov.yml | 10 +- .github/workflows/release.yml | 10 +- .github/workflows/test.yml | 10 +- app/Routes.tsx | 1 + app/app.global.css | 372 +- app/app.html | 2 +- app/client/package.json | 8 +- app/client/tsconfig.json | 2 +- .../AllowConnectionForDeviceAlert.spec.tsx | 25 + .../AllowConnectionForDeviceAlert.tsx | 56 + app/components/CloseOverlayButton.tsx | 46 + .../ConnectedDevicesListDrawer.spec.tsx | 24 + app/components/ConnectedDevicesListDrawer.tsx | 217 + app/components/Home.tsx | 5 +- app/components/NavPanel.tsx | 34 - .../SettingRowLabelAndInput.tsx | 63 + .../SettingsOverlay/SettingsOverlay.spec.tsx | 29 + .../SettingsOverlay/SettingsOverlay.tsx | 276 + .../SettingsOverlay.spec.tsx.snap | 1985 +++++++ .../ShareAppOrScreenControlGroup.spec.tsx | 23 + .../ShareAppOrScreenControlGroup.tsx | 137 + .../StepperPanel/ColorlibConnector.tsx | 28 + .../StepperPanel/ColorlibStepIcon.tsx | 76 + .../DeviceConnectedInfoButton.spec.tsx | 23 + .../DeviceConnectedInfoButton.tsx | 90 + .../DeviceConnectedInfoButton.spec.tsx.snap | 344 ++ .../ChooseAppOrScreeenStep.spec.tsx | 23 + .../StepsOfStepper/ChooseAppOrScreeenStep.tsx | 29 + .../ChooseAppOrScreenOverlay.spec.tsx | 26 + .../ChooseAppOrScreenOverlay.tsx | 164 + .../PreviewGridList.tsx | 166 + .../ChooseAppOrScreenOverlay.spec.tsx.snap | 4587 +++++++++++++++++ .../StepsOfStepper/ConfirmStep.spec.tsx | 20 + app/components/StepsOfStepper/ConfirmStep.tsx | 30 + .../StepsOfStepper/IntermediateStep.spec.tsx | 85 + .../StepsOfStepper/IntermediateStep.tsx | 146 + .../StepsOfStepper/ScanQRStep.spec.tsx | 77 + app/components/StepsOfStepper/ScanQRStep.tsx | 155 + .../StepsOfStepper/SuccessStep.spec.tsx | 49 + app/components/StepsOfStepper/SuccessStep.tsx | 93 + .../ChooseAppOrScreeenStep.spec.tsx.snap | 250 + .../__snapshots__/ConfirmStep.spec.tsx.snap | 112 + .../IntermediateStep.spec.tsx.snap | 1198 +++++ .../__snapshots__/ScanQRStep.spec.tsx.snap | 177 + .../__snapshots__/SuccessStep.spec.tsx.snap | 220 + app/components/TopPanel.spec.tsx | 78 + app/components/TopPanel.tsx | 177 + ...llowConnectionForDeviceAlert.spec.tsx.snap | 607 +++ .../ConnectedDevicesListDrawer.spec.tsx.snap | 644 +++ ...ShareAppOrScreenControlGroup.spec.tsx.snap | 230 + .../__snapshots__/TopPanel.spec.tsx.snap | 113 + app/configs/app.config.ts | 5 - app/configs/app.lang.config.ts | 10 + app/configs/i18next.config.client.ts | 44 +- app/configs/i18next.config.ts | 34 +- app/constants/test-devices.json | 72 + .../test-screen-sharing-objects.json | 88 + .../ConnectedDevicesProvider/Device.d.ts | 7 + .../ConnectedDevicesProvider/index.tsx | 99 + app/containers/CounterPage.tsx | 4 +- app/containers/HomePage.spec.tsx | 28 + app/containers/HomePage.tsx | 52 +- app/containers/Root.tsx | 16 +- app/containers/SettingsProvider.tsx | 61 + app/containers/Stepper.spec.tsx | 23 + app/containers/Stepper.tsx | 267 + app/containers/__mocks__/electron-settings.ts | 30 + app/containers/__mocks__/electron.ts | 10 + app/containers/__mocks__/react-i18next.ts | 19 + .../__snapshots__/HomePage.spec.tsx.snap | 2276 ++++++++ .../__snapshots__/Stepper.spec.tsx.snap | 1556 ++++++ app/index.tsx | 15 + app/locales/en/translation.json | 5 +- app/locales/ru/translation.json | 5 +- app/locales/ua/translation.json | 7 + app/locales/ua/translation.missing.json | 0 app/main.dev.ts | 35 +- app/main.prod.js.LICENSE.txt | 12 + app/menu.ts | 23 +- app/package-lock.json | 2 +- app/package.json | 2 +- app/server/index.ts | 15 +- app/utils/isProduction.ts | 8 + package.json | 36 +- test/e2e/HomePage.e2e.ts | 4 +- test/ux/Stepper.ux.ts | 488 ++ yarn.lock | 564 +- 88 files changed, 19046 insertions(+), 230 deletions(-) create mode 100644 app/components/AllowConnectionForDeviceAlert.spec.tsx create mode 100644 app/components/AllowConnectionForDeviceAlert.tsx create mode 100644 app/components/CloseOverlayButton.tsx create mode 100644 app/components/ConnectedDevicesListDrawer.spec.tsx create mode 100644 app/components/ConnectedDevicesListDrawer.tsx delete mode 100644 app/components/NavPanel.tsx create mode 100644 app/components/SettingsOverlay/SettingRowLabelAndInput.tsx create mode 100644 app/components/SettingsOverlay/SettingsOverlay.spec.tsx create mode 100644 app/components/SettingsOverlay/SettingsOverlay.tsx create mode 100644 app/components/SettingsOverlay/__snapshots__/SettingsOverlay.spec.tsx.snap create mode 100644 app/components/ShareAppOrScreenControlGroup.spec.tsx create mode 100644 app/components/ShareAppOrScreenControlGroup.tsx create mode 100644 app/components/StepperPanel/ColorlibConnector.tsx create mode 100644 app/components/StepperPanel/ColorlibStepIcon.tsx create mode 100644 app/components/StepperPanel/DeviceConnectedInfoButton.spec.tsx create mode 100644 app/components/StepperPanel/DeviceConnectedInfoButton.tsx create mode 100644 app/components/StepperPanel/__snapshots__/DeviceConnectedInfoButton.spec.tsx.snap create mode 100644 app/components/StepsOfStepper/ChooseAppOrScreeenStep.spec.tsx create mode 100644 app/components/StepsOfStepper/ChooseAppOrScreeenStep.tsx create mode 100644 app/components/StepsOfStepper/ChooseAppOrScreenOverlay/ChooseAppOrScreenOverlay.spec.tsx create mode 100644 app/components/StepsOfStepper/ChooseAppOrScreenOverlay/ChooseAppOrScreenOverlay.tsx create mode 100644 app/components/StepsOfStepper/ChooseAppOrScreenOverlay/PreviewGridList.tsx create mode 100644 app/components/StepsOfStepper/ChooseAppOrScreenOverlay/__snapshots__/ChooseAppOrScreenOverlay.spec.tsx.snap create mode 100644 app/components/StepsOfStepper/ConfirmStep.spec.tsx create mode 100644 app/components/StepsOfStepper/ConfirmStep.tsx create mode 100644 app/components/StepsOfStepper/IntermediateStep.spec.tsx create mode 100644 app/components/StepsOfStepper/IntermediateStep.tsx create mode 100644 app/components/StepsOfStepper/ScanQRStep.spec.tsx create mode 100644 app/components/StepsOfStepper/ScanQRStep.tsx create mode 100644 app/components/StepsOfStepper/SuccessStep.spec.tsx create mode 100644 app/components/StepsOfStepper/SuccessStep.tsx create mode 100644 app/components/StepsOfStepper/__snapshots__/ChooseAppOrScreeenStep.spec.tsx.snap create mode 100644 app/components/StepsOfStepper/__snapshots__/ConfirmStep.spec.tsx.snap create mode 100644 app/components/StepsOfStepper/__snapshots__/IntermediateStep.spec.tsx.snap create mode 100644 app/components/StepsOfStepper/__snapshots__/ScanQRStep.spec.tsx.snap create mode 100644 app/components/StepsOfStepper/__snapshots__/SuccessStep.spec.tsx.snap create mode 100644 app/components/TopPanel.spec.tsx create mode 100644 app/components/TopPanel.tsx create mode 100644 app/components/__snapshots__/AllowConnectionForDeviceAlert.spec.tsx.snap create mode 100644 app/components/__snapshots__/ConnectedDevicesListDrawer.spec.tsx.snap create mode 100644 app/components/__snapshots__/ShareAppOrScreenControlGroup.spec.tsx.snap create mode 100644 app/components/__snapshots__/TopPanel.spec.tsx.snap delete mode 100644 app/configs/app.config.ts create mode 100644 app/configs/app.lang.config.ts create mode 100644 app/constants/test-devices.json create mode 100644 app/constants/test-screen-sharing-objects.json create mode 100644 app/containers/ConnectedDevicesProvider/Device.d.ts create mode 100644 app/containers/ConnectedDevicesProvider/index.tsx create mode 100644 app/containers/HomePage.spec.tsx create mode 100644 app/containers/SettingsProvider.tsx create mode 100644 app/containers/Stepper.spec.tsx create mode 100644 app/containers/Stepper.tsx create mode 100644 app/containers/__mocks__/electron-settings.ts create mode 100644 app/containers/__mocks__/electron.ts create mode 100644 app/containers/__mocks__/react-i18next.ts create mode 100644 app/containers/__snapshots__/HomePage.spec.tsx.snap create mode 100644 app/containers/__snapshots__/Stepper.spec.tsx.snap create mode 100644 app/locales/ua/translation.json create mode 100644 app/locales/ua/translation.missing.json create mode 100644 app/utils/isProduction.ts create mode 100644 test/ux/Stepper.ux.ts diff --git a/.eslintignore b/.eslintignore index e0fea66..561b4ce 100644 --- a/.eslintignore +++ b/.eslintignore @@ -57,4 +57,4 @@ package.json # Entire app/client directory -app/client/* +app/client diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index a932eef..421791a 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -13,13 +13,13 @@ jobs: with: node-version: 14 - - name: yarn install from npmjs registry - run: | - yarn install --no-lockfile - yarn install-client:nolockfile + # - name: yarn install from npmjs registry + # run: | + # yarn install --no-lockfile + # yarn install-client:nolockfile - name: Configure private AWS npm registry and install packages from it - if: ${{ failure() }} + # if: ${{ failure() }} run: | npm config set registry https://packages.deskreen.com/ npm set //packages.deskreen.com/:_authToken="${{ secrets.NPMRC_USER_TOKEN }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7290d93..2c3e4e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,13 +46,13 @@ jobs: with: node-version: 14 - - name: yarn install from npmjs registry - run: | - yarn install --no-lockfile - yarn install-client:nolockfile + # - name: yarn install from npmjs registry + # run: | + # yarn install --no-lockfile + # yarn install-client:nolockfile - name: Configure private AWS npm registry and install packages from it - if: ${{ failure() }} + # if: ${{ failure() }} run: | npm config set registry https://packages.deskreen.com/ npm set //packages.deskreen.com/:_authToken="${{ secrets.NPMRC_USER_TOKEN }}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05e0d29..25d7e08 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,13 +21,13 @@ jobs: with: node-version: 14 - - name: yarn install from npmjs registry - run: | - yarn install --no-lockfile - yarn install-client:nolockfile + # - name: yarn install from npmjs registry + # run: | + # yarn install --no-lockfile + # yarn install-client:nolockfile - name: Configure private AWS npm registry and install packages from it - if: ${{ failure() }} + # if: ${{ failure() }} run: | npm config set registry https://packages.deskreen.com/ npm set //packages.deskreen.com/:_authToken="${{ secrets.NPMRC_USER_TOKEN }}" diff --git a/app/Routes.tsx b/app/Routes.tsx index df54410..3927751 100644 --- a/app/Routes.tsx +++ b/app/Routes.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint react/jsx-props-no-spreading: off */ import React from 'react'; import { Switch, Route } from 'react-router-dom'; diff --git a/app/app.global.css b/app/app.global.css index 57e855c..eefaaed 100644 --- a/app/app.global.css +++ b/app/app.global.css @@ -6,28 +6,384 @@ @import '~normalize.css/normalize.css'; @import '~@blueprintjs/core/lib/css/blueprint.css'; @import '~react-flexbox-grid/dist/react-flexbox-grid.css'; +@import '~fontsource-lexend-peta/index.css'; + +:root { + --dark-bg-color: #293742; + --light-bg-color: rgba(240, 248, 250, 1); + --light-btn-no-intent-color: rgb(218, 238, 243); + --dark-btn-no-intent-color: #394b59; + --custom-scrollbar-webkit-scrollbar-thumb-border-radius: 10px; + --custom-scrollbar-webkit-scrollbar-thumb-background-color: #8a9ba8; + --custom-scrollbar-webkit-scrollbar_background-color: rgba(0, 0, 0, 0); + --custom-scrollbar-webkit-scrollbar-width: 12px; + --custom-scrollbar-webkit-scrollbar-track-border-radius: 10px; + --custom-scrollbar-webkit-scrollbar-track-background-color: rgba(0, 0, 0, 0); +} body { position: relative; - color: white; height: 100vh; - background-color: whitesmoke; font-family: Arial, Helvetica, Helvetica Neue, serif; - overflow-y: hidden; + overflow: hidden; + background-color: var(--light-bg-color); +} + +/* UI colors FOR LIGHT AND DARK THEME START */ +.bp3-button:not([class*='bp3-intent-']) { + background-color: var(--light-btn-no-intent-color); } body.bp3-dark { - background-color: #293742; + background-color: var(--dark-bg-color) !important; } -/* .bp3-button { - outline: none !important; +.bp3-dialog { + background-color: var(--light-bg-color) !important; } -.bp3-control { - outline: none !important; +.bp3-dark .bp3-dialog { + background-color: var(--dark-bg-color) !important; +} + +.bp3-popover .bp3-popover-arrow-fill { + fill: var(--light-bg-color); +} + +.bp3-popover .bp3-popover-content { + background-color: var(--light-bg-color); + color: black; +} + +.bp3-html-select > select { + background-color: var(--light-btn-no-intent-color); +} + +.bp3-drawer { + background-color: var(--light-bg-color); +} + +.bp3-card { + background-color: var(--light-bg-color); +} + +/* for really small screen sizes (ex. Raspberry PI display etc. */ +@media screen and (max-height: 419px) { + body { + overflow-y: scroll; + } +} + +.react-toast-notifications__container { + overflow: hidden !important; +} + +/* Connected Devices List button pulse START */ +#top-panel-connected-devices-list-button.pulsing { + transform: scale(1); + animation: pulse-black-devices-list-button 0.75s infinite; +} + +#top-panel-connected-devices-list-button.pulse-not-infinite { + transform: scale(1); + animation: pulse-black-devices-list-button 0.75s 4; +} + +@keyframes pulse-black-devices-list-button { + 0% { + transform: scale(1); + box-shadow: 0 0 0 0 rgba(115, 134, 148, 0.7); + } + + 60% { + transform: scale(0.85); + box-shadow: 0 0 0 15px rgba(115, 134, 148, 0.3); + } + + 100% { + transform: scale(1); + box-shadow: 0 0 0 5px rgba(0, 0, 0, 0); + } +} + +/* Connected Devices List button pulse END */ + +/* For choose app or screen overlay popup without scrollbars! */ +.bp3-overlay-scroll-container { + overflow-y: hidden !important; +} + +/* help cursor when text hovered Connected Devices List */ +#connected-devices-list-text-success:hover { + cursor: help; +} + +/* react-toast-notifications progress bar more obvious look */ +body + > div.react-toast-notifications__container + > div + > div + > div.react-toast-notifications__toast__icon-wrapper + > div { + background-color: rgba(0, 0, 0, 0.4); +} + +.hide-toaster-progress { + height: 5px; + width: calc(100% + 87px) !important; + bottom: -11px !important; + left: -40px !important; +} + +/* ALLOW CONNECTION ALERT BLINK ANIMATION START */ +div.class-allow-device-to-connect-alert + > div.bp3-alert-body + > span + > svg + > path { + color: #a82a2a !important; + -webkit-animation: blink 0.75s infinite alternate; /* to blink 3 times instead of infinite write just 3 */ + -moz-animation: blink 0.75s infinite alternate; + -ms-animation: blink 0.75s infinite alternate; + -o-animation: blink 0.75s infinite alternate; + animation: blink 0.75s infinite alternate; +} + +@-webkit-keyframes blink { + from { + color: #a82a2a; + } + to { + color: #f55656; + } +} +@-moz-keyframes blink { + from { + color: #a82a2a; + } + to { + color: #f55656; + } +} +@-ms-keyframes blink { + from { + color: #a82a2a; + } + to { + color: #f55656; + } +} +@-o-keyframes blink { + from { + color: #a82a2a; + } + to { + color: #f55656; + } +} +@keyframes blink { + from { + color: #a82a2a; + } + to { + color: #f55656; + } +} + +/* ALLOW CONNECTION ALERT BLINK ANIMATION END */ + +/* Connected Device Info Button pulse animation START */ + +#connected-device-info-stepper-button { + transform: scale(1); + animation: pulse-black-connected-device 0.75s 3; +} + +@keyframes pulse-black-connected-device { + 0% { + transform: scale(1); + box-shadow: 0 0 0 0 rgba(61, 204, 145, 0.7); + } + + 60% { + transform: scale(0.75); + box-shadow: 0 0 0 15px rgba(61, 204, 145, 0.3); + } + + 100% { + transform: scale(1); + box-shadow: 0 0 0 5px rgba(0, 0, 0, 0); + } +} + +/* Connected Device Info Button pulse animation END */ + +#settings-overlay-inner > div > div.bp3-tab-panel { + width: 100% !important; +} + +/* settings panel tabs button left styles */ +#settings-overlay-inner > div > div.bp3-tab-list { + background-color: rgba(0, 0, 0, 0.1); + padding: 8px; + + /* height: 100%; */ +} + +/* settings inner 100% height regardless tab content height */ +#settings-overlay-inner > div { + height: 100%; +} + +.bp3-overlay-settings { + display: flex; + align-items: center; + justify-content: center; +} + +/* .bp3-overlay-settings.bp3-overlay-content { + display: flex; } */ +/* TODO: move to appropriate style file in ShareEntireScreenOrAppWindowControlGroup */ +#share-screen-or-app-btn-group > button > span { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +/* #root>div.MuiPaper-root.MuiStepper-root.MuiStepper-horizontal.MuiStepper-alternativeLabel.MuiPaper-elevation0>div:nth-child(1)>span>span.MuiStepLabel-iconContainer.MuiStepLabel-alternativeLabel>div { + transform: scale(1); + animation: pulse-black 2s infinite; +} */ + +.active-stepper-pulse-icon { + transform: scale(1); + animation: pulse-black 3s infinite; +} + +@keyframes pulse-black { + 0% { + transform: scale(0.9); + box-shadow: 0 0 0 0 rgba(191, 115, 38, 0.7); + } + + 60% { + transform: scale(1); + box-shadow: 0 0 0 12px rgba(255, 179, 102, 0.3); + } + + 100% { + transform: scale(0.9); + box-shadow: 0 0 0 20px rgba(0, 0, 0, 0); + } +} + +/* TODO: move it to DeskreenStepper.css ! */ +#step-label-deskreen > span.MuiStepLabel-labelContainer > span { + margin-top: 8px; +} + +#share-screen-or-app-btn-group > button:nth-child(1):hover { + border-width: 10px; +} + +.bp3-overlay::-webkit-scrollbar-track { + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); */ + border-radius: var(--custom-scrollbar-webkit-scrollbar-track-border-radius); + background-color: var( + --custom-scrollbar-webkit-scrollbar-track-background-color + ); +} + +.bp3-overlay::-webkit-scrollbar { + width: var(--custom-scrollbar-webkit-scrollbar-width); + background-color: var(--custom-scrollbar-webkit-scrollbar_background-color); +} + +.bp3-overlay::-webkit-scrollbar-thumb { + border-radius: var(--custom-scrollbar-webkit-scrollbar-thumb-border-radius); + + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); */ + background-color: var( + --custom-scrollbar-webkit-scrollbar-thumb-background-color + ); +} + +.bp3-drawer::-webkit-scrollbar-track { + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); */ + border-radius: var(--custom-scrollbar-webkit-scrollbar-track-border-radius); + background-color: var( + --custom-scrollbar-webkit-scrollbar-track-background-color + ); +} + +.bp3-drawer::-webkit-scrollbar { + width: var(--custom-scrollbar-webkit-scrollbar-width); + background-color: var(--custom-scrollbar-webkit-scrollbar_background-color); +} + +.bp3-drawer::-webkit-scrollbar-thumb { + border-radius: var(--custom-scrollbar-webkit-scrollbar-thumb-border-radius); + + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); */ + background-color: var( + --custom-scrollbar-webkit-scrollbar-thumb-background-color + ); +} + +body::-webkit-scrollbar-track { + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); */ + border-radius: var(--custom-scrollbar-webkit-scrollbar-track-border-radius); + background-color: var( + --custom-scrollbar-webkit-scrollbar-track-background-color + ); +} + +body::-webkit-scrollbar { + width: var(--custom-scrollbar-webkit-scrollbar-width); + background-color: var(--custom-scrollbar-webkit-scrollbar_background-color); +} + +body::-webkit-scrollbar-thumb { + border-radius: var(--custom-scrollbar-webkit-scrollbar-thumb-border-radius); + + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); */ + background-color: var( + --custom-scrollbar-webkit-scrollbar-thumb-background-color + ); +} + +.choose-app-or-screen-dialog::-webkit-scrollbar-track { + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); */ + border-radius: var(--custom-scrollbar-webkit-scrollbar-track-border-radius); + background-color: var( + --custom-scrollbar-webkit-scrollbar-track-background-color + ); +} + +.choose-app-or-screen-dialog::-webkit-scrollbar { + width: var(--custom-scrollbar-webkit-scrollbar-width); + background-color: var(--custom-scrollbar-webkit-scrollbar_background-color); +} + +.choose-app-or-screen-dialog::-webkit-scrollbar-thumb { + border-radius: var(--custom-scrollbar-webkit-scrollbar-thumb-border-radius); + + /* -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); */ + background-color: var( + --custom-scrollbar-webkit-scrollbar-thumb-background-color + ); +} + +/* --custom-scrollbar-webkit-scrollbar-thumb-border-radius: 10px; +--custom-scrollbar-webkit-scrollbar-thumb-background-color: #8A9BA8; +--custom-scrollbar-webkit-scrollbar_background-color: rgba(0,0,0,0); +--custom-scrollbar-webkit-scrollbar-width: 12px; +--custom-scrollbar-webkit-scrollbar-track-border-radius: 10px; +--custom-scrollbar-webkit-scrollbar-track-background-color: rgba(0,0,0,0); */ + h2 { margin: 0; font-size: 2.25rem; diff --git a/app/app.html b/app/app.html index 77ee53c..4f927ee 100644 --- a/app/app.html +++ b/app/app.html @@ -2,7 +2,7 @@ - Hello Deskreen! + Deskreen