mirror of
https://github.com/danog/telegram-tt.git
synced 2025-01-22 13:21:37 +01:00
Auth / QR Code: New animated design (#1778)
This commit is contained in:
parent
41a67c9baf
commit
b2ad6a0ae4
9015
package-lock.json
generated
9015
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -110,7 +110,10 @@
|
||||
"os-browserify": "^0.3.0",
|
||||
"pako": "^2.0.4",
|
||||
"path-browserify": "^1.0.1",
|
||||
"qr-creator": "^1.0.0",
|
||||
"qr-code-styling": "github:zubiden/qr-code-styling#10f7cf3",
|
||||
"websocket": "^1.0.34"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.1.2"
|
||||
}
|
||||
}
|
||||
|
BIN
src/assets/blank.png
Normal file
BIN
src/assets/blank.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 B |
BIN
src/assets/tgs/auth/QrPlane.tgs
Normal file
BIN
src/assets/tgs/auth/QrPlane.tgs
Normal file
Binary file not shown.
@ -113,22 +113,34 @@
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.qr-container {
|
||||
.qr-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
transition: transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 300ms;
|
||||
|
||||
&.pre-animate {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.8);
|
||||
transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
canvas {
|
||||
padding: 0.625rem;
|
||||
border-radius: var(--border-radius-default);
|
||||
background: var(--color-white);
|
||||
margin-top: -0.625rem;
|
||||
}
|
||||
.qr-plane {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-telegram-blue);
|
||||
}
|
||||
|
||||
.qr-container {
|
||||
background-color: white;
|
||||
border-radius: var(--border-radius-default);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import QrCreator from 'qr-creator';
|
||||
import QrCodeStyling from 'qr-code-styling';
|
||||
import React, {
|
||||
FC, useEffect, useRef, memo, useCallback,
|
||||
FC, useEffect, useRef, memo, useCallback, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
@ -10,13 +10,17 @@ import { LangCode } from '../../types';
|
||||
import { DEFAULT_LANG_CODE } from '../../config';
|
||||
import { setLanguage } from '../../util/langProvider';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
|
||||
import getAnimationData from '../common/helpers/animatedAssets';
|
||||
|
||||
import useLangString from '../../hooks/useLangString';
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
|
||||
|
||||
import Loading from '../ui/Loading';
|
||||
import Button from '../ui/Button';
|
||||
import AnimatedSticker from '../common/AnimatedSticker';
|
||||
import blankUrl from '../../assets/blank.png';
|
||||
|
||||
type StateProps =
|
||||
Pick<GlobalState, 'connectionState' | 'authState' | 'authQrCode'>
|
||||
@ -25,6 +29,29 @@ type StateProps =
|
||||
};
|
||||
|
||||
const DATA_PREFIX = 'tg://login?token=';
|
||||
const QR_SIZE = 280;
|
||||
const QR_PLANE_SIZE = 54;
|
||||
|
||||
const QR_CODE = new QrCodeStyling({
|
||||
width: QR_SIZE,
|
||||
height: QR_SIZE,
|
||||
image: blankUrl,
|
||||
margin: 10,
|
||||
type: 'svg',
|
||||
dotsOptions: {
|
||||
type: 'rounded',
|
||||
},
|
||||
cornersSquareOptions: {
|
||||
type: 'extra-rounded',
|
||||
},
|
||||
imageOptions: {
|
||||
imageSize: 0.4,
|
||||
margin: 8,
|
||||
},
|
||||
qrOptions: {
|
||||
errorCorrectionLevel: 'M',
|
||||
},
|
||||
});
|
||||
|
||||
const AuthCode: FC<StateProps> = ({
|
||||
connectionState,
|
||||
@ -43,25 +70,45 @@ const AuthCode: FC<StateProps> = ({
|
||||
const qrCodeRef = useRef<HTMLDivElement>(null);
|
||||
const continueText = useLangString(suggestedLanguage, 'ContinueOnThisLanguage');
|
||||
const [isLoading, markIsLoading, unmarkIsLoading] = useFlag();
|
||||
const [isQrMounted, markQrMounted, unmarkQrMounted] = useFlag();
|
||||
|
||||
const [animationData, setAnimationData] = useState<string>();
|
||||
const [isAnimationLoaded, setIsAnimationLoaded] = useState(false);
|
||||
const handleAnimationLoad = useCallback(() => setIsAnimationLoaded(true), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!authQrCode || connectionState !== 'connectionStateReady') {
|
||||
return;
|
||||
if (!animationData) {
|
||||
getAnimationData('QrPlane').then(setAnimationData);
|
||||
}
|
||||
}, [animationData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!authQrCode) {
|
||||
return () => {
|
||||
unmarkQrMounted();
|
||||
};
|
||||
}
|
||||
|
||||
if (connectionState !== 'connectionStateReady') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const container = qrCodeRef.current!;
|
||||
|
||||
container.innerHTML = '';
|
||||
container.classList.remove('pre-animate');
|
||||
container.parentElement!.classList.remove('pre-animate');
|
||||
|
||||
QrCreator.render({
|
||||
text: `${DATA_PREFIX}${authQrCode.token}`,
|
||||
radius: 0.5,
|
||||
ecLevel: 'M',
|
||||
fill: '#4E96D4',
|
||||
size: 280,
|
||||
}, container);
|
||||
}, [connectionState, authQrCode]);
|
||||
const data = `${DATA_PREFIX}${authQrCode.token}`;
|
||||
|
||||
QR_CODE.update({
|
||||
data,
|
||||
});
|
||||
|
||||
if (!isQrMounted) {
|
||||
QR_CODE.append(container);
|
||||
markQrMounted();
|
||||
}
|
||||
return undefined;
|
||||
}, [connectionState, authQrCode, isQrMounted, markQrMounted, unmarkQrMounted]);
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionState === 'connectionStateReady') {
|
||||
@ -85,7 +132,25 @@ const AuthCode: FC<StateProps> = ({
|
||||
<div id="auth-qr-form" className="custom-scroll">
|
||||
<div className="auth-form qr">
|
||||
{authQrCode ? (
|
||||
<div key="qr-container" className="qr-container pre-animate" ref={qrCodeRef} />
|
||||
<div className="qr-wrapper pre-animate" key="qr-wrapper">
|
||||
<div
|
||||
key="qr-container"
|
||||
className="qr-container"
|
||||
ref={qrCodeRef}
|
||||
style={`width: ${QR_SIZE}px; height: ${QR_SIZE}px`}
|
||||
/>
|
||||
{animationData && (
|
||||
<AnimatedSticker
|
||||
id="qrPlane"
|
||||
className="qr-plane"
|
||||
size={QR_PLANE_SIZE}
|
||||
animationData={animationData}
|
||||
play={isAnimationLoaded}
|
||||
onLoad={handleAnimationLoad}
|
||||
key="qrPlane"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div key="qr-loading" className="qr-loading"><Loading /></div>
|
||||
)}
|
||||
|
@ -27,6 +27,8 @@ import Cumshot from '../../../assets/tgs/animatedEmojis/Cumshot.tgs';
|
||||
import JoinRequest from '../../../assets/tgs/invites/Requests.tgs';
|
||||
import Invite from '../../../assets/tgs/invites/Invite.tgs';
|
||||
|
||||
import QrPlane from '../../../assets/tgs/auth/QrPlane.tgs';
|
||||
|
||||
export const ANIMATED_STICKERS_PATHS = {
|
||||
MonkeyIdle,
|
||||
MonkeyTracking,
|
||||
@ -48,6 +50,7 @@ export const ANIMATED_STICKERS_PATHS = {
|
||||
Cumshot,
|
||||
JoinRequest,
|
||||
Invite,
|
||||
QrPlane,
|
||||
};
|
||||
|
||||
export default function getAnimationData(name: keyof typeof ANIMATED_STICKERS_PATHS) {
|
||||
|
@ -165,6 +165,8 @@ $color-message-reaction-own-hover: #b5e0a4;
|
||||
--color-skeleton-background: rgba(33, 33, 33, 0.15);
|
||||
--color-skeleton-foreground: rgba(232, 232, 232, 0.2);
|
||||
|
||||
--color-telegram-blue: #{$color-primary};
|
||||
|
||||
--vh: 1vh;
|
||||
|
||||
--border-radius-default: 0.75rem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user