Auto-detect theme
Sign with QR by default
This commit is contained in:
parent
635b2382e7
commit
b66d85ed37
@ -83,7 +83,7 @@ export default class Chat extends EventListenerBase<{
|
||||
}
|
||||
|
||||
public setBackground(url: string): Promise<void> {
|
||||
const theme = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme);
|
||||
const theme = rootScope.getTheme();
|
||||
|
||||
let item: HTMLElement;
|
||||
if(theme.background.type === 'color' && document.documentElement.style.cursor === 'grabbing') {
|
||||
|
@ -101,7 +101,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
|
||||
const themeCheckboxField = new CheckboxField({
|
||||
toggle: true,
|
||||
checked: rootScope.settings.theme === 'night'
|
||||
checked: rootScope.getTheme().name === 'night'
|
||||
});
|
||||
themeCheckboxField.input.addEventListener('change', () => {
|
||||
rootScope.settings.theme = themeCheckboxField.input.checked ? 'night' : 'day';
|
||||
@ -109,6 +109,10 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
appImManager.applyCurrentTheme();
|
||||
});
|
||||
|
||||
rootScope.on('theme_change', () => {
|
||||
themeCheckboxField.setValueSilently(rootScope.getTheme().name === 'night');
|
||||
});
|
||||
|
||||
const menuButtons: (ButtonMenuItemOptions & {verify?: () => boolean})[] = [{
|
||||
icon: 'saved',
|
||||
text: 'SavedMessages',
|
||||
|
@ -42,7 +42,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
||||
this.container.classList.add('background-container', 'background-image-container');
|
||||
this.setTitle('ChatBackground');
|
||||
|
||||
this.theme = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme);
|
||||
this.theme = rootScope.getTheme();
|
||||
|
||||
{
|
||||
const container = generateSection(this.scrollable);
|
||||
@ -347,7 +347,7 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
||||
|
||||
private setActive = () => {
|
||||
const active = this.grid.querySelector('.active');
|
||||
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background;
|
||||
const background = this.theme.background;
|
||||
const target = background.type === 'image' ? this.grid.querySelector(`.grid-item[data-slug="${background.slug}"]`) : null;
|
||||
if(active === target) {
|
||||
return;
|
||||
|
@ -5,7 +5,7 @@ import findUpClassName from "../../../helpers/dom/findUpClassName";
|
||||
import highlightningColor from "../../../helpers/highlightningColor";
|
||||
import { throttle } from "../../../helpers/schedulers";
|
||||
import appImManager from "../../../lib/appManagers/appImManager";
|
||||
import appStateManager from "../../../lib/appManagers/appStateManager";
|
||||
import appStateManager, { Theme } from "../../../lib/appManagers/appStateManager";
|
||||
import rootScope from "../../../lib/rootScope";
|
||||
import ColorPicker, { ColorPickerColor } from "../../colorPicker";
|
||||
import { SliderSuperTab } from "../../slider";
|
||||
@ -14,11 +14,14 @@ export default class AppBackgroundColorTab extends SliderSuperTab {
|
||||
private colorPicker: ColorPicker;
|
||||
private grid: HTMLElement;
|
||||
private applyColor: (hex: string, updateColorPicker?: boolean) => void;
|
||||
private theme: Theme;
|
||||
|
||||
init() {
|
||||
this.container.classList.add('background-container', 'background-color-container');
|
||||
this.setTitle('SetColor');
|
||||
|
||||
this.theme = rootScope.getTheme();
|
||||
|
||||
const section = new SettingSection({});
|
||||
this.colorPicker = new ColorPicker();
|
||||
|
||||
@ -79,7 +82,7 @@ export default class AppBackgroundColorTab extends SliderSuperTab {
|
||||
|
||||
private setActive() {
|
||||
const active = this.grid.querySelector('.active');
|
||||
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background;
|
||||
const background = this.theme.background;
|
||||
const target = background.type === 'color' ? this.grid.querySelector(`.grid-item[data-color="${background.color}"]`) : null;
|
||||
if(active === target) {
|
||||
return;
|
||||
@ -99,7 +102,7 @@ export default class AppBackgroundColorTab extends SliderSuperTab {
|
||||
this.colorPicker.setColor(hex);
|
||||
} else {
|
||||
const rgba = hexaToRgba(hex);
|
||||
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background;
|
||||
const background = this.theme.background;
|
||||
const hsla = highlightningColor(rgba);
|
||||
|
||||
background.color = hex.toLowerCase();
|
||||
@ -118,7 +121,7 @@ export default class AppBackgroundColorTab extends SliderSuperTab {
|
||||
|
||||
onOpen() {
|
||||
setTimeout(() => {
|
||||
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background;
|
||||
const background = this.theme.background;
|
||||
|
||||
// * set active if type is color
|
||||
if(background.type === 'color') {
|
||||
|
@ -21,6 +21,7 @@
|
||||
<meta name="msapplication-TileImage" content="/assets/img/mstile-144x144.png?v=jw3mK7G9Ry">
|
||||
<meta name="msapplication-config" content="/assets/img/browserconfig.xml?v=jw3mK7G9Ry">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
{{# each htmlWebpackPlugin.files.css }}
|
||||
<link rel="stylesheet" href="{{ this }}">
|
||||
{{/ each }}
|
||||
|
@ -257,6 +257,8 @@ console.timeEnd('get storage1'); */
|
||||
//console.log('got auth:', auth);
|
||||
//console.timeEnd('get storage');
|
||||
|
||||
rootScope.default.setThemeListener();
|
||||
|
||||
if(langPack.appVersion !== App.langPackVersion) {
|
||||
I18n.default.getLangPack(langPack.lang_code);
|
||||
}
|
||||
@ -286,6 +288,9 @@ console.timeEnd('get storage1'); */
|
||||
case 'authStateSignIn':
|
||||
(await import('./pages/pageSignIn')).default.mount();
|
||||
break;
|
||||
case 'authStateSignQr':
|
||||
(await import('./pages/pageSignQR')).default.mount();
|
||||
break;
|
||||
case 'authStateAuthCode':
|
||||
(await import('./pages/pageAuthCode')).default.mount(authState.sentCode);
|
||||
break;
|
||||
|
@ -38,7 +38,7 @@ import appDraftsManager from './appDraftsManager';
|
||||
import serverTimeManager from '../mtproto/serverTimeManager';
|
||||
import sessionStorage from '../sessionStorage';
|
||||
import appDownloadManager from './appDownloadManager';
|
||||
import appStateManager, { AppStateManager } from './appStateManager';
|
||||
import { AppStateManager } from './appStateManager';
|
||||
import { MOUNT_CLASS_TO } from '../../config/debug';
|
||||
import appNavigationController from '../../components/appNavigationController';
|
||||
import appNotificationsManager from './appNotificationsManager';
|
||||
@ -170,6 +170,10 @@ export class AppImManager {
|
||||
this.saveChatPosition(chat);
|
||||
});
|
||||
|
||||
rootScope.on('theme_change', () => {
|
||||
this.applyCurrentTheme();
|
||||
});
|
||||
|
||||
sessionStorage.get('chatPositions').then((c) => {
|
||||
sessionStorage.setToCache('chatPositions', c || {});
|
||||
});
|
||||
@ -247,7 +251,7 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
public setCurrentBackground(broadcastEvent = false) {
|
||||
const theme = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme);
|
||||
const theme = rootScope.getTheme();
|
||||
|
||||
if(theme.background.type === 'image' || (theme.background.type === 'default' && theme.background.slug)) {
|
||||
const defaultTheme = AppStateManager.STATE_INIT.settings.themes.find(t => t.name === theme.name);
|
||||
@ -327,7 +331,7 @@ export class AppImManager {
|
||||
|
||||
public applyHighlightningColor() {
|
||||
let hsla: string;
|
||||
const theme = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme);
|
||||
const theme = rootScope.getTheme();
|
||||
if(theme.background.highlightningColor) {
|
||||
hsla = theme.background.highlightningColor;
|
||||
document.documentElement.style.setProperty('--message-highlightning-color', hsla);
|
||||
@ -352,7 +356,7 @@ export class AppImManager {
|
||||
public applyCurrentTheme(slug?: string, backgroundUrl?: string, broadcastEvent?: boolean) {
|
||||
this.applyHighlightningColor();
|
||||
|
||||
document.documentElement.classList.toggle('night', rootScope.settings.theme === 'night');
|
||||
rootScope.setTheme();
|
||||
|
||||
if(backgroundUrl) {
|
||||
this.backgroundPromises[slug] = Promise.resolve(backgroundUrl);
|
||||
|
@ -4222,7 +4222,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
};
|
||||
|
||||
private setDialogToStateIfMessageIsTop(message: any) {
|
||||
public setDialogToStateIfMessageIsTop(message: any) {
|
||||
const dialog = this.getDialogOnly(message.peerId);
|
||||
if(dialog && dialog.top_message === message.mid) {
|
||||
this.dialogsStorage.setDialogToState(dialog);
|
||||
|
@ -32,7 +32,7 @@ export type Background = {
|
||||
};
|
||||
|
||||
export type Theme = {
|
||||
name: 'day' | 'night',
|
||||
name: 'day' | 'night' | 'system',
|
||||
background: Background
|
||||
};
|
||||
|
||||
@ -96,7 +96,7 @@ export const STATE_INIT: State = {
|
||||
recentSearch: [],
|
||||
version: STATE_VERSION,
|
||||
authState: {
|
||||
_: 'authStateSignIn'
|
||||
_: 'authStateSignQr'
|
||||
},
|
||||
hiddenPinnedMessages: {},
|
||||
settings: {
|
||||
@ -134,7 +134,7 @@ export const STATE_INIT: State = {
|
||||
highlightningColor: 'hsla(0, 0%, 3.82353%, 0.4)'
|
||||
}
|
||||
}],
|
||||
theme: 'day',
|
||||
theme: 'system',
|
||||
notifications: {
|
||||
sound: false
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import type { ConnectionStatusChange } from "../types";
|
||||
import type { UserTyping } from "./appManagers/appChatsManager";
|
||||
import type Chat from "../components/chat/chat";
|
||||
import type { UserAuth } from "./mtproto/mtproto_config";
|
||||
import type { State } from "./appManagers/appStateManager";
|
||||
import type { State, Theme } from "./appManagers/appStateManager";
|
||||
import type { MyDraftMessage } from "./appManagers/appDraftsManager";
|
||||
import EventListenerBase from "../helpers/eventListenerBase";
|
||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
@ -109,6 +109,8 @@ export type BroadcastEvents = {
|
||||
'notify_peer_type_settings': {key: Exclude<NotifyPeer['_'], 'notifyPeer'>, settings: PeerNotifySettings},
|
||||
|
||||
'language_change': void,
|
||||
|
||||
'theme_change': void,
|
||||
};
|
||||
|
||||
export class RootScope extends EventListenerBase<{
|
||||
@ -124,6 +126,7 @@ export class RootScope extends EventListenerBase<{
|
||||
public connectionStatus: {[name: string]: ConnectionStatusChange} = {};
|
||||
public settings: State['settings'];
|
||||
public peerId = 0;
|
||||
public systemTheme: Theme['name'];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -142,6 +145,27 @@ export class RootScope extends EventListenerBase<{
|
||||
});
|
||||
}
|
||||
|
||||
public setThemeListener() {
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const checkDarkMode = () => {
|
||||
//const theme = this.getTheme();
|
||||
this.systemTheme = darkModeMediaQuery.matches ? 'night' : 'day';
|
||||
//const newTheme = this.getTheme();
|
||||
|
||||
if(this.myId) {
|
||||
this.broadcast('theme_change');
|
||||
} else {
|
||||
this.setTheme();
|
||||
}
|
||||
};
|
||||
darkModeMediaQuery.addEventListener('change', checkDarkMode);
|
||||
checkDarkMode();
|
||||
}
|
||||
|
||||
public setTheme() {
|
||||
document.documentElement.classList.toggle('night', this.getTheme().name === 'night');
|
||||
}
|
||||
|
||||
get overlayIsActive() {
|
||||
return this._overlayIsActive;
|
||||
}
|
||||
@ -151,6 +175,10 @@ export class RootScope extends EventListenerBase<{
|
||||
this.broadcast('overlay_toggle', value);
|
||||
}
|
||||
|
||||
public getTheme(name: Theme['name'] = this.settings.theme === 'system' ? this.systemTheme : this.settings.theme) {
|
||||
return this.settings.themes.find(t => t.name === name);
|
||||
}
|
||||
|
||||
public broadcast = <T extends keyof BroadcastEvents>(name: T, detail?: BroadcastEvents[T]) => {
|
||||
/* //if(DEBUG) {
|
||||
if(name !== 'user_update') {
|
||||
|
@ -1,4 +1,3 @@
|
||||
//import apiManager from '../lib/mtproto/apiManager';
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
@ -17,11 +16,16 @@ import { pause } from '../helpers/schedulers';
|
||||
import App from '../config/app';
|
||||
import Button from '../components/button';
|
||||
import { _i18n, i18n, LangPackKey } from '../lib/langPack';
|
||||
import appStateManager from '../lib/appManagers/appStateManager';
|
||||
import rootScope from '../lib/rootScope';
|
||||
import { putPreloader } from '../components/misc';
|
||||
|
||||
let onFirstMount = async() => {
|
||||
const pageElement = page.pageEl;
|
||||
const imageDiv = pageElement.querySelector('.auth-image') as HTMLDivElement;
|
||||
|
||||
let preloader = putPreloader(imageDiv, true);
|
||||
|
||||
const inputWrapper = document.createElement('div');
|
||||
inputWrapper.classList.add('input-wrapper');
|
||||
|
||||
@ -54,10 +58,10 @@ let onFirstMount = async() => {
|
||||
const QRCodeStyling = results[0].default;
|
||||
|
||||
let stop = false;
|
||||
document.addEventListener('user_auth', () => {
|
||||
rootScope.addEventListener('user_auth', () => {
|
||||
stop = true;
|
||||
cachedPromise = null;
|
||||
}, {once: true});
|
||||
}, true);
|
||||
|
||||
let options: {dcId?: number, ignoreErrors: true} = {ignoreErrors: true};
|
||||
let prevToken: Uint8Array | number[];
|
||||
@ -99,20 +103,46 @@ let onFirstMount = async() => {
|
||||
let encoded = bytesToBase64(loginToken.token);
|
||||
let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
|
||||
|
||||
const style = window.getComputedStyle(document.documentElement);
|
||||
const surfaceColor = style.getPropertyValue('--surface-color').trim();
|
||||
const textColor = style.getPropertyValue('--primary-text-color').trim();
|
||||
const primaryColor = style.getPropertyValue('--primary-color').trim();
|
||||
|
||||
const logoUrl = await fetch('assets/img/logo_padded.svg')
|
||||
.then(res => res.text())
|
||||
.then(text => {
|
||||
text = text.replace(/(fill:).+?(;)/, `$1${primaryColor}$2`);
|
||||
const blob = new Blob([text], {type: 'image/svg+xml;charset=utf-8'});
|
||||
|
||||
// * because iOS Safari doesn't want to eat objectURL
|
||||
return new Promise<string>((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
resolve(e.target.result as string);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
//return URL.createObjectURL(blob);
|
||||
});
|
||||
|
||||
const qrCode = new QRCodeStyling({
|
||||
width: 240 * window.devicePixelRatio,
|
||||
height: 240 * window.devicePixelRatio,
|
||||
data: url,
|
||||
image: "assets/img/logo_padded.svg",
|
||||
image: logoUrl,
|
||||
dotsOptions: {
|
||||
color: "#000000",
|
||||
type: "rounded"
|
||||
color: textColor,
|
||||
type: 'rounded'
|
||||
},
|
||||
cornersSquareOptions: {
|
||||
type: 'extra-rounded'
|
||||
},
|
||||
imageOptions: {
|
||||
imageSize: .75
|
||||
imageSize: 1,
|
||||
margin: 0
|
||||
},
|
||||
backgroundOptions: {
|
||||
color: "#ffffff"
|
||||
color: surfaceColor
|
||||
},
|
||||
qrOptions: {
|
||||
errorCorrectionLevel: "L"
|
||||
@ -138,9 +168,25 @@ let onFirstMount = async() => {
|
||||
|
||||
// * это костыль, но библиотека не предоставляет никаких событий
|
||||
await promise.then(() => {
|
||||
Array.from(imageDiv.children).slice(0, -1).forEach(el => {
|
||||
el.remove();
|
||||
});
|
||||
if(preloader) {
|
||||
preloader.style.animation = 'hide-icon .4s forwards';
|
||||
|
||||
const c = imageDiv.children[1] as HTMLElement;
|
||||
c.style.display = 'none';
|
||||
c.style.animation = 'grow-icon .4s forwards';
|
||||
setTimeout(() => {
|
||||
c.style.display = '';
|
||||
}, 150);
|
||||
|
||||
setTimeout(() => {
|
||||
c.style.animation = '';
|
||||
}, 500);
|
||||
preloader = undefined;
|
||||
} else {
|
||||
Array.from(imageDiv.children).slice(0, -1).forEach(el => {
|
||||
el.remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -171,7 +217,7 @@ let onFirstMount = async() => {
|
||||
return false;
|
||||
};
|
||||
|
||||
await iterate(false);
|
||||
//await iterate(false);
|
||||
|
||||
return async() => {
|
||||
stop = false;
|
||||
@ -198,6 +244,8 @@ const page = new Page('page-signQR', true, () => {
|
||||
cachedPromise.then(func => {
|
||||
func();
|
||||
});
|
||||
|
||||
appStateManager.pushToState('authState', {_: 'authStateSignQr'});
|
||||
});
|
||||
|
||||
export default page;
|
||||
|
@ -117,6 +117,7 @@
|
||||
&:-webkit-autofill:active {
|
||||
font-family: "Roboto", -apple-system, apple color emoji, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important;
|
||||
font-size: 1rem !important;
|
||||
color: var(--primary-text-color) !important;
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
@ -428,7 +429,7 @@ input:focus, button:focus {
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
color: #000;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,9 @@
|
||||
*/
|
||||
|
||||
#auth-pages {
|
||||
max-width: 720px; // 360 + 360 / 2
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
background: var(--surface-color);
|
||||
|
||||
.btn-primary {
|
||||
text-transform: uppercase;
|
||||
@ -42,6 +43,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
max-width: 720px; // 360 + 360 / 2
|
||||
margin: 0 auto;
|
||||
|
||||
.auth-placeholder {
|
||||
flex: 1;
|
||||
@ -73,7 +76,7 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
background: var(--surface-color);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@ -167,14 +170,29 @@
|
||||
}
|
||||
|
||||
.page-signQR {
|
||||
overflow: unset !important;
|
||||
|
||||
.auth-image {
|
||||
width: 240px !important;
|
||||
height: 240px !important;
|
||||
overflow: hidden;
|
||||
//overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.preloader {
|
||||
transform: none;
|
||||
left: unset;
|
||||
top: unset;
|
||||
}
|
||||
|
||||
.qr-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
& + .qr-canvas {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,6 +219,14 @@ html.night {
|
||||
// * Night theme end
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
|
||||
}
|
||||
|
||||
@import "partials/ico";
|
||||
@import "partials/input";
|
||||
@import "partials/button";
|
||||
@ -671,7 +679,7 @@ hr {
|
||||
top: calc(100% + .5rem);
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
background-color: var(--surface-color);
|
||||
z-index: 3;
|
||||
border-radius: $border-radius-medium;
|
||||
display: flex;
|
||||
|
6
src/types.d.ts
vendored
6
src/types.d.ts
vendored
@ -42,12 +42,16 @@ export type AnyFunction = (...args: any) => any;
|
||||
export type AnyToVoidFunction = (...args: any) => void;
|
||||
export type NoneToVoidFunction = () => void;
|
||||
|
||||
export type AuthState = AuthState.signIn | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
|
||||
export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
|
||||
export namespace AuthState {
|
||||
export type signIn = {
|
||||
_: 'authStateSignIn'
|
||||
};
|
||||
|
||||
export type signQr = {
|
||||
_: 'authStateSignQr'
|
||||
};
|
||||
|
||||
export type authCode = {
|
||||
_: 'authStateAuthCode',
|
||||
sentCode: AuthSentCode.authSentCode
|
||||
|
Loading…
Reference in New Issue
Block a user