Skip to content

Commit

Permalink
Merge pull request #996 from novasamatech/rc/2.0.1-120
Browse files Browse the repository at this point in the history
Release candidate - 1.0.3
  • Loading branch information
stepanLav authored Aug 1, 2023
2 parents 44f50b4 + e5dc690 commit 82b0713
Show file tree
Hide file tree
Showing 46 changed files with 1,281 additions and 971 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ jobs:
for file in *; do
if [ -f "$file" ]; then
new_name=$(echo "$file" | tr ' ' '-')
mv "$file" "$new_name"
if [ "$new_name" != "$file" ]; then # Skip files with no spaces
mv "$file" "$new_name"
fi
fi
done
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "nova-spektr",
"description": "Polkadot Enterprise application",
"version": "2.0.0",
"version": "1.0.3",
"main": "./release/build/main.js",
"license": "MIT",
"author": {
Expand Down
18 changes: 16 additions & 2 deletions src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { join } from 'path';
import { BrowserWindow, shell } from 'electron';
import log from 'electron-log';
import log, { LogFile } from 'electron-log';
import windowStateKeeper from 'electron-window-state';
import * as path from 'path';
import * as fs from 'fs';

import { ENVIRONMENT } from '@shared/constants';
import { APP_CONFIG } from '../../app.config';
Expand All @@ -17,7 +19,19 @@ log.transports.console.useStyles = true;
log.transports.file.fileName = 'nova-spektr.log';
log.transports.file.format = '{y}/{m}/{d} {h}:{i}:{s}.{ms} [{env}#{version}]-{processType} [{level}] > {text}';
log.transports.file.level = 'info';
log.transports.file.maxSize = 1048576 * 5; //5mb
log.transports.file.maxSize = 1048576 * 5; //5mb;
log.transports.file.archiveLogFn = (oldLogFile: LogFile): void => {
const file = oldLogFile.toString();
const info = path.parse(file);

try {
const date = new Date().toISOString();
let newFileName = path.join(info.dir, info.name + '.' + date + info.ext);
fs.renameSync(file, newFileName);
} catch (e) {
console.warn('Could not rotate log', e);
}
};

Object.assign(console, log.functions);
log.errorHandler.startCatching({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const QrGeneratorContainer = ({ countdown, onQrReset, chainId, children }: Props
<section className="flex flex-col items-center flex-1">
<SmallTitleText>{t('signing.scanQrTitle')}</SmallTitleText>

<div className="flex items-center gap-x-2 mt-3 mb-4">
<div className="flex items-center gap-x-2 mt-2 mb-6">
<FootnoteText className="text-text-tertiary">{t('signing.qrCountdownTitle')}</FootnoteText>
<CaptionText
align="center"
Expand Down Expand Up @@ -54,7 +54,7 @@ const QrGeneratorContainer = ({ countdown, onQrReset, chainId, children }: Props
{!children && <Shimmering />}
</div>

<div className="flex flex-row items-center gap-x-2 mt-6 mb-4">
<div className="flex flex-row items-center gap-x-2 mt-2 mb-4.5 h-[78px]">
<InfoLink url={TROUBLESHOOTING_URL}>{t('signing.troubleshootingLink')}</InfoLink>

<span className="border border-divider h-4"></span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const QrMultiframeSignatureReader = ({
size = 300,
cameraId,
className,
bgVideo,
bgVideo = true,
bgVideoClassName,
onCameraList,
onResult,
Expand Down Expand Up @@ -298,7 +298,7 @@ const QrMultiframeSignatureReader = ({

return (
<>
<div className="relative w-[240px] h-[240px] rounded-[1.75rem] overflow-hidden">
<div className="relative w-[240px] h-[240px] rounded-[22px] overflow-hidden">
<video
muted
autoPlay
Expand Down
117 changes: 73 additions & 44 deletions src/renderer/components/common/QrCode/QrReader/QrReaderWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import cn from 'classnames';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';

import cnTw from '@renderer/shared/utils/twMerge';
import { Icon, Shimmering } from '@renderer/components/ui';
import { Shimmering } from '@renderer/components/ui';
import { DropdownOption, DropdownResult } from '@renderer/components/ui-redesign/Dropdowns/common/types';
import { useI18n } from '@renderer/context/I18nContext';
import { ValidationErrors } from '@renderer/shared/utils/validation';
Expand All @@ -18,6 +18,12 @@ import './style.css';

const RESULT_DELAY = 250;

const ValidationErrorLabels = {
[ValidationErrors.INSUFFICIENT_BALANCE]: 'transfer.notEnoughBalanceError',
[ValidationErrors.INSUFFICIENT_BALANCE_FOR_FEE]: 'transfer.notEnoughBalanceForFeeError',
[ValidationErrors.INVALID_SIGNATURE]: 'transfer.invalidSignature',
};

type ScanResult = HexString | HexString[];
type QrReaderProps = Omit<React.ComponentProps<typeof QrSignatureReader>, 'onResult'>;

Expand All @@ -36,10 +42,17 @@ const QrReaderWrapper = ({ className, onResult, countdown, validationError, isMu
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<CameraError>();
const [progress, setProgress] = useState<Progress>();
const [isSuccess, setIsSuccess] = useState<boolean>(false);

const [activeCamera, setActiveCamera] = useState<DropdownResult<string>>();
const [availableCameras, setAvailableCameras] = useState<DropdownOption<string>[]>([]);

useEffect(() => {
if (validationError) {
setIsSuccess(false);
}
}, [validationError]);

const isCameraOn = !(error && CameraAccessErrors.includes(error));

const onCameraList = (cameras: VideoInput[]) => {
Expand Down Expand Up @@ -68,11 +81,13 @@ const QrReaderWrapper = ({ className, onResult, countdown, validationError, isMu

const onScanResult = (qrPayload: HexString | HexString[]) => {
if (countdown === 0) return;
setIsSuccess(true);

try {
setTimeout(() => onResult(qrPayload), RESULT_DELAY);
} catch (error) {
setError(CameraError.INVALID_ERROR);
setIsSuccess(false);

// try to scan again after 5 seconds
setTimeout(() => setError(undefined), 5000);
Expand All @@ -87,14 +102,16 @@ const QrReaderWrapper = ({ className, onResult, countdown, validationError, isMu
} else {
setError(CameraError.UNKNOWN_ERROR);
}

setIsSuccess(false);
setIsLoading(false);
};

const qrReaderProps: QrReaderProps = {
size: 240,
bgVideoClassName: 'w-[440px] h-[532px]',
bgVideoClassName: 'w-[440px] h-[544px]',
className: cnTw(
'z-10 w-[440px] h-[532px] top-[-124px]',
'z-10 w-[440px] h-[544px] top-[-126px]',
error === CameraError.INVALID_ERROR && 'blur-[13px]',
className,
),
Expand All @@ -105,7 +122,12 @@ const QrReaderWrapper = ({ className, onResult, countdown, validationError, isMu
};

return (
<div className="flex flex-col items-center flex-1 w-full relative pt-[52px] overflow-y-hidden">
<div
className={cnTw(
'flex flex-col items-center flex-1 w-full relative pt-[52px] overflow-y-hidden',
isLoading && 'bg-black',
)}
>
<SmallTitleText as="h3" className={cnTw('z-10', isCameraOn && 'text-white')}>
{t('signing.scanQrTitle')}
</SmallTitleText>
Expand All @@ -127,28 +149,20 @@ const QrReaderWrapper = ({ className, onResult, countdown, validationError, isMu

{/* scanning frame */}
<div className="w-[240px] h-[240px] mb-4">
{!isLoading && (
<div className="relative">
<Icon
name="qrFrame"
size={240}
className={cnTw(
'absolute w-full h-full min-h-[240px] camera-frame z-20',
isCameraOn ? 'text-white' : 'text-filter-border',
)}
/>
<div className="z-30 absolute flex flex-col items-center justify-center gap-y-4 w-full h-[240px]">
<SignatureReaderError
error={error}
validationError={validationError}
isCameraOn={isCameraOn && !isLoading}
onTryAgain={onRetryCamera}
/>
</div>
<div className="relative">
<div
className={cnTw(
'absolute w-[240px] h-[240px] z-20',
isCameraOn ? (isSuccess ? 'border-text-positive' : 'border-white') : 'border-filter-border',
'border-2 rounded-[22px]',
)}
></div>
<div className="z-30 absolute flex flex-col items-center justify-center gap-y-4 w-full h-[240px]">
<SignatureReaderError error={error} isCameraOn={isCameraOn && !isLoading} onTryAgain={onRetryCamera} />
</div>
)}
</div>

{isLoading && <Shimmering width={240} height={240} className="absolute rounded-[1.75rem]" />}
{isLoading && <Shimmering width={240} height={240} className="absolute rounded-[22px]" />}

{isCameraOn && (
<div className={cn(isLoading && 'hidden', className)}>
Expand All @@ -161,31 +175,46 @@ const QrReaderWrapper = ({ className, onResult, countdown, validationError, isMu
)}
</div>

{availableCameras && availableCameras.length > 1 && (
<Select
placeholder={t('onboarding.paritySigner.selectCameraLabel')}
selectedId={activeCamera?.id}
options={availableCameras}
className="mb-4 w-[208px]"
onChange={setActiveCamera}
/>
)}
<div className="h-8.5 mb-4">
{availableCameras && availableCameras.length > 1 && (
<Select
theme="dark"
placeholder={t('onboarding.paritySigner.selectCameraLabel')}
selectedId={activeCamera?.id}
options={availableCameras}
className="w-[208px]"
onChange={setActiveCamera}
/>
)}
</div>

{progress && (
<div className="flex items-center gap-x-2 mt-4 z-10">
<FootnoteText className="text-text-tertiary">{t('signing.parsingLabel')}</FootnoteText>
<CaptionText as="span" className="bg-label-background-gray text-white uppercase px-2 py-1 rounded-[26px]">
{t('signing.parsingCount', { current: progress.decoded, total: progress.total })}
</CaptionText>
</div>
)}
<div className="h-9 mb-3 z-10">
{validationError && (
<FootnoteText className="text-white text-center max-w-[320px] h-full flex items-center justify-center ">
{t(ValidationErrorLabels[validationError as keyof typeof ValidationErrorLabels])}
</FootnoteText>
)}
</div>

<footer className="flex w-full justify-start mt-auto pt-5 pb-6 pl-7 z-10">
<footer className="flex w-full justify-between items-center mt-auto h-[66px] px-5 mb-1 z-10">
{onGoBack && (
<Button variant="text" className={cn('h-6.5', isCameraOn ? WhiteTextButtonStyle : '')} onClick={onGoBack}>
<Button
variant="text"
className={cn('h-6.5 px-4', isCameraOn ? WhiteTextButtonStyle : '')}
onClick={onGoBack}
>
{t('operation.goBackButton')}
</Button>
)}

{progress && (
<div className="flex items-center gap-x-2 z-10 p-1.5 pl-3 rounded-2xl bg-black-background">
<FootnoteText className="text-text-tertiary">{t('signing.parsingLabel')}</FootnoteText>
<CaptionText as="span" className="bg-label-background-gray text-white uppercase px-2 py-1 rounded-[26px]">
{t('signing.parsingCount', { current: progress.decoded, total: progress.total })}
</CaptionText>
</div>
)}
</footer>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const QrSignatureReader = ({
controls={false}
ref={videoRef}
data-testid="qr-reader"
className={cnTw('object-cover absolute -scale-x-100', className)}
className={cnTw('object-cover absolute -scale-x-100', className)}
style={videoStyle}
>
{t('qrReader.videoError')}
Expand All @@ -173,7 +173,7 @@ const QrSignatureReader = ({

return (
<>
<div className="relative w-[240px] h-[240px] rounded-[1.75rem] overflow-hidden">
<div className="relative w-[240px] h-[240px] rounded-[22px] overflow-hidden">
<video
muted
autoPlay
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Button, FootnoteText } from '@renderer/components/ui-redesign';
import { ValidationErrors } from '@renderer/shared/utils/validation';
import { useI18n } from '@renderer/context/I18nContext';
import { CameraError, CameraErrorText } from '../common/constants';

type Props = {
error?: CameraError;
validationError?: ValidationErrors;
onTryAgain: () => void;
isCameraOn: boolean;
};

const SignatureReaderError = ({ error, validationError, onTryAgain, isCameraOn }: Props) => {
const SignatureReaderError = ({ error, onTryAgain, isCameraOn }: Props) => {
const { t } = useI18n();
const showTryAgainButton =
error && [CameraError.UNKNOWN_ERROR, CameraError.DENY_ERROR, CameraError.DECODE_ERROR].includes(error);
Expand All @@ -32,14 +30,6 @@ const SignatureReaderError = ({ error, validationError, onTryAgain, isCameraOn }
);
}

if (validationError === ValidationErrors.INSUFFICIENT_BALANCE) {
return <FootnoteText>{t('transfer.notEnoughBalanceError')}</FootnoteText>;
}

if (validationError === ValidationErrors.INSUFFICIENT_BALANCE_FOR_FEE) {
return <FootnoteText>{t('transfer.notEnoughBalanceForFeeError')}</FootnoteText>;
}

return null;
};

Expand Down
15 changes: 9 additions & 6 deletions src/renderer/components/common/Scanning/ScanMultiframeQr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type Props = {
countdown: number;
onGoBack: () => void;
onResetCountdown: () => void;
onResult: (unsigned: UnsignedTransaction[]) => void;
onResult: (unsigned: UnsignedTransaction[], txPayloads: Uint8Array[]) => void;
};

const ScanMultiframeQr = ({
Expand All @@ -49,6 +49,7 @@ const ScanMultiframeQr = ({
const [encoder, setEncoder] = useState<Encoder>();
const [bulkTransactions, setBulkTransactions] = useState<Uint8Array>();
const [unsignedTransactions, setUnsignedTransactions] = useState<UnsignedTransaction[]>([]);
const [txPayloads, setTxPayloads] = useState<Uint8Array[]>([]);

useEffect(() => {
if (unsignedTransactions.length) return;
Expand Down Expand Up @@ -84,6 +85,7 @@ const ScanMultiframeQr = ({

setBulkTransactions(createMultipleSignPayload(transactionsEncoded));
setUnsignedTransactions(txRequests.map((t) => t.unsigned));
setTxPayloads(txRequests.map((t) => t.signPayload));
setEncoder(Encoder.with_defaults(bulk, 128));
};

Expand All @@ -93,16 +95,17 @@ const ScanMultiframeQr = ({

return (
<div className="flex flex-col items-center w-full">
<QrGeneratorContainer countdown={countdown} chainId={chainId} onQrReset={setupTransactions}>
{bulkTxExist && encoder && <QrMultiframeGenerator payload={bulkTransactions} size={200} encoder={encoder} />}
</QrGeneratorContainer>

<div className="mt-10">
<QrGeneratorContainer countdown={countdown} chainId={chainId} onQrReset={setupTransactions}>
{bulkTxExist && encoder && <QrMultiframeGenerator payload={bulkTransactions} size={200} encoder={encoder} />}
</QrGeneratorContainer>
</div>
<div className="flex w-full justify-between mt-3">
<Button variant="text" onClick={onGoBack}>
{t('operation.goBackButton')}
</Button>

<Button disabled={!bulkTxExist || countdown === 0} onClick={() => onResult(unsignedTransactions)}>
<Button disabled={!bulkTxExist || countdown === 0} onClick={() => onResult(unsignedTransactions, txPayloads)}>
{t('signing.continueButton')}
</Button>
</div>
Expand Down
Loading

0 comments on commit 82b0713

Please sign in to comment.