/* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { BsGoogle } from 'react-icons/bs'; import { FaFillDrip, FaPalette } from 'react-icons/fa'; import { ClientUtils, addStyleSheet, addStyleSheetRule } from '../../ClientUtils'; import { Doc, Opt } from '../../fields/Doc'; import { DashVersion } from '../../fields/DocSymbols'; import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { GestureOverlay } from '../views/GestureOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { SnappingManager } from './SnappingManager'; import { undoable } from './UndoManager'; export enum ColorScheme { Dark = 'Dark', Light = 'Light', Custom = 'Custom', CoolBlue = 'CoolBlue', Cupcake = 'Cupcake', } export enum freeformScrollMode { Pan = 'pan', Zoom = 'zoom', } @observer export class SettingsManager extends React.Component<{}> { // eslint-disable-next-line no-use-before-define public static Instance: SettingsManager; static _settingsStyle = addStyleSheet(); @observable private _passwordResultText = ''; @observable private _playgroundMode = false; @observable private _curr_password = ''; @observable private _new_password = ''; @observable private _new_confirm = ''; @observable private _lastPressedSidebarBtn: Opt = undefined; // bcz: this is a hack to handle highlighting buttons in the leftpanel menu .. need to find a cleaner approach @observable private _activeTab = 'Accounts'; @observable private _isOpen = false; @observable public propertiesWidth: number = 0; private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)); public closeMgr = action(() => { this._isOpen = false; }); public openMgr = action(() => { this._isOpen = true; }); private matchSystem = undoable(() => { if (Doc.UserDoc().userThemeSystem) { if (window.matchMedia('(prefers-color-scheme: dark)').matches) this.changeColorScheme(ColorScheme.Dark); if (window.matchMedia('(prefers-color-scheme: light)').matches) this.changeColorScheme(ColorScheme.Light); } }, 'match system theme'); private setFreeformScrollMode = undoable((mode: string) => { Doc.UserDoc().freeformScrollMode = mode; }, 'set scroll mode'); private selectUserMode = undoable((mode: string) => { Doc.noviceMode = mode === 'Novice'; }, 'change user mode'); private changeFontFamily = undoable((font: string) => { Doc.UserDoc().fontFamily = font; }, 'change font family'); private switchUserBackgroundColor = undoable((color: string) => { Doc.UserDoc().userBackgroundColor = color; addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${color} !important` }); }, 'change background color'); private switchUserColor = undoable((color: string) => { Doc.UserDoc().userColor = color; }, 'change user color'); switchUserVariantColor = undoable((color: string) => { Doc.UserDoc().userVariantColor = color; }, 'change variant color'); userThemeSystemToggle = undoable(() => { Doc.UserDoc().userThemeSystem = !Doc.UserDoc().userThemeSystem; this.matchSystem(); }, 'change theme color'); playgroundModeToggle = undoable( action(() => { this._playgroundMode = !this._playgroundMode; if (this._playgroundMode) { DocServer.Control.makeReadOnly(); addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' }); } else ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Control.makeEditable(); }), 'set playgorund mode' ); changeColorScheme = undoable( action((scheme: string) => { Doc.UserDoc().userTheme = scheme; switch (scheme) { case ColorScheme.Light: this.switchUserColor('#323232'); this.switchUserBackgroundColor('#DFDFDF'); this.switchUserVariantColor('#BDDDF5'); break; case ColorScheme.Dark: this.switchUserColor('#DFDFDF'); this.switchUserBackgroundColor('#323232'); this.switchUserVariantColor('#4476F7'); break; case ColorScheme.CoolBlue: this.switchUserColor('#ADEAFF'); this.switchUserBackgroundColor('#060A15'); this.switchUserVariantColor('#3C51FF'); break; case ColorScheme.Cupcake: this.switchUserColor('#3BC7FF'); this.switchUserBackgroundColor('#fffdf7'); this.switchUserVariantColor('#FFD7F3'); break; case ColorScheme.Custom: break; default: } }), 'change color scheme' ); constructor(props: {}) { super(props); makeObservable(this); SettingsManager.Instance = this; this.matchSystem(); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { if (Doc.UserDoc().userThemeSystem) { if (window.matchMedia('(prefers-color-scheme: dark)').matches) this.changeColorScheme(ColorScheme.Dark); if (window.matchMedia('(prefers-color-scheme: light)').matches) this.changeColorScheme(ColorScheme.Light); } // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) }); reaction( () => [SettingsManager.userBackgroundColor, SettingsManager.userColor, SettingsManager.userVariantColor], ([back, user, variant]) => { SnappingManager.userBackgroundColor = back; SnappingManager.userVariantColor = variant; SnappingManager.userColor = user; }, { fireImmediately: true } ); SnappingManager.SettingsStyle = SettingsManager._settingsStyle; } public get LastPressedBtn() { return this._lastPressedSidebarBtn; } // prettier-ignore public set LastPressedBtn(state:Doc|undefined) { this._lastPressedSidebarBtn = state; } // prettier-ignore @computed public static get userColor() { return StrCast(Doc.UserDoc().userColor); } @computed public static get userVariantColor() { return StrCast(Doc.UserDoc().userVariantColor); } @computed public static get userBackgroundColor() { return StrCast(Doc.UserDoc().userBackgroundColor); } @computed get colorsContent() { const schemeMap = Array.from(Object.keys(ColorScheme)); const userTheme = StrCast(Doc.UserDoc().userTheme); return (
{ this.changeColorScheme(scheme as string); Doc.UserDoc().userThemeSystem = false; }} items={Object.keys(ColorScheme).map((scheme, i) => ({ text: schemeMap[i].replace(/([a-z])([A-Z])/, '$1 $2'), val: scheme, }))} dropdownType={DropdownType.SELECT} color={SettingsManager.userColor} fillWidth /> {userTheme === ColorScheme.Custom && ( } selectedColor={SettingsManager.userColor} setSelectedColor={this.switchUserColor} setFinalColor={this.switchUserColor} /> } selectedColor={SettingsManager.userBackgroundColor} setSelectedColor={this.switchUserBackgroundColor} setFinalColor={this.switchUserBackgroundColor} /> } selectedColor={SettingsManager.userVariantColor} setSelectedColor={this.switchUserVariantColor} setFinalColor={this.switchUserVariantColor} /> )}
); } @computed get formatsContent() { return (
{ Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date'; }} toggleStatus={Doc.UserDoc().layout_showTitle !== undefined} size={Size.XSMALL} color={SettingsManager.userColor} /> { Doc.UserDoc().documentLinksButton_fullMenu = !Doc.UserDoc().documentLinksButton_fullMenu; }} toggleStatus={BoolCast(Doc.UserDoc().documentLinksButton_fullMenu)} size={Size.XSMALL} color={SettingsManager.userColor} /> { FontIconBox.ShowIconLabels = !FontIconBox.ShowIconLabels; }} toggleStatus={FontIconBox.ShowIconLabels} size={Size.XSMALL} color={SettingsManager.userColor} /> { GestureOverlay.RecognizeGestures = !GestureOverlay.RecognizeGestures; }} toggleStatus={GestureOverlay.RecognizeGestures} size={Size.XSMALL} color={SettingsManager.userColor} /> { Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels; }} toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} size={Size.XSMALL} color={SettingsManager.userColor} /> { Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox; }} toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)} size={Size.XSMALL} color={SettingsManager.userColor} /> { Doc.UserDoc().showLinkLines = !Doc.UserDoc().showLinkLines; }} toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)} size={Size.XSMALL} color={SettingsManager.userColor} /> { Doc.UserDoc().headerHeight = val; }} />
); } @computed get appearanceContent() { return (
Colors
{this.colorsContent}
Formats
{this.formatsContent}
); } @computed get textContent() { const fontFamilies = ['Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text', 'Roboto']; return (
Text
{/* */} { Doc.UserDoc().fontSize = val + 'px'; }} /> ({ text: val, val: val, style: { fontFamily: val, }, }))} closeOnSelect dropdownType={DropdownType.SELECT} type={Type.TERT} selectedVal={StrCast(Doc.UserDoc().fontFamily)} setSelectedVal={val => { this.changeFontFamily(val as string); }} color={SettingsManager.userColor} fillWidth />
); } @computed get passwordContent() { return (
this.changeVal(val as string, 'curr')} fillWidth password /> this.changeVal(val as string, 'new')} fillWidth password /> this.changeVal(val as string, 'conf')} fillWidth password /> {!this._passwordResultText ? null :
{this._passwordResultText}
}
); } @computed get accountOthersContent() { return (
); } @computed get accountsContent() { return (
Password
{this.passwordContent}
Others
{this.accountOthersContent}
); } @computed get modesContent() { return (
Modes
{ this.selectUserMode(val as string); }} dropdownType={DropdownType.SELECT} type={Type.TERT} placement="bottom-start" color={SettingsManager.userColor} fillWidth />
Freeform Navigation
this.setFreeformScrollMode(val as string)} dropdownType={DropdownType.SELECT} type={Type.TERT} placement="bottom-start" color={SettingsManager.userColor} />
Permissions
); } private get settingsInterface() { // const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent }, // { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }]; const tabs = [ { title: 'Accounts', ele: this.accountsContent }, { title: 'Modes', ele: this.modesContent }, { title: 'Appearance', ele: this.appearanceContent }, { title: 'Text', ele: this.textContent }, ]; return (
{tabs.map(tab => { const isActive = this._activeTab === tab.title; return (
{ this._activeTab = tab.title; })}> {tab.title}
); })}
{DashVersion}
{ClientUtils.CurrentUserEmail()}
{tabs.map(tab => (
{tab.ele}
))}
); } private changePassword = async () => { if (!(this._curr_password && this._new_password && this._new_confirm)) { runInAction(() => { this._passwordResultText = "Error: Hey, we're missing some fields!"; }); } else { const passwordBundle = { curr_pass: this._curr_password, new_pass: this._new_password, new_confirm: this._new_confirm }; const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle); runInAction(() => { this._passwordResultText = error ? 'Error: ' + error[0].msg + '...' : 'Password successfully updated!'; }); } }; @action changeVal = (value: string, pass: string) => { switch (pass) { case 'curr': this._curr_password = value; break; case 'new': this._new_password = value; break; case 'conf': this._new_confirm = value; break; default: } // prettier-ignore }; render() { return ( ); } }