mirror of
https://github.com/mudita/mudita-center.git
synced 2025-12-23 22:28:03 -05:00
Release v2.3.0 - to stage (#1840)
This commit is contained in:
2
.github/workflows/e2e-development.yml
vendored
2
.github/workflows/e2e-development.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
node-version: 18.16.1
|
||||
- name: Setup environment variables
|
||||
env:
|
||||
CI: "true"
|
||||
E2ECI: "true"
|
||||
TEST_GITHUB_TOKEN: ${{ secrets.MC_GITHUB_ACCESS_TOKEN }}
|
||||
TEST_BINARY_PATH: "../mudita-center/release/linux-unpacked/Mudita Center"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
|
||||
2
.github/workflows/e2e-feature-branch.yml
vendored
2
.github/workflows/e2e-feature-branch.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
node-version: 18.16.1
|
||||
- name: Setup environment variables
|
||||
env:
|
||||
CI: "true"
|
||||
E2ECI: "true"
|
||||
TEST_GITHUB_TOKEN: ${{ secrets.MC_GITHUB_ACCESS_TOKEN }}
|
||||
TEST_BINARY_PATH: "../mudita-center/release/linux-unpacked/Mudita Center"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
|
||||
2
.github/workflows/e2e-pre-production.yml
vendored
2
.github/workflows/e2e-pre-production.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
node-version: 18.16.1
|
||||
- name: Setup environment variables
|
||||
env:
|
||||
CI: "true"
|
||||
E2ECI: "true"
|
||||
TEST_GITHUB_TOKEN: ${{ secrets.MC_GITHUB_ACCESS_TOKEN }}
|
||||
TEST_BINARY_PATH: "../mudita-center/release/linux-unpacked/Mudita Center"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
|
||||
2
.github/workflows/e2e-production.yml
vendored
2
.github/workflows/e2e-production.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
node-version: 18.16.1
|
||||
- name: Setup environment variables
|
||||
env:
|
||||
CI: "true"
|
||||
E2ECI: "true"
|
||||
TEST_GITHUB_TOKEN: ${{ secrets.MC_GITHUB_ACCESS_TOKEN }}
|
||||
TEST_BINARY_PATH: "../mudita-center/release/linux-unpacked/Mudita Center"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
|
||||
1
.github/workflows/nexus-development.yml
vendored
1
.github/workflows/nexus-development.yml
vendored
@@ -21,6 +21,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
|
||||
1
.github/workflows/nexus-feature-branch.yml
vendored
1
.github/workflows/nexus-feature-branch.yml
vendored
@@ -21,6 +21,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
|
||||
3
.github/workflows/nexus-mass-update.yml
vendored
3
.github/workflows/nexus-mass-update.yml
vendored
@@ -23,6 +23,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
@@ -142,13 +143,11 @@ jobs:
|
||||
run: |
|
||||
$env:NODE_OPTIONS="--max-old-space-size=4096"
|
||||
$env:LOCALAPPDATA=""
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Build App for Linux/Mac
|
||||
if: matrix.os != 'Windows'
|
||||
run: |
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Verify apple sign
|
||||
if: matrix.os == 'macOS'
|
||||
|
||||
@@ -24,6 +24,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
@@ -165,13 +166,11 @@ jobs:
|
||||
run: |
|
||||
$env:NODE_OPTIONS="--max-old-space-size=4096"
|
||||
$env:LOCALAPPDATA=""
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Build App for Linux/Mac
|
||||
if: matrix.os != 'Windows'
|
||||
run: |
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Verify apple sign
|
||||
if: matrix.os == 'macOS'
|
||||
|
||||
3
.github/workflows/nexus-pre-production.yml
vendored
3
.github/workflows/nexus-pre-production.yml
vendored
@@ -23,6 +23,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
@@ -90,13 +91,11 @@ jobs:
|
||||
run: |
|
||||
$env:NODE_OPTIONS="--max-old-space-size=4096"
|
||||
$env:LOCALAPPDATA=""
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Build App for Linux/Mac
|
||||
if: matrix.os != 'Windows'
|
||||
run: |
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Verify apple sign
|
||||
if: matrix.os == 'macOS'
|
||||
|
||||
@@ -23,6 +23,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
@@ -142,13 +143,11 @@ jobs:
|
||||
run: |
|
||||
$env:NODE_OPTIONS="--max-old-space-size=4096"
|
||||
$env:LOCALAPPDATA=""
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Build App for Linux/Mac
|
||||
if: matrix.os != 'Windows'
|
||||
run: |
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Verify apple sign
|
||||
if: matrix.os == 'macOS'
|
||||
|
||||
3
.github/workflows/nexus-production.yml
vendored
3
.github/workflows/nexus-production.yml
vendored
@@ -23,6 +23,7 @@ jobs:
|
||||
- name: Setup Env for Windows
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
E2ECI: "false"
|
||||
PHRASE_API_KEY: ${{ secrets.PHRASE_API_KEY }}
|
||||
PHRASE_API_URL: ${{ secrets.PHRASE_API_URL }}
|
||||
PHRASE_API_KEY_DEV: ${{ secrets.PHRASE_API_KEY_DEV }}
|
||||
@@ -90,13 +91,11 @@ jobs:
|
||||
run: |
|
||||
$env:NODE_OPTIONS="--max-old-space-size=4096"
|
||||
$env:LOCALAPPDATA=""
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Build App for Linux/Mac
|
||||
if: matrix.os != 'Windows'
|
||||
run: |
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
cd apps/mudita-center/
|
||||
npm run app:dist
|
||||
- name: Verify apple sign
|
||||
if: matrix.os == 'macOS'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Software License Agreement
|
||||
|
||||
Mudita Center – https://github.com/mudita/mudita-center
|
||||
Copyright (c) 2017-2021, Mudita Sp. z o.o. All rights reserved.
|
||||
Copyright (c) 2017-2024, Mudita Sp. z o.o. All rights reserved.
|
||||
|
||||
## Sources of Intellectual Property Included in Mudita Center
|
||||
|
||||
|
||||
4
apps/mudita-center/package-lock.json
generated
4
apps/mudita-center/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@mudita/mudita-center-app",
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@mudita/mudita-center-app",
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"serialport": "10.1.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mudita-center",
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"description": "Mudita Center",
|
||||
"main": "./dist/main.js",
|
||||
"productName": "Mudita Center",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build": {
|
||||
"productName": "Mudita Center",
|
||||
"appId": "com.mudita.center",
|
||||
"copyright": "Copyright (c) 2017-2023, Mudita sp. z o.o. All rights reserved.",
|
||||
"copyright": "Copyright (c) 2017-2024, Mudita sp. z o.o. All rights reserved.",
|
||||
"mac": {
|
||||
"category": "public.app-category.utilities",
|
||||
"icon": "./icons/mac/icon.icns",
|
||||
|
||||
@@ -436,7 +436,7 @@ const InputSearchComponent: FunctionComponent<InputSearchProps> = ({
|
||||
useEffect(() => {
|
||||
if (listRef.current) {
|
||||
listRef.current.children[activeItemIndex]?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
behavior: "auto",
|
||||
block: "nearest",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -107,8 +107,6 @@ export const ModalSubTitle = styled(Text)`
|
||||
`
|
||||
|
||||
export const Close = styled(Button)`
|
||||
margin-top: -0.6rem;
|
||||
margin-right: -0.8rem;
|
||||
grid-area: Close;
|
||||
justify-self: end;
|
||||
width: 2.8rem;
|
||||
|
||||
@@ -300,33 +300,6 @@ exports[`Device: Mudita harmony matches snapshot 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
<a
|
||||
class="c5"
|
||||
displaystyle="7"
|
||||
href="#/onboarding"
|
||||
size="1"
|
||||
>
|
||||
<span
|
||||
class="c6 c7"
|
||||
data-testid="icon-Send"
|
||||
height="3.2"
|
||||
width="3.2"
|
||||
>
|
||||
<test-file-stub
|
||||
image="test-file-stub"
|
||||
/>
|
||||
</span>
|
||||
<p
|
||||
class="c8 c9"
|
||||
color="primary"
|
||||
>
|
||||
[value] module.onboarding
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
@@ -724,33 +697,6 @@ exports[`Device: Mudita pure matches snapshot 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
<a
|
||||
class="c5"
|
||||
displaystyle="7"
|
||||
href="#/onboarding"
|
||||
size="1"
|
||||
>
|
||||
<span
|
||||
class="c6 c7"
|
||||
data-testid="icon-Send"
|
||||
height="3.2"
|
||||
width="3.2"
|
||||
>
|
||||
<test-file-stub
|
||||
image="test-file-stub"
|
||||
/>
|
||||
</span>
|
||||
<p
|
||||
class="c8 c9"
|
||||
color="primary"
|
||||
>
|
||||
[value] module.onboarding
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
|
||||
@@ -93,16 +93,6 @@ export interface MenuElement {
|
||||
}
|
||||
|
||||
export const baseMenuElements: MenuElement[] = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
button: views[View.Onboarding],
|
||||
icon: IconType.Send,
|
||||
visibleOn: [DeviceType.MuditaPure, DeviceType.MuditaHarmony],
|
||||
},
|
||||
],
|
||||
viewKey: View.Onboarding,
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
|
||||
@@ -257,11 +257,6 @@
|
||||
"module.connecting.criticalBatteryLevelModalDescription": "Charge your Pure, then disconnect and reconnect your phone to use Mudita Center.",
|
||||
"module.connecting.criticalBatteryLevelModalHeaderTitle": "MuditaOS",
|
||||
"module.connecting.criticalBatteryLevelModalTitle": "The battery in your Pure is flat.",
|
||||
"module.connecting.errorConnectingDescription": "If you still have problems connecting to your device, have a look at our <link>help pages.</link>",
|
||||
"module.connecting.errorConnectingModalHeaderSubTitle": "We couldn't connect to your device",
|
||||
"module.connecting.errorConnectingModalHeaderTitle": "Error",
|
||||
"module.connecting.errorConnectingModalSecondaryButton": "Cancel",
|
||||
"module.connecting.errorConnectingModalTitle": "Make sure your device is plugged in using a USB C cable and restart Mudita Center or your computer to try again",
|
||||
"module.connecting.errorSyncModalButton": "Try again",
|
||||
"module.connecting.errorSyncModalDescription": "Please try to reconnect your device",
|
||||
"module.connecting.errorSyncModalHeaderTitle": "Error",
|
||||
|
||||
@@ -14,14 +14,9 @@ import { Settings } from "Core/settings/dto"
|
||||
|
||||
export const fakeAppSettings: Settings = {
|
||||
applicationId: "app-Nr8uiSV7KmWxX3WOFqZPF7uB",
|
||||
autostart: false,
|
||||
tethering: false,
|
||||
tray: true,
|
||||
osBackupLocation: `fake/path/pure/phone/backups/`,
|
||||
osDownloadLocation: `fake/path/pure/os/downloads/`,
|
||||
language: "en-US",
|
||||
neverConnected: true,
|
||||
collectingData: undefined,
|
||||
privacyPolicyAccepted: false,
|
||||
diagnosticSentTimestamp: 0,
|
||||
ignoredCrashDumps: [],
|
||||
|
||||
@@ -61,7 +61,7 @@ const AppUpdateStepModal: FunctionComponent<Props> = ({
|
||||
})
|
||||
|
||||
return () => unregister()
|
||||
})
|
||||
}, [appCurrentVersion, appLatestVersion])
|
||||
|
||||
useEffect(() => {
|
||||
const unregister = registerErrorAppUpdateListener(() => {
|
||||
@@ -70,10 +70,15 @@ const AppUpdateStepModal: FunctionComponent<Props> = ({
|
||||
toCenterVersion: appLatestVersion,
|
||||
state: TrackCenterUpdateState.Fail,
|
||||
})
|
||||
setAppUpdateStep(AppUpdateStep.Error)
|
||||
setAppUpdateStep((prevAppUpdateStep) => {
|
||||
// allow user to try updating before throw error to handle no network connection
|
||||
return prevAppUpdateStep === AppUpdateStep.Updating
|
||||
? AppUpdateStep.Error
|
||||
: prevAppUpdateStep
|
||||
})
|
||||
})
|
||||
return () => unregister()
|
||||
})
|
||||
}, [appCurrentVersion, appLatestVersion])
|
||||
|
||||
const handleProcessDownload = () => {
|
||||
void trackCenterUpdate({
|
||||
|
||||
@@ -91,7 +91,9 @@ export class AnalyticDataTrackerService implements AnalyticDataTrackerClass {
|
||||
this.visitorMetadata = visitorMetadata
|
||||
}
|
||||
|
||||
private trackRequest(event: TrackEvent): Promise<AxiosResponse | undefined> {
|
||||
private async trackRequest(
|
||||
event: TrackEvent
|
||||
): Promise<AxiosResponse | undefined> {
|
||||
const params: AxiosRequestConfig["params"] = {
|
||||
rec: 1,
|
||||
apiv: 1,
|
||||
@@ -101,10 +103,12 @@ export class AnalyticDataTrackerService implements AnalyticDataTrackerClass {
|
||||
...event,
|
||||
}
|
||||
|
||||
return this.httpClient.post(this.apiUrl, undefined, {
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
params,
|
||||
})
|
||||
try {
|
||||
return await this.httpClient.post(this.apiUrl, undefined, {
|
||||
params,
|
||||
})
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,17 @@
|
||||
import { createSelector } from "@reduxjs/toolkit"
|
||||
import { isAppUpdateProcessPassed } from "Core/app-initialization/selectors/is-app-update-process-passed.selector"
|
||||
import { isUsbAccessGrantedSelector } from "Core/settings/selectors/is-usb-access-granted.selector"
|
||||
import { settingsStateSelector } from "Core/settings/selectors"
|
||||
|
||||
export const isAppInitializationFinishedSelector = createSelector(
|
||||
isAppUpdateProcessPassed,
|
||||
isUsbAccessGrantedSelector,
|
||||
(appUpdateProcessPassed, usbAccessGranted): boolean => {
|
||||
return appUpdateProcessPassed && usbAccessGranted
|
||||
settingsStateSelector,
|
||||
(
|
||||
appUpdateProcessPassed,
|
||||
usbAccessGranted,
|
||||
{ privacyPolicyAccepted }
|
||||
): boolean => {
|
||||
return appUpdateProcessPassed && usbAccessGranted && Boolean(privacyPolicyAccepted)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5,11 +5,37 @@
|
||||
|
||||
import { createSelector } from "@reduxjs/toolkit"
|
||||
import { settingsStateSelector } from "Core/settings/selectors"
|
||||
import { shouldAppUpdateFlowVisible } from "Core/app-initialization/selectors/should-app-update-flow-visible.selector"
|
||||
|
||||
export const isAppUpdateProcessPassed = createSelector(
|
||||
const selectNoDataAboutUpdateAvaible = createSelector(
|
||||
settingsStateSelector,
|
||||
(settingsState): boolean => {
|
||||
const { updateAvailable, updateAvailableSkipped, checkingForUpdateFailed } = settingsState
|
||||
return checkingForUpdateFailed || updateAvailableSkipped || updateAvailable === false
|
||||
({
|
||||
updateAvailable,
|
||||
updateAvailableSkipped,
|
||||
checkingForUpdateFailed,
|
||||
updateRequired,
|
||||
}): boolean => {
|
||||
return (
|
||||
updateAvailableSkipped === undefined &&
|
||||
updateAvailable === undefined &&
|
||||
checkingForUpdateFailed &&
|
||||
!updateRequired
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
const selectAppUpdateFlowPassed = createSelector(
|
||||
settingsStateSelector,
|
||||
shouldAppUpdateFlowVisible,
|
||||
({ updateAvailable }, appUpdateFlowVisible): boolean => {
|
||||
return updateAvailable !== undefined && !appUpdateFlowVisible
|
||||
}
|
||||
)
|
||||
|
||||
export const isAppUpdateProcessPassed = createSelector(
|
||||
selectNoDataAboutUpdateAvaible,
|
||||
selectAppUpdateFlowPassed,
|
||||
(noDataAboutUpdateAvaible, appUpdateFlowPassed): boolean => {
|
||||
return appUpdateFlowPassed || noDataAboutUpdateAvaible
|
||||
}
|
||||
)
|
||||
|
||||
@@ -8,8 +8,7 @@ import { settingsStateSelector } from "Core/settings/selectors"
|
||||
|
||||
export const shouldAppUpdateFlowVisible = createSelector(
|
||||
settingsStateSelector,
|
||||
(settingsState): boolean => {
|
||||
const { updateAvailable, updateAvailableSkipped } = settingsState
|
||||
return updateAvailable === true && !updateAvailableSkipped
|
||||
({ updateAvailable, updateAvailableSkipped, updateRequired }): boolean => {
|
||||
return (updateAvailable && !updateAvailableSkipped) || updateRequired
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
export enum ErrorConnectingModalTestIds {
|
||||
Container = "error-connecting-modal-container"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { Meta } from "@storybook/react"
|
||||
import Story from "Core/__deprecated__/renderer/components/storybook/story.component"
|
||||
import { action } from "@storybook/addon-actions"
|
||||
import ErrorConnectingModal from "Core/connecting/components/error-connecting-modal"
|
||||
|
||||
export const ErrorConnectingModalStory = (): JSX.Element => {
|
||||
return (
|
||||
<Story transparentMode>
|
||||
<ErrorConnectingModal
|
||||
open
|
||||
closeModal={action("Close Backup Modal")}
|
||||
onCloseButton={action("Cancel Backup Action")}
|
||||
/>
|
||||
</Story>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: "Views|Connecting/Backup Modal Dialogs",
|
||||
component: ErrorConnectingModalStory,
|
||||
} as Meta
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { FunctionComponent } from "Core/core/types/function-component.interface"
|
||||
import { intl } from "Core/__deprecated__/renderer/utils/intl"
|
||||
import Icon from "Core/__deprecated__/renderer/components/core/icon/icon.component"
|
||||
import Text, {
|
||||
TextDisplayStyle,
|
||||
} from "Core/__deprecated__/renderer/components/core/text/text.component"
|
||||
import React, { ComponentProps, ReactNode } from "react"
|
||||
import { defineMessages } from "react-intl"
|
||||
import {
|
||||
ModalContent,
|
||||
ModalDialog,
|
||||
ModalLink,
|
||||
RoundIconWrapper,
|
||||
} from "Core/ui/components/modal-dialog"
|
||||
import { ModalSize } from "Core/__deprecated__/renderer/components/core/modal/modal.interface"
|
||||
import { Size } from "Core/__deprecated__/renderer/components/core/button/button.config"
|
||||
import { ErrorConnectingModalTestIds } from "Core/connecting/components/error-connecting-modal-test-ids.enum"
|
||||
import { IconType } from "Core/__deprecated__/renderer/components/core/icon/icon-type"
|
||||
import { ModalLayers } from "Core/modals-manager/constants/modal-layers.enum"
|
||||
import { ipcRenderer } from "electron-better-ipc"
|
||||
import { HelpActions } from "Core/__deprecated__/common/enums/help-actions.enum"
|
||||
|
||||
const messages = defineMessages({
|
||||
errorConnectingModalHeaderTitle: {
|
||||
id: "module.connecting.errorConnectingModalHeaderTitle",
|
||||
},
|
||||
errorConnectingModalHeaderSubtitle: {
|
||||
id: "module.connecting.errorConnectingModalHeaderSubTitle",
|
||||
},
|
||||
errorConnectingModalSecondaryButton: {
|
||||
id: "module.connecting.errorConnectingModalSecondaryButton",
|
||||
},
|
||||
errorConnectingModalTitle: {
|
||||
id: "module.connecting.errorConnectingModalTitle",
|
||||
},
|
||||
errorConnectingDescription: {
|
||||
id: "module.connecting.errorConnectingDescription",
|
||||
},
|
||||
})
|
||||
|
||||
const ErrorConnectingModal: FunctionComponent<
|
||||
ComponentProps<typeof ModalDialog>
|
||||
> = ({ closeModal, onClose, ...props }) => {
|
||||
const openHelpWindow = () => ipcRenderer.callMain(HelpActions.OpenWindow)
|
||||
return (
|
||||
<ModalDialog
|
||||
testId={ErrorConnectingModalTestIds.Container}
|
||||
size={ModalSize.Small}
|
||||
title={intl.formatMessage(messages.errorConnectingModalHeaderTitle)}
|
||||
actionButtonSize={Size.FixedMedium}
|
||||
layer={ModalLayers.ErrorConnecting}
|
||||
closeButton={false}
|
||||
actionButtonLabel={intl.formatMessage(
|
||||
messages.errorConnectingModalSecondaryButton
|
||||
)}
|
||||
onActionButtonClick={closeModal}
|
||||
onClose={onClose}
|
||||
closeModal={closeModal}
|
||||
{...props}
|
||||
>
|
||||
<ModalContent>
|
||||
<RoundIconWrapper>
|
||||
<Icon type={IconType.ThinFail} width={3.2} />
|
||||
</RoundIconWrapper>
|
||||
<Text
|
||||
displayStyle={TextDisplayStyle.Headline4}
|
||||
color="primary"
|
||||
message={messages.errorConnectingModalHeaderSubtitle}
|
||||
/>
|
||||
<Text
|
||||
displayStyle={TextDisplayStyle.Paragraph3}
|
||||
color="info"
|
||||
message={messages.errorConnectingModalTitle}
|
||||
/>
|
||||
<Text
|
||||
displayStyle={TextDisplayStyle.Paragraph3}
|
||||
color="info"
|
||||
message={{
|
||||
...messages.errorConnectingDescription,
|
||||
values: {
|
||||
link: (...chunks: ReactNode[]) => (
|
||||
<ModalLink onClick={openHelpWindow}>{chunks}</ModalLink>
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ModalContent>
|
||||
</ModalDialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorConnectingModal
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
const registerFirstPhoneConnection = (): void => {
|
||||
void updateSettings({ key: "neverConnected", value: false })
|
||||
}
|
||||
|
||||
export default registerFirstPhoneConnection
|
||||
@@ -7,7 +7,7 @@ import React, { ComponentProps } from "react"
|
||||
import { Provider } from "react-redux"
|
||||
import store from "Core/__deprecated__/renderer/store"
|
||||
import { renderWithThemeAndIntl } from "Core/__deprecated__/renderer/utils/render-with-theme-and-intl"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.container"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.component"
|
||||
import ContactSupportFlow from "Core/contact-support/containers/contact-support-flow.container"
|
||||
import { ContactSupportFlowTestIds } from "Core/contact-support/components/contact-support-flow-test-ids.component"
|
||||
|
||||
|
||||
@@ -274,6 +274,13 @@ const Contacts: FunctionComponent<ContactsProps> = ({
|
||||
|
||||
if (payload || message) {
|
||||
const newError: FormError[] = []
|
||||
|
||||
if (message === "Edit Contact request failed") {
|
||||
void modalService.openModal(<ErrorDataModal />, true)
|
||||
reject()
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
message === "phone-number-duplicated" &&
|
||||
payload?.primaryPhoneNumberIsDuplicated
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useDeviceConnectedEffect } from "Core/core/hooks/use-device-connected-e
|
||||
import { useApplicationUpdateEffects } from "Core/core/hooks/use-application-update-effects"
|
||||
import { CrashDump } from "Core/crash-dump"
|
||||
import NetworkStatusChecker from "Core/__deprecated__/renderer/components/core/network-status-checker/network-status-checker.container"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.container"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.component"
|
||||
import { useWatchOutboxEntriesEffect } from "Core/core/hooks/use-watch-outbox-entries-effect"
|
||||
import { useWatchUnlockStatus } from "Core/core/hooks/use-watch-unlock-status-effect"
|
||||
import { useDeviceLockedEffect } from "Core/core/hooks/use-device-locked-effect"
|
||||
|
||||
@@ -198,14 +198,13 @@ test("Don't display any modal when `loadingState` flag is equal to `State.Loadin
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("display `Error` modal if data.files list isn't empty and `loadingState` flag is equal to `State.Failed`", () => {
|
||||
test("Don't display any modal when `loadingState` flag is equal to `State.Failed`", () => {
|
||||
render({
|
||||
...initialStateMock,
|
||||
crashDump: {
|
||||
...initialStateMock.crashDump,
|
||||
data: {
|
||||
...initialStateMock.crashDump.data,
|
||||
files: ["/pure/logs/crash-dumps/file.hex"],
|
||||
},
|
||||
loadingState: State.Failed,
|
||||
},
|
||||
@@ -214,7 +213,9 @@ test("display `Error` modal if data.files list isn't empty and `loadingState` fl
|
||||
expect(
|
||||
screen.queryByTestId(CrashDumpModalTestingIds.Content)
|
||||
).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId(CrashDumpTestingIds.Failed)).toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByTestId(CrashDumpTestingIds.Failed)
|
||||
).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByTestId(CrashDumpTestingIds.Success)
|
||||
).not.toBeInTheDocument()
|
||||
|
||||
@@ -157,7 +157,6 @@ const mapStateToProps = (state: ReduxRootState) => ({
|
||||
state.crashDump.downloadingState === State.Loaded &&
|
||||
state.crashDump.sendingState === State.Loaded,
|
||||
failed:
|
||||
state.crashDump.loadingState === State.Failed ||
|
||||
state.crashDump.downloadingState === State.Failed ||
|
||||
state.crashDump.sendingState === State.Failed,
|
||||
deviceType: state.device.deviceType,
|
||||
|
||||
@@ -11,49 +11,42 @@ enum SerialPortGroup {
|
||||
uucp = "uucp",
|
||||
}
|
||||
|
||||
const POTENTIAL_GROUPS = [SerialPortGroup.dialout, SerialPortGroup.uucp];
|
||||
|
||||
export class DesktopService {
|
||||
public async isLinux(): Promise<boolean> {
|
||||
return process.platform === "linux"
|
||||
}
|
||||
|
||||
public async hasUserSerialPortAccess(): Promise<boolean> {
|
||||
const userGroups = await this.getUserGroups();
|
||||
return POTENTIAL_GROUPS.some(group => userGroups.includes(group));
|
||||
const userGroups = await this.getUserGroups()
|
||||
return userGroups.includes(SerialPortGroup.dialout)
|
||||
}
|
||||
|
||||
public async addUserToSerialPortGroup(): Promise<void> {
|
||||
const userGroups = await this.getUserGroups();
|
||||
const groupName = POTENTIAL_GROUPS.find(group => !userGroups.includes(group));
|
||||
const command = `usermod -aG ${SerialPortGroup.dialout} $USER & usermod -aG ${SerialPortGroup.uucp} $USER`
|
||||
// Set simpler process.title, otherwise, there is an error from sudoPrompt.exec - 'process.title cannot be used as a valid name.'
|
||||
process.title = "Mudita Center: assign serial port access"
|
||||
|
||||
if (groupName) {
|
||||
const command = `usermod -aG ${groupName} $USER`;
|
||||
// Set simpler process.title, otherwise, there is an error from sudoPrompt.exec - 'process.title cannot be used as a valid name.'
|
||||
process.title = "Mudita Center: assign serial port access";
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
sudoPrompt.exec(command, { name: 'User Serial Port Access' }, (error) => {
|
||||
if (error === null) {
|
||||
resolve();
|
||||
} else {
|
||||
reject("Could not add user to serial port group");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
sudoPrompt.exec(command, { name: "User Serial Port Access" }, (error) => {
|
||||
if (error === null) {
|
||||
resolve()
|
||||
} else {
|
||||
reject("Could not add user to serial port group")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private async getUserGroups(): Promise<string[]> {
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
exec("groups", (error, stdout, stderr) => {
|
||||
if (error || stderr) {
|
||||
reject(`${error?.name} - ${error?.message} - ${stderr}`);
|
||||
reject(`${error?.name} - ${error?.message} - ${stderr}`)
|
||||
} else {
|
||||
const groups = stdout.trim().split(/\s+/);
|
||||
resolve(groups);
|
||||
const groups = stdout.trim().split(/\s+/)
|
||||
resolve(groups)
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,9 @@ export const initializeMuditaPure = createAsyncThunk<
|
||||
return DeviceInitializationStatus.Aborted
|
||||
}
|
||||
const unlockStatus = await dispatch(getUnlockStatus())
|
||||
if (!unlockStatus.payload) {
|
||||
if (unlockStatus.payload === "LOCKED") {
|
||||
return DeviceInitializationStatus.Initializing
|
||||
}
|
||||
|
||||
if (!isActiveDeviceAttachedSelector(getState())) {
|
||||
} else if (unlockStatus.payload === "ABORTED") {
|
||||
dispatch(
|
||||
setDeviceInitializationStatus(DeviceInitializationStatus.Aborted)
|
||||
)
|
||||
|
||||
@@ -86,7 +86,7 @@ export const PasscodeInputs: FunctionComponent<Props> = ({
|
||||
const onKeyDownHandler =
|
||||
(number: number) =>
|
||||
(e: { key: string; code: string; preventDefault: () => void }) => {
|
||||
if (/[0-9]/.test(e.key)) {
|
||||
if (/^[0-9]$/.test(e.key)) {
|
||||
const backspaceEdgeCase = activeInput === 0 && e.key === ""
|
||||
if (
|
||||
activeInput !== undefined &&
|
||||
|
||||
@@ -46,6 +46,13 @@ const letterKeyEvent = {
|
||||
charCode: 0,
|
||||
} as KeyboardEvent
|
||||
|
||||
const F1KeyEvent = {
|
||||
key: "F1",
|
||||
code: "F1",
|
||||
keyCode: 112,
|
||||
charCode: 0,
|
||||
} as KeyboardEvent
|
||||
|
||||
const backspaceKeyEvent = {
|
||||
key: "Backspace",
|
||||
code: "Backspace",
|
||||
@@ -88,7 +95,7 @@ test("Passcode inputs are disabled when filled", () => {
|
||||
expect(inputsList()[0]).toHaveStyleRule("background-color", "#f4f5f6")
|
||||
})
|
||||
|
||||
test("Show typing error message", async () => {
|
||||
test("Show typing error message when a latter is typed", async () => {
|
||||
const { inputsList, errorMessage } = renderer()
|
||||
fireEvent.keyDown(inputsList()[0] as Element, letterKeyEvent)
|
||||
await waitFor(() =>
|
||||
@@ -98,6 +105,16 @@ test("Show typing error message", async () => {
|
||||
)
|
||||
})
|
||||
|
||||
test("Show typing error message when F1 is typed", async () => {
|
||||
const { inputsList, errorMessage } = renderer()
|
||||
fireEvent.keyDown(inputsList()[0] as Element, F1KeyEvent)
|
||||
await waitFor(() =>
|
||||
expect(errorMessage()).toHaveTextContent(
|
||||
"[value] component.passcodeModalErrorTyping"
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
test("Message is displayed properly when request about phone lock return internal server error", async () => {
|
||||
const { inputsList, errorMessage } = renderer({
|
||||
unlockDevice: jest.fn().mockReturnValue({
|
||||
|
||||
@@ -44,21 +44,24 @@ const DrawerWrapper = styled("div")`
|
||||
.EZDrawer .EZDrawer__checkbox:checked ~ .EZDrawer__container {
|
||||
transition: transform 500ms;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`
|
||||
|
||||
const DrawerChildrenContainer = styled("div")`
|
||||
padding: 1.3rem 1.8rem 1.3rem 1.8rem;
|
||||
padding: 1.8rem;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3.2rem;
|
||||
gap: 3.4rem;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
const Header = styled("div")`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-right: 1.8rem;
|
||||
padding-right: 0.7rem;
|
||||
`
|
||||
|
||||
const DevicesContainer = styled("div")`
|
||||
|
||||
@@ -25,10 +25,9 @@ import { intl } from "Core/__deprecated__/renderer/utils/intl"
|
||||
import { getSerialNumberValue } from "Core/utils/get-serial-number-value"
|
||||
|
||||
const Device = styled("div")<{ active: boolean }>`
|
||||
padding: 1.8rem 2.4rem 1.8rem 1rem;
|
||||
padding: 1.8rem 2.1rem;
|
||||
display: flex;
|
||||
min-width: 27.2rem;
|
||||
max-width: 27.2rem;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background: ${backgroundColor("main")};
|
||||
@@ -55,7 +54,7 @@ const DeviceImageContainer = styled("div")`
|
||||
justify-content: center;
|
||||
min-height: 9.6rem;
|
||||
min-width: 9.1rem;
|
||||
padding: 0 2.4rem 0 0rem;
|
||||
padding: 0 2.8rem 0 0;
|
||||
`
|
||||
|
||||
export const DeviceImageStyled = styled(DeviceImage)`
|
||||
@@ -101,7 +100,7 @@ const ActiveDot = styled("span")`
|
||||
background-color: ${textColor("primary")};
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin: 0rem 0.5rem 0.8rem 0.5rem;
|
||||
margin: 0 0.5rem 0.8rem 0.5rem;
|
||||
`
|
||||
|
||||
const DeviceName = styled(Text)`
|
||||
|
||||
@@ -4,19 +4,22 @@
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||
import { DeviceEvent } from "Core/device/constants"
|
||||
import { DeviceCommunicationError, DeviceEvent } from "Core/device/constants"
|
||||
import { unlockDeviceStatusRequest } from "Core/device/requests"
|
||||
import { ReduxRootState } from "Core/__deprecated__/renderer/store"
|
||||
import { setLockTime } from "Core/device/actions/base.action"
|
||||
import { setLockTime, setUnlockedStatus } from "Core/device/actions/base.action"
|
||||
import { getLeftTimeSelector } from "Core/device/selectors"
|
||||
import { handleCommunicationError } from "Core/device/actions/handle-communication-error.action"
|
||||
|
||||
export type UnlockStatus = "UNLOCKED" | "LOCKED" | "ABORTED"
|
||||
|
||||
export const getUnlockStatus = createAsyncThunk<
|
||||
boolean,
|
||||
UnlockStatus,
|
||||
void,
|
||||
{ state: ReduxRootState }
|
||||
>(DeviceEvent.GetUnlockedStatus, async (_, { dispatch, getState }) => {
|
||||
const { ok, error } = await unlockDeviceStatusRequest()
|
||||
const result = await unlockDeviceStatusRequest()
|
||||
const { ok, error } = result
|
||||
const leftTime = getLeftTimeSelector(getState())
|
||||
|
||||
if (ok && leftTime !== undefined) {
|
||||
@@ -27,5 +30,15 @@ export const getUnlockStatus = createAsyncThunk<
|
||||
await dispatch(handleCommunicationError(error))
|
||||
}
|
||||
|
||||
return ok
|
||||
if (error?.type === DeviceCommunicationError.DeviceLocked) {
|
||||
dispatch(setUnlockedStatus(false))
|
||||
return "LOCKED"
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
dispatch(setUnlockedStatus(true))
|
||||
return "UNLOCKED"
|
||||
} else {
|
||||
return "ABORTED"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { createReducer } from "@reduxjs/toolkit"
|
||||
import {
|
||||
getUnlockStatus,
|
||||
loadDeviceData,
|
||||
loadStorageInfoAction,
|
||||
setCriticalBatteryLevelStatus,
|
||||
@@ -110,15 +109,6 @@ export const deviceReducer = createReducer<DeviceState>(
|
||||
error: action.payload as AppError,
|
||||
}
|
||||
})
|
||||
.addCase(getUnlockStatus.fulfilled, (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
status: {
|
||||
...state.status,
|
||||
unlocked: action.payload,
|
||||
},
|
||||
}
|
||||
})
|
||||
.addCase(setLockTime, (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -25,8 +25,7 @@ import Icon, {
|
||||
IconSize,
|
||||
} from "Core/__deprecated__/renderer/components/core/icon/icon.component"
|
||||
import { NormalizedHelpEntry } from "Core/__deprecated__/renderer/utils/contentful/normalize-help-data"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.container"
|
||||
import { fontWeight } from "Core/core/styles/theming/theme-getters"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.component"
|
||||
import { IconButtonWithSecondaryTooltip } from "Core/__deprecated__/renderer/components/core/icon-button-with-tooltip/icon-button-with-secondary-tooltip.component"
|
||||
import { defineMessages } from "react-intl"
|
||||
import { IconType } from "Core/__deprecated__/renderer/components/core/icon/icon-type"
|
||||
@@ -91,10 +90,6 @@ const ArrowIcon = styled(Icon)`
|
||||
transform: rotate(270deg);
|
||||
`
|
||||
|
||||
const NormalHeading = styled(Text)`
|
||||
font-weight: ${fontWeight("default")};
|
||||
`
|
||||
|
||||
const Help: FunctionComponent<Props> = ({
|
||||
list: { collection = [], items },
|
||||
searchQuestion,
|
||||
|
||||
@@ -19,9 +19,9 @@ export const checkAppRequiresSerialPortGroup = createAsyncThunk<
|
||||
ModalsManagerEvent.ShowAppRequiresSerialPortGroup,
|
||||
async (_, { dispatch }) => {
|
||||
const runningOnLinux = await isLinux()
|
||||
const runningOnCI = process.env.CI === "true"
|
||||
const runningOnE2ECI = process.env.E2ECI === "true"
|
||||
|
||||
if (!runningOnLinux || runningOnCI) {
|
||||
if (!runningOnLinux || runningOnE2ECI) {
|
||||
dispatch(setUserHasSerialPortAccess(true))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,34 +4,28 @@
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { useSelector } from "react-redux"
|
||||
import { FunctionComponent } from "Core/core/types/function-component.interface"
|
||||
import ContactSupportFlow from "Core/contact-support/containers/contact-support-flow.container"
|
||||
import { UpdateOsInterruptedFlowContainer } from "Core/update/components/update-os-interrupted-flow"
|
||||
import ErrorConnectingModal from "Core/connecting/components/error-connecting-modal"
|
||||
import ConnectingLoaderModal from "Core/modals-manager/components/connecting-loader-modal.component"
|
||||
import DetachedDuringUploadErrorModal
|
||||
from "Core/files-manager/components/dettached-during-upload-error-modal/dettached-during-upload-error-modal.component"
|
||||
import DetachedDuringUploadErrorModal from "Core/files-manager/components/dettached-during-upload-error-modal/dettached-during-upload-error-modal.component"
|
||||
import { AppUpdateFlow } from "Core/settings/components"
|
||||
import { shouldAppUpdateVisibleSelector } from "Core/modals-manager/selectors/should-app-update-visible.selector"
|
||||
import { ReduxRootState } from "Core/__deprecated__/renderer/store"
|
||||
|
||||
type Props = {
|
||||
contactSupportFlowShow: boolean
|
||||
deviceInitializationFailedModalShowEnabled: boolean
|
||||
hideModals: () => void
|
||||
}
|
||||
|
||||
const ModalsManager: FunctionComponent<Props> = ({
|
||||
contactSupportFlowShow,
|
||||
deviceInitializationFailedModalShowEnabled,
|
||||
hideModals,
|
||||
}) => {
|
||||
const ModalsManager: FunctionComponent = () => {
|
||||
const appUpdateVisible = useSelector(shouldAppUpdateVisibleSelector)
|
||||
const contactSupportFlowShow = useSelector(
|
||||
(state: ReduxRootState) => state.modalsManager.contactSupportFlowShow
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{deviceInitializationFailedModalShowEnabled && (
|
||||
<ErrorConnectingModal open closeModal={hideModals} />
|
||||
)}
|
||||
{contactSupportFlowShow && <ContactSupportFlow />}
|
||||
<UpdateOsInterruptedFlowContainer />
|
||||
<ConnectingLoaderModal />
|
||||
<DetachedDuringUploadErrorModal />
|
||||
{appUpdateVisible && <AppUpdateFlow />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { connect } from "react-redux"
|
||||
import { ReduxRootState, RootState } from "Core/__deprecated__/renderer/store"
|
||||
import ModalsManager from "Core/modals-manager/components/modals-manager.component"
|
||||
import { hideModals } from "Core/modals-manager"
|
||||
|
||||
const mapStateToProps = (state: RootState & ReduxRootState) => {
|
||||
return {
|
||||
...state.modalsManager,
|
||||
deviceInitializationFailedModalShowEnabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
hideModals,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ModalsManager)
|
||||
@@ -4,14 +4,7 @@
|
||||
*/
|
||||
|
||||
export enum ModalsManagerEvent {
|
||||
CheckAppForcedUpdateFlowToShow = "CHECK_APP_FORCED_UPDATE_FLOW_TO_SHOW",
|
||||
CheckAppUpdateFlowToShow = "CHECK_APP_UPDATE_FLOW_TO_SHOW",
|
||||
CheckCollectingDataModalToShow = "CHECK_COLLECTING_DATA_MODAL_TO_SHOW",
|
||||
HideModals = "HIDE_MODALS",
|
||||
HideCollectingDataModal = "HIDE_COLLECTING_DATA_MODAL",
|
||||
ShowModal = "SHOW_MODAL",
|
||||
ShowCollectingDataModal = "SHOW_COLLECTING_DATA_MODAL",
|
||||
ShowAppForcedUpdateFlow = "SHOW_APP_FORCED_UPDATE_FLOW",
|
||||
ShowAppRequiresSerialPortGroup = "SHOW_APP_REQUIRES_SERIAL_PORT_GROUP",
|
||||
AddUSBAccess = "ADD_USB_ACCESS",
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ export enum ModalLayers {
|
||||
ContactSupport,
|
||||
Passcode,
|
||||
EULA,
|
||||
ErrorConnecting,
|
||||
LinuxSerialPortGroup,
|
||||
UpdateApp,
|
||||
PrivacyPolicy,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { createSelector } from "@reduxjs/toolkit"
|
||||
import { getAppInitializationStatus } from "Core/app-initialization/selectors/get-app-initialization-status.selector"
|
||||
import { modalsManagerStateSelector } from "Core/modals-manager"
|
||||
import { AppInitializationStatus } from "Core/app-initialization/reducers/app-initialization.interface"
|
||||
|
||||
export const shouldAppUpdateVisibleSelector = createSelector(
|
||||
modalsManagerStateSelector,
|
||||
getAppInitializationStatus,
|
||||
({ appUpdateFlowShow }, appInitializationStatus): boolean => {
|
||||
return (
|
||||
appUpdateFlowShow &&
|
||||
appInitializationStatus === AppInitializationStatus.Initialized
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -41,7 +41,7 @@ export interface Props {
|
||||
onTroubleshooting?: VoidFunction
|
||||
}
|
||||
|
||||
const deviceNames = ["Harmony 1", "Harmony 2", "Pure", "Kompakt"]
|
||||
const deviceNames = ["Harmony 1", "Harmony 2", "Pure"]
|
||||
|
||||
const OnboardingUI: FunctionComponent<Props> = ({
|
||||
onCancel = noop,
|
||||
|
||||
@@ -17,14 +17,9 @@ const lastBackupDate = new Date("2020-01-15T07:35:01.562Z")
|
||||
type Props = ComponentProps<typeof Backup>
|
||||
|
||||
const defaultProps: Props = {
|
||||
autostart: false,
|
||||
collectingData: undefined,
|
||||
tethering: false,
|
||||
tray: false,
|
||||
diagnosticSentTimestamp: 0,
|
||||
language: "en-US",
|
||||
onBackupCreate: noop,
|
||||
neverConnected: false,
|
||||
osBackupLocation: "",
|
||||
osDownloadLocation: "",
|
||||
backupActionDisabled: false,
|
||||
|
||||
@@ -15,6 +15,8 @@ export const setSettings = createAction<
|
||||
| "updateAvailable"
|
||||
| "latestVersion"
|
||||
| "updateAvailableSkipped"
|
||||
| "checkingForUpdate"
|
||||
| "checkingForUpdateFailed"
|
||||
>
|
||||
>(SettingsEvent.SetSettings)
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||
import { SettingsEvent } from "Core/settings/constants"
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
export const deleteCollectingData = createAsyncThunk(
|
||||
SettingsEvent.DeleteCollectingData,
|
||||
async () => {
|
||||
await updateSettings({ key: "collectingData", value: undefined })
|
||||
|
||||
return
|
||||
}
|
||||
)
|
||||
@@ -6,9 +6,5 @@
|
||||
export * from "./base.action"
|
||||
export * from "./check-update-available.action"
|
||||
export * from "./load-settings.action"
|
||||
export * from "./send-diagnostic-data.action"
|
||||
export * from "./set-diagnostic-timestamp.action"
|
||||
export * from "./set-os-backup-location.action"
|
||||
export * from "./toggle-tethering.action"
|
||||
export * from "./toggle-collection-data.action"
|
||||
export * from "./toggle-privacy-policy-accepted.action"
|
||||
|
||||
@@ -18,7 +18,7 @@ jest.mock("../../../../apps/mudita-center/package.json", () => ({
|
||||
}))
|
||||
|
||||
jest.mock("Core/settings/requests", () => ({
|
||||
getSettings: jest.fn().mockReturnValue({ collectingData: false }),
|
||||
getSettings: jest.fn().mockReturnValue({}),
|
||||
getConfiguration: jest.fn().mockReturnValue({
|
||||
centerVersion: "1.0.0",
|
||||
productVersions: {
|
||||
@@ -63,9 +63,6 @@ test("`loadSettings` action dispatch SettingsEvent.LoadSettings event and calls
|
||||
{
|
||||
type: SettingsEvent.SetSettings,
|
||||
payload: {
|
||||
checkingForUpdate: false,
|
||||
checkingForUpdateFailed: false,
|
||||
collectingData: false,
|
||||
currentVersion: `${packageInfo.version}`,
|
||||
lowestSupportedVersions: {
|
||||
lowestSupportedCenterVersion: "1.0.0",
|
||||
|
||||
@@ -37,15 +37,11 @@ export const loadSettings = createAsyncThunk<
|
||||
)
|
||||
}
|
||||
|
||||
settings.collectingData ? logger.enableRollbar() : logger.disableRollbar()
|
||||
|
||||
dispatch(
|
||||
setSettings({
|
||||
...settings,
|
||||
updateRequired,
|
||||
currentVersion: packageInfo.version,
|
||||
checkingForUpdate: false,
|
||||
checkingForUpdateFailed: false,
|
||||
lowestSupportedVersions: {
|
||||
lowestSupportedCenterVersion: configuration.centerVersion,
|
||||
lowestSupportedProductVersion: configuration.productVersions,
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { AnyAction } from "@reduxjs/toolkit"
|
||||
import thunk from "redux-thunk"
|
||||
import createMockStore from "redux-mock-store"
|
||||
import MockDate from "mockdate"
|
||||
import { sendDiagnosticData } from "./send-diagnostic-data.action"
|
||||
import logger from "Core/__deprecated__/main/utils/logger"
|
||||
|
||||
const dayBeforeDateMock = "2021-07-05T11:50:35.157Z"
|
||||
const todayDateMock = "2021-08-05T11:50:35.157Z"
|
||||
|
||||
MockDate.set(new Date(todayDateMock))
|
||||
|
||||
const setDiagnosticTimestampMock = jest.fn()
|
||||
|
||||
jest.mock("Core/__deprecated__/main/utils/logger", () => ({
|
||||
error: jest.fn(),
|
||||
info: jest.fn(),
|
||||
}))
|
||||
jest.mock("Core/settings/actions/set-diagnostic-timestamp.action", () => ({
|
||||
setDiagnosticTimestamp: () => setDiagnosticTimestampMock,
|
||||
}))
|
||||
|
||||
afterAll(() => {
|
||||
MockDate.reset()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
describe("When `serialNumber` is equal to `undefined`", () => {
|
||||
test("calls `logger.error` with error message and skip `setDiagnosticTimestamp`", async () => {
|
||||
const mockStore = createMockStore([thunk])({
|
||||
settings: {
|
||||
collectingData: undefined,
|
||||
diagnosticSentTimestamp: undefined,
|
||||
},
|
||||
device: {
|
||||
data: {
|
||||
serialNumber: undefined,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
meta: { requestId },
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
} = await mockStore.dispatch(sendDiagnosticData() as unknown as AnyAction)
|
||||
|
||||
expect(mockStore.getActions()).toEqual([
|
||||
sendDiagnosticData.pending(requestId),
|
||||
sendDiagnosticData.fulfilled(undefined, requestId, undefined),
|
||||
])
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
"Send Diagnostic Data: device logs fail. SerialNumber is undefined."
|
||||
)
|
||||
expect(setDiagnosticTimestampMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("When user disallow sending diagnostic data", () => {
|
||||
test("calls `logger.info` with info message and skip `setDiagnosticTimestamp`", async () => {
|
||||
const mockStore = createMockStore([thunk])({
|
||||
settings: {
|
||||
collectingData: false,
|
||||
diagnosticSentTimestamp: undefined,
|
||||
},
|
||||
device: {
|
||||
data: {
|
||||
serialNumber: "0000000000",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
meta: { requestId },
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
} = await mockStore.dispatch(sendDiagnosticData() as unknown as AnyAction)
|
||||
|
||||
expect(mockStore.getActions()).toEqual([
|
||||
sendDiagnosticData.pending(requestId),
|
||||
sendDiagnosticData.fulfilled(undefined, requestId, undefined),
|
||||
])
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
"Send Diagnostic Data: user no allowed sent data"
|
||||
)
|
||||
expect(setDiagnosticTimestampMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("When diagnostic data has been sended today", () => {
|
||||
test("calls `logger.info` with info message and skip `setDiagnosticTimestamp`", async () => {
|
||||
const mockStore = createMockStore([thunk])({
|
||||
settings: {
|
||||
collectingData: true,
|
||||
diagnosticSentTimestamp: todayDateMock,
|
||||
},
|
||||
device: {
|
||||
data: {
|
||||
serialNumber: "0000000000",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
meta: { requestId },
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
} = await mockStore.dispatch(sendDiagnosticData() as unknown as AnyAction)
|
||||
|
||||
expect(mockStore.getActions()).toEqual([
|
||||
sendDiagnosticData.pending(requestId),
|
||||
sendDiagnosticData.fulfilled(undefined, requestId, undefined),
|
||||
])
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
`Send Diagnostic Data: data was sent at ${todayDateMock}`
|
||||
)
|
||||
expect(setDiagnosticTimestampMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("When diagnostic data has been sended day before", () => {
|
||||
test("calls `logger.info` with info message and set `diagnosticSentTimestamp` via `setDiagnosticTimestamp`", async () => {
|
||||
const mockStore = createMockStore([thunk])({
|
||||
settings: {
|
||||
collectingData: true,
|
||||
diagnosticSentTimestamp: dayBeforeDateMock,
|
||||
},
|
||||
device: {
|
||||
data: {
|
||||
serialNumber: "0000000000",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
meta: { requestId },
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
} = await mockStore.dispatch(sendDiagnosticData() as unknown as AnyAction)
|
||||
|
||||
expect(mockStore.getActions()).toEqual([
|
||||
sendDiagnosticData.pending(requestId),
|
||||
sendDiagnosticData.fulfilled(undefined, requestId, undefined),
|
||||
])
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
`Send Diagnostic Data: skipped until the diagnostic data and storage system will be refined`
|
||||
)
|
||||
expect(setDiagnosticTimestampMock).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||
import { ReduxRootState } from "Core/__deprecated__/renderer/store"
|
||||
import logger from "Core/__deprecated__/main/utils/logger"
|
||||
import { isToday } from "Core/__deprecated__/renderer/utils/is-today"
|
||||
import { SettingsEvent } from "Core/settings/constants"
|
||||
import { setDiagnosticTimestamp } from "Core/settings/actions/set-diagnostic-timestamp.action"
|
||||
|
||||
export const sendDiagnosticData = createAsyncThunk<void, void>(
|
||||
SettingsEvent.SendDiagnosticData,
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async (_, { dispatch, getState }) => {
|
||||
const state = getState() as ReduxRootState
|
||||
|
||||
const { collectingData, diagnosticSentTimestamp } = state.settings
|
||||
const serialNumber = state.device.data?.serialNumber
|
||||
|
||||
if (serialNumber === undefined) {
|
||||
logger.error(
|
||||
`Send Diagnostic Data: device logs fail. SerialNumber is undefined.`
|
||||
)
|
||||
return
|
||||
}
|
||||
if (!collectingData) {
|
||||
logger.info("Send Diagnostic Data: user no allowed sent data")
|
||||
return
|
||||
}
|
||||
|
||||
if (isToday(new Date(diagnosticSentTimestamp))) {
|
||||
logger.info(
|
||||
`Send Diagnostic Data: data was sent at ${diagnosticSentTimestamp}`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Send Diagnostic Data: skipped until the diagnostic data and storage system will be refined`
|
||||
)
|
||||
|
||||
const nowTimestamp = Date.now()
|
||||
|
||||
void dispatch(setDiagnosticTimestamp(nowTimestamp))
|
||||
|
||||
return
|
||||
}
|
||||
)
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { AnyAction } from "@reduxjs/toolkit"
|
||||
import thunk from "redux-thunk"
|
||||
import createMockStore from "redux-mock-store"
|
||||
import { setDiagnosticTimestamp } from "./set-diagnostic-timestamp.action"
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
jest.mock("Core/settings/requests", () => ({
|
||||
updateSettings: jest.fn(),
|
||||
}))
|
||||
|
||||
const dateMock = new Date("2021-08-05T11:50:35.157Z").getDate()
|
||||
|
||||
const mockStore = createMockStore([thunk])()
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
test("calls `setDiagnosticTimestamp` and `updateSettings` request with timestamp", async () => {
|
||||
expect(updateSettings).not.toHaveBeenCalled()
|
||||
|
||||
const {
|
||||
meta: { requestId },
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
} = await mockStore.dispatch(
|
||||
setDiagnosticTimestamp(dateMock) as unknown as AnyAction
|
||||
)
|
||||
|
||||
expect(mockStore.getActions()).toEqual([
|
||||
setDiagnosticTimestamp.pending(requestId, dateMock),
|
||||
setDiagnosticTimestamp.fulfilled(dateMock, requestId, dateMock),
|
||||
])
|
||||
expect(updateSettings).toHaveBeenCalledWith({
|
||||
key: "diagnosticSentTimestamp",
|
||||
value: dateMock,
|
||||
})
|
||||
})
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||
import { SettingsEvent } from "Core/settings/constants"
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
export const setDiagnosticTimestamp = createAsyncThunk<number, number>(
|
||||
SettingsEvent.SetDiagnosticTimestamp,
|
||||
async (payload) => {
|
||||
await updateSettings({ key: "diagnosticSentTimestamp", value: payload })
|
||||
|
||||
return payload
|
||||
}
|
||||
)
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||
import { SettingsEvent } from "Core/settings/constants"
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
export const toggleCollectionData = createAsyncThunk<boolean, boolean>(
|
||||
SettingsEvent.ToggleCollectionData,
|
||||
async (payload) => {
|
||||
await updateSettings({ key: "collectingData", value: payload })
|
||||
|
||||
return payload
|
||||
}
|
||||
)
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { AnyAction } from "@reduxjs/toolkit"
|
||||
import thunk from "redux-thunk"
|
||||
import createMockStore from "redux-mock-store"
|
||||
import { toggleTethering } from "./toggle-tethering.action"
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
jest.mock("Core/settings/requests", () => ({
|
||||
updateSettings: jest.fn(),
|
||||
}))
|
||||
|
||||
const mockStore = createMockStore([thunk])()
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
test("calls `toggleTethering` and `updateSettings` request with boolean", async () => {
|
||||
expect(updateSettings).not.toHaveBeenCalled()
|
||||
|
||||
const {
|
||||
meta: { requestId },
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
} = await mockStore.dispatch(toggleTethering(true) as unknown as AnyAction)
|
||||
|
||||
expect(mockStore.getActions()).toEqual([
|
||||
toggleTethering.pending(requestId, true),
|
||||
toggleTethering.fulfilled(true, requestId, true),
|
||||
])
|
||||
expect(updateSettings).toHaveBeenCalledWith({ key: "tethering", value: true })
|
||||
})
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit"
|
||||
import { SettingsEvent } from "Core/settings/constants"
|
||||
import { updateSettings } from "Core/settings/requests"
|
||||
|
||||
export const toggleTethering = createAsyncThunk<boolean, boolean>(
|
||||
SettingsEvent.ToggleTethering,
|
||||
async (payload) => {
|
||||
await updateSettings({ key: "tethering", value: payload })
|
||||
|
||||
return payload
|
||||
}
|
||||
)
|
||||
@@ -12,6 +12,7 @@ import { ModalLayers } from "Core/modals-manager/constants/modal-layers.enum"
|
||||
import { settingsStateSelector } from "Core/settings/selectors"
|
||||
import { Dispatch } from "Core/__deprecated__/renderer/store"
|
||||
import { skipAvailableUpdate } from "Core/settings/actions/base.action"
|
||||
import { hideModals } from "Core/modals-manager"
|
||||
|
||||
export const AppUpdateFlow: FunctionComponent = () => {
|
||||
const dispatch = useDispatch<Dispatch>()
|
||||
@@ -21,6 +22,7 @@ export const AppUpdateFlow: FunctionComponent = () => {
|
||||
|
||||
const handleCloseModal = () => {
|
||||
dispatch(skipAvailableUpdate())
|
||||
dispatch(hideModals())
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { defineMessages } from "react-intl"
|
||||
import { useDispatch } from "react-redux"
|
||||
import { ipcRenderer } from "electron-better-ipc"
|
||||
import styled from "styled-components"
|
||||
import {
|
||||
ModalContent,
|
||||
ModalDialog,
|
||||
@@ -10,24 +15,17 @@ import {
|
||||
} from "Core/ui/components/modal-dialog"
|
||||
import { ModalSize } from "Core/__deprecated__/renderer/components/core/modal/modal.interface"
|
||||
import Icon from "Core/__deprecated__/renderer/components/core/icon/icon.component"
|
||||
|
||||
import React, { useEffect } from "react"
|
||||
import { IconType } from "Core/__deprecated__/renderer/components/core/icon/icon-type"
|
||||
import Text from "Core/__deprecated__/renderer/components/core/text/text.component"
|
||||
import { TextDisplayStyle } from "Core/__deprecated__/renderer/components/core/text/text.component"
|
||||
import styled from "styled-components"
|
||||
import {
|
||||
fontWeight,
|
||||
textColor,
|
||||
} from "Core/core/styles/theming/theme-getters"
|
||||
import { defineMessages } from "react-intl"
|
||||
import { intl } from "Core/__deprecated__/renderer/utils/intl"
|
||||
import { ipcRenderer } from "electron-better-ipc"
|
||||
import { AboutActions } from "Core/__deprecated__/common/enums/about-actions.enum"
|
||||
import { useDispatch, useSelector } from "react-redux"
|
||||
import { togglePrivacyPolicyAccepted } from "Core/settings/actions"
|
||||
import { Dispatch, ReduxRootState } from "Core/__deprecated__/renderer/store"
|
||||
import { deleteCollectingData } from "Core/settings/actions/delete-collecting-data.action"
|
||||
import { Dispatch } from "Core/__deprecated__/renderer/store"
|
||||
import { FunctionComponent } from "Core/core/types/function-component.interface"
|
||||
import { ModalLayers } from "Core/modals-manager/constants/modal-layers.enum"
|
||||
|
||||
@@ -55,30 +53,15 @@ export const DescriptionText = styled(Text)`
|
||||
`
|
||||
|
||||
const PrivacyPolicyModal: FunctionComponent = () => {
|
||||
const { collectingData } = useSelector(
|
||||
(state: ReduxRootState) => state.settings
|
||||
)
|
||||
|
||||
const dispatch = useDispatch<Dispatch>()
|
||||
|
||||
const openPrivacyPolicyWindow = () =>
|
||||
ipcRenderer.callMain(AboutActions.PolicyOpenBrowser)
|
||||
|
||||
const handleAgreeButtonClick = (): void => {
|
||||
void dispatch(deleteCollectingData())
|
||||
void dispatch(togglePrivacyPolicyAccepted(true))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (collectingData === undefined) {
|
||||
void dispatch(togglePrivacyPolicyAccepted(true))
|
||||
}
|
||||
}, [collectingData, dispatch])
|
||||
|
||||
if (collectingData === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
open
|
||||
|
||||
@@ -8,15 +8,11 @@ export enum SettingsEvent {
|
||||
SetLatestVersion = "settings_set-latest-version",
|
||||
SetSettings = "settings_set-settings",
|
||||
SetSetting = "settings_set-setting",
|
||||
SetDiagnosticTimestamp = "settings_set-diagnostic-timestamp",
|
||||
SetOsBackupLocation = "settings_set-os-backup-location",
|
||||
ToggleTethering = "settings_toggle-tethering",
|
||||
ToggleUpdateAvailable = "settings_toggle-update-available",
|
||||
ToggleCollectionData = "settings_toggle-collection-data",
|
||||
TogglePrivacyPolicyAccepted = "settings_toggle-privacy-policy-accepted",
|
||||
CheckUpdateAvailable = "settings_check-update-available",
|
||||
SendDiagnosticData = "settings_send-diagnostic-data",
|
||||
DeleteCollectingData = "settings_delete-collecting-data",
|
||||
SetCheckingForUpdate = "settings_set-checking-for-update",
|
||||
SetCheckingForUpdateFailed = "settings_set-checking-for-update-failed",
|
||||
SkipAvailableUpdate = "settings_skip-available-update",
|
||||
|
||||
@@ -9,14 +9,9 @@ import { SettingsService } from "Core/settings/services"
|
||||
|
||||
export const fakeSettings: Settings = {
|
||||
applicationId: "app-Nr8uiSV7KmWxX3WOFqZPF7uB",
|
||||
autostart: false,
|
||||
tethering: false,
|
||||
tray: true,
|
||||
osBackupLocation: `fake/path/pure/phone/backups/`,
|
||||
osDownloadLocation: `fake/path/pure/os/downloads/`,
|
||||
language: "en-US",
|
||||
neverConnected: true,
|
||||
collectingData: false,
|
||||
privacyPolicyAccepted: false,
|
||||
diagnosticSentTimestamp: 0,
|
||||
ignoredCrashDumps: [],
|
||||
|
||||
@@ -4,17 +4,13 @@
|
||||
*/
|
||||
|
||||
export interface Settings {
|
||||
settingsSchemaVersion?: number
|
||||
applicationId: string | null
|
||||
osBackupLocation: string
|
||||
osDownloadLocation: string
|
||||
language: string
|
||||
ignoredCrashDumps: string[]
|
||||
diagnosticSentTimestamp: number
|
||||
collectingData: boolean | undefined
|
||||
privacyPolicyAccepted: boolean | undefined
|
||||
neverConnected: boolean
|
||||
tray: boolean
|
||||
autostart: boolean
|
||||
tethering: boolean
|
||||
usbAccessRestartRequired: boolean
|
||||
}
|
||||
|
||||
@@ -16,14 +16,9 @@ import { SettingsState } from "Core/settings/reducers"
|
||||
|
||||
const settings: SettingsState = {
|
||||
applicationId: "app-Nr8uiSV7KmWxX3WOFqZPF7uB",
|
||||
autostart: false,
|
||||
tethering: false,
|
||||
tray: true,
|
||||
osBackupLocation: `fake/path/pure/phone/backups/`,
|
||||
osDownloadLocation: `fake/path/pure/os/downloads/`,
|
||||
language: "en-US",
|
||||
neverConnected: true,
|
||||
collectingData: false,
|
||||
privacyPolicyAccepted: false,
|
||||
diagnosticSentTimestamp: 0,
|
||||
ignoredCrashDumps: [],
|
||||
@@ -81,74 +76,6 @@ describe("Functionality: loading settings", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("Functionality: toggle tethering", () => {
|
||||
test("Event: ToggleTethering/fulfilled set `tethering` value", () => {
|
||||
expect(
|
||||
settingsReducer(undefined, {
|
||||
type: fulfilledAction(SettingsEvent.ToggleTethering),
|
||||
payload: true,
|
||||
})
|
||||
).toEqual({
|
||||
...initialState,
|
||||
tethering: true,
|
||||
})
|
||||
expect(
|
||||
settingsReducer(undefined, {
|
||||
type: fulfilledAction(SettingsEvent.ToggleTethering),
|
||||
payload: false,
|
||||
})
|
||||
).toEqual({
|
||||
...initialState,
|
||||
tethering: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Functionality: toggle data collection", () => {
|
||||
test("Event: ToggleCollectionData/fulfilled set `collectingData` value", () => {
|
||||
expect(
|
||||
settingsReducer(undefined, {
|
||||
type: fulfilledAction(SettingsEvent.ToggleCollectionData),
|
||||
payload: true,
|
||||
})
|
||||
).toEqual({
|
||||
...initialState,
|
||||
collectingData: true,
|
||||
})
|
||||
expect(
|
||||
settingsReducer(undefined, {
|
||||
type: fulfilledAction(SettingsEvent.ToggleCollectionData),
|
||||
payload: false,
|
||||
})
|
||||
).toEqual({
|
||||
...initialState,
|
||||
collectingData: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Functionality: diagnostic data timestamp", () => {
|
||||
test("Event: SetDiagnosticTimestamp/fulfilled set `diagnosticSentTimestamp` value", () => {
|
||||
const timestamp = new Date().getDate()
|
||||
|
||||
expect(
|
||||
settingsReducer(
|
||||
{
|
||||
...initialState,
|
||||
diagnosticSentTimestamp: 0,
|
||||
},
|
||||
{
|
||||
type: fulfilledAction(SettingsEvent.SetDiagnosticTimestamp),
|
||||
payload: timestamp,
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
...initialState,
|
||||
diagnosticSentTimestamp: timestamp,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Functionality: os backup location", () => {
|
||||
test("Event: SetOsBackupLocation/fulfilled set `osBackupLocation` value", () => {
|
||||
expect(
|
||||
|
||||
@@ -10,15 +10,11 @@ import {
|
||||
setLatestVersion,
|
||||
setOsBackupLocation,
|
||||
setSettings,
|
||||
setDiagnosticTimestamp,
|
||||
toggleTethering,
|
||||
toggleApplicationUpdateAvailable,
|
||||
toggleCollectionData,
|
||||
togglePrivacyPolicyAccepted,
|
||||
setCheckingForUpdate,
|
||||
setUserHasSerialPortAccess,
|
||||
} from "Core/settings/actions"
|
||||
import { deleteCollectingData } from "Core/settings/actions/delete-collecting-data.action"
|
||||
import {
|
||||
setCheckingForUpdateFailed,
|
||||
skipAvailableUpdate,
|
||||
@@ -35,12 +31,7 @@ export const initialState: SettingsState = {
|
||||
language: "",
|
||||
ignoredCrashDumps: [],
|
||||
diagnosticSentTimestamp: 0,
|
||||
collectingData: undefined,
|
||||
privacyPolicyAccepted: undefined,
|
||||
neverConnected: false,
|
||||
tray: false,
|
||||
autostart: false,
|
||||
tethering: false,
|
||||
updateRequired: false,
|
||||
updateAvailable: undefined,
|
||||
updateAvailableSkipped: undefined,
|
||||
@@ -73,27 +64,13 @@ export const settingsReducer = createReducer<SettingsState>(
|
||||
state.latestVersion = action.payload
|
||||
})
|
||||
|
||||
.addCase(toggleTethering.fulfilled, (state, action) => {
|
||||
state.tethering = action.payload
|
||||
})
|
||||
|
||||
.addCase(toggleApplicationUpdateAvailable, (state, action) => {
|
||||
state.updateAvailable = action.payload
|
||||
})
|
||||
|
||||
.addCase(toggleCollectionData.fulfilled, (state, action) => {
|
||||
state.collectingData = action.payload
|
||||
})
|
||||
|
||||
.addCase(togglePrivacyPolicyAccepted.fulfilled, (state, action) => {
|
||||
state.privacyPolicyAccepted = action.payload
|
||||
})
|
||||
.addCase(deleteCollectingData.fulfilled, (state, _) => {
|
||||
state.collectingData = undefined
|
||||
})
|
||||
.addCase(setDiagnosticTimestamp.fulfilled, (state, action) => {
|
||||
state.diagnosticSentTimestamp = action.payload
|
||||
})
|
||||
|
||||
.addCase(setOsBackupLocation.fulfilled, (state, action) => {
|
||||
state.osBackupLocation = action.payload
|
||||
|
||||
@@ -17,14 +17,9 @@ jest.mock("Core/settings/store/schemas", () => ({
|
||||
|
||||
export const fakeSettings: Settings = {
|
||||
applicationId: "app-Nr8uiSV7KmWxX3WOFqZPF7uB",
|
||||
autostart: false,
|
||||
tethering: false,
|
||||
tray: true,
|
||||
osBackupLocation: `fake/path/pure/phone/backups/`,
|
||||
osDownloadLocation: `fake/path/pure/os/downloads/`,
|
||||
language: "en-US",
|
||||
neverConnected: true,
|
||||
collectingData: false,
|
||||
privacyPolicyAccepted: false,
|
||||
diagnosticSentTimestamp: 0,
|
||||
ignoredCrashDumps: [],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
||||
export const privacyPolicyAcceptedMigration = (store: any) => {
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||
store.set("privacyPolicyAccepted", false)
|
||||
if (store.get("settingsSchemaVersion") === undefined) {
|
||||
store.set("privacyPolicyAccepted", false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) Mudita sp. z o.o. All rights reserved.
|
||||
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
// AUTO DISABLED - fix me if you like :)
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
||||
export const removeDeprecatedFieldsMigration = (store: any) => {
|
||||
store.set("settingsSchemaVersion", 4)
|
||||
store.delete("collectingData")
|
||||
store.delete("autostart")
|
||||
store.delete("tray")
|
||||
store.delete("neverConnected")
|
||||
}
|
||||
@@ -11,22 +11,13 @@ import translationConfig from "App/translations.config.json"
|
||||
import { generateApplicationId } from "Core/settings/store/schemas/generate-application-id"
|
||||
|
||||
export const settingsSchema: Schema<Settings> = {
|
||||
settingsSchemaVersion: {
|
||||
type: "number",
|
||||
},
|
||||
applicationId: {
|
||||
type: ["string", "null"],
|
||||
default: generateApplicationId(),
|
||||
},
|
||||
autostart: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
tethering: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
tray: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
osBackupLocation: {
|
||||
type: "string",
|
||||
default: path.join(getAppPath(), "pure", "phone", "backups"),
|
||||
@@ -39,17 +30,9 @@ export const settingsSchema: Schema<Settings> = {
|
||||
type: "string",
|
||||
default: translationConfig.defaultLanguage,
|
||||
},
|
||||
neverConnected: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
},
|
||||
collectingData: {
|
||||
type: "boolean",
|
||||
default: undefined,
|
||||
},
|
||||
privacyPolicyAccepted: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
default: true,
|
||||
},
|
||||
diagnosticSentTimestamp: {
|
||||
type: "number",
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
removeUnusedFields,
|
||||
osDownloadLocationMigration,
|
||||
} from "Core/settings/store/migrations"
|
||||
import { removeDeprecatedFieldsMigration } from "Core/settings/store/migrations/004-remove-deprecated-fields.migration"
|
||||
|
||||
export const settingsStore = new Store({
|
||||
name: "settings",
|
||||
@@ -27,5 +28,6 @@ export const settingsStore = new Store({
|
||||
">=1.4.1": removeUnusedFields,
|
||||
">=2.0.2": privacyPolicyAcceptedMigration,
|
||||
">=2.1.0": osDownloadLocationMigration,
|
||||
">=2.3.0": removeDeprecatedFieldsMigration,
|
||||
},
|
||||
})
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mudita-center",
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mudita-center",
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"license": "GPL-3.0",
|
||||
"workspaces": [
|
||||
"apps/mudita-center",
|
||||
@@ -247,7 +247,7 @@
|
||||
}
|
||||
},
|
||||
"apps/mudita-center": {
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"serialport": "10.1.0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "mudita-center",
|
||||
"description": "Mudita Center",
|
||||
"productName": "Mudita Center",
|
||||
"version": "2.2.8",
|
||||
"version": "2.3.0",
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
|
||||
Reference in New Issue
Block a user