import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { BoolCast, Cast, StrCast } from '../../fields/Types'; import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; import { MainViewModal } from '../views/MainViewModal'; import { FontIconBox } from '../views/nodes/button/FontIconBox'; import { DragManager } from './DragManager'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { undoBatch } from './UndoManager'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; export enum ColorScheme { Dark = '-Dark', Light = '-Light', System = '-MatchSystem', } export enum freeformScrollMode { Pan = 'pan', Zoom = 'zoom', } @observer export class SettingsManager extends React.Component<{}> { public static Instance: SettingsManager; static _settingsStyle = addStyleSheet(); @observable public isOpen = false; @observable private passwordResultText = ''; @observable private playgroundMode = false; @observable private curr_password = ''; @observable private new_password = ''; @observable private new_confirm = ''; @observable activeTab = 'Accounts'; @observable public static propertiesWidth: number = 0; @observable public static headerBarHeight: number = 0; @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; } @computed get colorScheme() { return Doc.ActiveDashboard?.colorScheme; } constructor(props: {}) { super(props); SettingsManager.Instance = this; } public close = action(() => (this.isOpen = false)); public open = action(() => (this.isOpen = true)); private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)); 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!')); } }; @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice')); @undoBatch changelayout_showTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().layout_showTitle = (e.currentTarget as any).value ? 'title' : undefined)); @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value)); @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value)); @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex))); @undoBatch switchUserColor = action((color: ColorState) => { Doc.SharingDoc().userColor = undefined; Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex); }); @undoBatch playgroundModeToggle = action(() => { this.playgroundMode = !this.playgroundMode; if (this.playgroundMode) { DocServer.Control.makeReadOnly(); addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' }); } else Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable(); }); @undoBatch @action changeColorScheme = action((e: React.ChangeEvent) => { const activeDashboard = Doc.ActiveDashboard; if (!activeDashboard) return; const scheme: ColorScheme = (e.currentTarget as any).value; switch (scheme) { case ColorScheme.Light: activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: '#d3d3d3 !important' }); break; case ColorScheme.Dark: activeDashboard.colorScheme = ColorScheme.Dark; addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: 'black !important' }); break; case ColorScheme.System: default: window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) }); break; } }); @computed get colorsContent() { const colorBox = (func: (color: ColorState) => void) => ( ); const colorFlyout = (
e.stopPropagation()}>
); const userColorFlyout = (
e.stopPropagation()}>
); const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System]; const schemeMap = ['Light', 'Dark', 'Match system']; return (
Background Color
{colorFlyout}
Border/Header Color
{userColorFlyout}
Color Scheme
); } @computed get formatsContent() { return (
(Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().layout_showTitle !== undefined} />
Show doc header
(Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} checked={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} />
Show full toolbar
FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} checked={FontIconBox.GetShowLabels()} />
Show button labels
FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} checked={FontIconBox.GetRecognizeGestures()} />
Recognize ink Gestures
(Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} checked={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} />
Hide Labels In Ink Shapes
(Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} checked={BoolCast(Doc.UserDoc().openInkInLightbox)} />
Open Ink Docs in Lightbox
); } @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']; const fontSizes = ['7px', '8px', '9px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '32px', '48px', '72px']; return (
Default Font
); } @action changeVal = (e: React.ChangeEvent, pass: string) => { const value = (e.target as any).value; switch (pass) { case 'curr': this.curr_password = value; break; case 'new': this.new_password = value; break; case 'conf': this.new_confirm = value; break; } }; @computed get passwordContent() { return (
this.changeVal(e, 'curr')} /> this.changeVal(e, 'new')} /> this.changeVal(e, 'conf')} />
{!this.passwordResultText ? null :
{this.passwordResultText}
} forgot password?
); } @computed get accountOthersContent() { return (
); } @computed get accountsContent() { return (
Password
{this.passwordContent}
Others
{this.accountOthersContent}
); } setFreeformScrollMode = (mode: freeformScrollMode) => { Doc.UserDoc().freeformScrollMode = mode; }; @computed get modesContent() { return (
Modes
Playground Mode
Freeform scrolling
Scrolling pans canvas, shift + scrolling zooms
Scrolling zooms canvas
Permissions
(Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} />
Default access private
); } 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 => (
(this.activeTab = tab.title))}> {tab.title}
))}
{Doc.CurrentUserEmail}
{tabs.map(tab => (
{tab.ele}
))}
); } render() { return ( ); } }