From 1c38deb502bba8336a282ed99ecfa6ff6692141a Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 17 Feb 2021 15:47:34 +1000 Subject: [PATCH 01/18] settings: match designs for already implemented features --- pkg/interface/src/logic/state/local.tsx | 7 +- .../views/apps/notifications/preferences.tsx | 2 - .../settings/components/lib/BackButton.tsx | 11 ++ .../components/lib/BackgroundPicker.tsx | 32 +++-- .../settings/components/lib/BucketList.tsx | 37 ++++-- .../apps/settings/components/lib/CalmPref.tsx | 115 +++++++++++++++++ .../settings/components/lib/DisplayForm.tsx | 38 +++--- .../components/lib/NotificationPref.tsx | 88 +++++++++++++ .../apps/settings/components/lib/S3Form.tsx | 56 ++++---- .../apps/settings/components/lib/Security.tsx | 71 ++++++---- .../apps/settings/components/settings.tsx | 121 +++++++++++++----- .../src/views/apps/settings/settings.tsx | 108 +++++++++++----- .../src/views/components/ColorInput.tsx | 6 +- 13 files changed, 534 insertions(+), 158 deletions(-) create mode 100644 pkg/interface/src/views/apps/settings/components/lib/BackButton.tsx create mode 100644 pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx create mode 100644 pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx diff --git a/pkg/interface/src/logic/state/local.tsx b/pkg/interface/src/logic/state/local.tsx index 64e68a0ca..169582477 100644 --- a/pkg/interface/src/logic/state/local.tsx +++ b/pkg/interface/src/logic/state/local.tsx @@ -6,7 +6,7 @@ import produce from 'immer'; import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress } from "~/types/local-update"; -export interface LocalState extends State { +export interface LocalState { hideAvatars: boolean; hideNicknames: boolean; remoteContentPolicy: RemoteContentPolicy; @@ -23,10 +23,13 @@ export interface LocalState extends State { toggleOmnibox: () => void; set: (fn: (state: LocalState) => void) => void }; + +type LocalStateZus = LocalState & State; + export const selectLocalState = (keys: K[]) => f.pick(keys); -const useLocalState = create(persist((set, get) => ({ +const useLocalState = create(persist((set, get) => ({ dark: false, background: undefined, hideAvatars: false, diff --git a/pkg/interface/src/views/apps/notifications/preferences.tsx b/pkg/interface/src/views/apps/notifications/preferences.tsx index 8d3f900b4..99b889329 100644 --- a/pkg/interface/src/views/apps/notifications/preferences.tsx +++ b/pkg/interface/src/views/apps/notifications/preferences.tsx @@ -31,12 +31,10 @@ export default function NotificationPreferences( mentions: graphConfig.mentions, watchOnSelf: graphConfig.watchOnSelf, dnd, - watching: graphConfig.watching, }; const onSubmit = useCallback( async (values: FormSchema, actions: FormikHelpers) => { - console.log(values); try { let promises: Promise[] = []; if (values.mentions !== graphConfig.mentions) { diff --git a/pkg/interface/src/views/apps/settings/components/lib/BackButton.tsx b/pkg/interface/src/views/apps/settings/components/lib/BackButton.tsx new file mode 100644 index 000000000..0ba81209c --- /dev/null +++ b/pkg/interface/src/views/apps/settings/components/lib/BackButton.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Text } from '@tlon/indigo-react'; + +export function BackButton(props: {}) { + return ( + + {"<- Back to System Preferences"} + + ); +} diff --git a/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx b/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx index 53aed1b3e..1e50e95f8 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Box, + Text, Row, Label, Col, @@ -28,31 +29,38 @@ export function BackgroundPicker({ }) { const rowSpace = { my: 0, alignItems: 'center' }; - const radioProps = { my: 4, mr: 4, name: 'bgType' }; + const colProps = { my: 3, mr: 4, gapY: 1 }; return ( - + - - {bgType === "url" && ( + + + Set an image background - )} + - - {bgType === "color" && ( - - )} + + + Set a hex-based background + + - + ); } diff --git a/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx b/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx index 5ffdc270a..12a955f40 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useState } from "react"; import { ManagedTextInputField as Input, @@ -11,8 +11,9 @@ import { MenuButton, MenuList, MenuItem, + Row, } from "@tlon/indigo-react"; -import { Formik } from "formik"; +import { Formik, FormikHelpers } from "formik"; import GlobalApi from "~/logic/api/global"; @@ -27,9 +28,12 @@ export function BucketList({ }) { const _buckets = Array.from(buckets); + const [adding, setAdding] = useState(false); + const onSubmit = useCallback( - (values: { newBucket: string }) => { + (values: { newBucket: string }, actions: FormikHelpers) => { api.s3.addBucket(values.newBucket); + actions.resetForm({ values: { newBucket: "" } }); }, [api] ); @@ -68,7 +72,7 @@ export function BucketList({ alignItems="center" borderRadius={1} border={1} - borderColor="washedGray" + borderColor="lightGray" fontSize={1} pl={2} mb={2} @@ -92,10 +96,27 @@ export function BucketList({ )} ))} - - + {adding && ( + + )} + + + + ); diff --git a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx new file mode 100644 index 000000000..c36d70f6d --- /dev/null +++ b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx @@ -0,0 +1,115 @@ +import React, {useCallback} from "react"; +import { + Box, + ManagedToggleSwitchField as Toggle, + Button, + Col, + Text, +} from "@tlon/indigo-react"; +import { Formik, Form, FormikHelpers } from "formik"; +import * as Yup from "yup"; +import { BackButton } from "./BackButton"; +import useLocalState, { selectLocalState } from "~/logic/state/local"; + +interface FormSchema { + hideAvatars: boolean; + hideNicknames: boolean; + imageShown: boolean; + audioShown: boolean; + oembedShown: boolean; +} + +const localSelector = selectLocalState([ + "hideAvatars", + "hideNicknames", + "remoteContentPolicy", + "set", +]); + +export function CalmPrefs(props: {}) { + const { + hideAvatars, + hideNicknames, + remoteContentPolicy, + set: setLocalState, + } = useLocalState(localSelector); + const { + imageShown, + videoShown, + oembedShown, + audioShown, + } = remoteContentPolicy; + + const initialValues: FormSchema = { + hideAvatars, + hideNicknames, + imageShown, + videoShown, + oembedShown, + audioShown, + }; + + const onSubmit = useCallback(async (values: FormSchema, actions: FormikHelpers) => { + setLocalState(state => { + const { hideAvatars, hideNicknames, ...remote } = values; + Object.assign(state.remoteContentPolicy, remote); + state.hideNicknames = hideNicknames; + state.hideAvatars = hideAvatars; + }); + + }, [setLocalState]); + + return ( + +
+ + + + + CalmEngine + + + Modulate various elemednts across Landscape to maximize calmness + + + User-set identity + + + Remote Content + + + + + + + + +
+ ); +} diff --git a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx index 1fcc3a120..e3288a689 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx @@ -3,7 +3,9 @@ import React from 'react'; import { Box, ManagedCheckboxField as Checkbox, - Button + Button, + Col, + Text } from '@tlon/indigo-react'; import { Formik, Form } from 'formik'; import * as Yup from 'yup'; @@ -12,6 +14,7 @@ import GlobalApi from '~/logic/api/global'; import { uxToHex } from '~/logic/lib/util'; import { S3State, BackgroundConfig } from '~/types'; import { BackgroundPicker, BgType } from './BackgroundPicker'; +import { BackButton } from "./BackButton"; import useLocalState, { LocalState } from '~/logic/state/local'; const formSchema = Yup.object().shape({ @@ -81,35 +84,26 @@ export default function DisplayForm(props: DisplayFormProps) { > {props => (
- - - Display Preferences - + + + + + Display Preferences + + + Customize visual interfaces across your Landscape + + - - - - +
)} diff --git a/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx new file mode 100644 index 000000000..cdbfecae1 --- /dev/null +++ b/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx @@ -0,0 +1,88 @@ +import React, { useCallback } from "react"; +import { + Col, + Text, + ManagedToggleSwitchField as Toggle, +} from "@tlon/indigo-react"; +import { Form, FormikHelpers } from "formik"; +import { FormikOnBlur } from "~/views/components/FormikOnBlur"; +import { BackButton } from "./BackButton"; +import GlobalApi from "~/logic/api/global"; +import {NotificationGraphConfig} from "~/types"; + +interface FormSchema { + mentions: boolean; + dnd: boolean; + watchOnSelf: boolean; +} + +export function NotificationPreferences(props: { + api: GlobalApi; + graphConfig: NotificationGraphConfig; + dnd: boolean; +}) { + const { graphConfig, api, dnd } = props; + const initialValues = { + mentions: graphConfig.mentions, + dnd: dnd, + watchOnSelf: graphConfig.watchOnSelf, + }; + + const onSubmit = useCallback(async (values: FormSchema, actions: FormikHelpers) => { + try { + let promises: Promise[] = []; + if (values.mentions !== graphConfig.mentions) { + promises.push(api.hark.setMentions(values.mentions)); + } + if (values.watchOnSelf !== graphConfig.watchOnSelf) { + promises.push(api.hark.setWatchOnSelf(values.watchOnSelf)); + } + if (values.dnd !== dnd && !_.isUndefined(values.dnd)) { + promises.push(api.hark.setDoNotDisturb(values.dnd)) + } + + await Promise.all(promises); + actions.setStatus({ success: null }); + actions.resetForm({ values: initialValues }); + } catch (e) { + console.error(e); + actions.setStatus({ error: e.message }); + } + }, [api]); + + return ( + + + + + Notification Preferences + + + Set notification visibility and default behaviours for groups and + messaging + + + +
+ + + + + + +
+ + ); +} diff --git a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx index 963e2feb1..dcbb2180c 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx @@ -49,7 +49,7 @@ export default function S3Form(props: S3FormProps) { ); return ( <> - + -
- - S3 Credentials - - - - - + + + + + S3 Storage Setup + + + Store credentials for your S3 object storage buckets on your + Urbit ship, and upload media freely to various modules. Learn + more + + + + + + +
- - - S3 Buckets - + + + + S3 Buckets + + + Your 'active' bucket will be the one used when Landscape uploads a + file + + - - Security - - - Log out of this session - - - You will be logged out of your Urbit on this browser. + + + + + Security Preferences + + + Manage sessions, login credentials and Landscape access + + + + + Log out of this session + + + {allSessions + ? "You will be logged out of all browsers that have currently logged into your Urbit." + : "You will be logged out of your Urbit on this browser."} + + setAllSessions((s) => !s)} + > + Log out of all sessions +
-
-
- - Log out of all sessions - - - You will be logged out of all browsers that have currently logged into your Urbit. -
- - -
-
- + + ); } diff --git a/pkg/interface/src/views/apps/settings/components/settings.tsx b/pkg/interface/src/views/apps/settings/components/settings.tsx index c199f8291..218ea0b40 100644 --- a/pkg/interface/src/views/apps/settings/components/settings.tsx +++ b/pkg/interface/src/views/apps/settings/components/settings.tsx @@ -1,37 +1,96 @@ -import React from 'react'; +import React from "react"; -import { Box } from '@tlon/indigo-react'; +import { Row, Icon, Box, Col, Text } from "@tlon/indigo-react"; -import GlobalApi from '~/logic/api/global'; -import { StoreState } from '~/logic/store/type'; -import DisplayForm from './lib/DisplayForm'; -import S3Form from './lib/S3Form'; -import SecuritySettings from './lib/Security'; -import RemoteContentForm from './lib/RemoteContent'; +import GlobalApi from "~/logic/api/global"; +import { StoreState } from "~/logic/store/type"; +import DisplayForm from "./lib/DisplayForm"; +import S3Form from "./lib/S3Form"; +import SecuritySettings from "./lib/Security"; +import RemoteContentForm from "./lib/RemoteContent"; +import { NotificationPreferences } from "./lib/NotificationPref"; +import { CalmPrefs } from "./lib/CalmPref"; +import { Link } from "react-router-dom"; -type ProfileProps = StoreState & { api: GlobalApi; ship: string }; - -export default function Settings({ - api, - s3 -}: ProfileProps) { +export function SettingsItem(props: { + title: string; + description: string; + to: string; +}) { + const { to, title, description } = props; return ( - - - - - - + + + + + {title} + {description} + + + + ); +} + +export default function Settings(props: {}) { + return ( + + + System Preferences + Configure and customize Landscape + + + + + + + + + + + + ); } diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index bfe331ecd..8c0d78e31 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -1,48 +1,94 @@ -import React from "react"; +import React, { ReactNode } from "react"; import { Route, Link, Switch } from "react-router-dom"; -import Helmet from 'react-helmet'; +import Helmet from "react-helmet"; import { Box, Text, Row, Col, Icon, BaseImage } from "@tlon/indigo-react"; import Settings from "./components/settings"; +import { NotificationPreferences } from "./components/lib/NotificationPref"; +import DisplayForm from './components/lib/DisplayForm'; +import S3Form from "./components/lib/S3Form"; import useLocalState from "~/logic/state/local"; +import {CalmPrefs} from "./components/lib/CalmPref"; +import SecuritySettings from "./components/lib/Security"; + +export const Skeleton = (props: { children: ReactNode }) => ( + + + {props.children} + + +); export default function SettingsScreen(props: any) { const { ship, dark } = props; - const hideAvatars = useLocalState(state => state.hideAvatars); + const hideAvatars = useLocalState((state) => state.hideAvatars); return ( <> Landscape - Settings - { - return ( - - - - - - ); - }} - /> + + + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ( + + ); + }} + /> + { + return ; + }} + /> + + ); } diff --git a/pkg/interface/src/views/components/ColorInput.tsx b/pkg/interface/src/views/components/ColorInput.tsx index 0ee279db1..127d6e97e 100644 --- a/pkg/interface/src/views/components/ColorInput.tsx +++ b/pkg/interface/src/views/components/ColorInput.tsx @@ -13,12 +13,13 @@ import { uxToHex, hexToUx } from "~/logic/lib/util"; type ColorInputProps = Parameters[0] & { id: string; - label: string; + label?: string; + placeholder?: string; disabled?: boolean; }; export function ColorInput(props: ColorInputProps) { - const { id, label, caption, disabled, ...rest } = props; + const { id, placeholder, label, caption, disabled, ...rest } = props; const [{ value, onBlur }, meta, { setValue }] = useField(id); const hex = value.replace('#', '').replace("0x","").replace(".", ""); @@ -54,6 +55,7 @@ export function ColorInput(props: ColorInputProps) { value={hex} disabled={disabled || false} borderRight={0} + placeholder={placeholder} /> Date: Wed, 17 Feb 2021 16:48:05 +1000 Subject: [PATCH 02/18] leap: add category blacklist --- pkg/interface/src/logic/lib/omnibox.js | 28 +++++--- pkg/interface/src/logic/state/local.tsx | 6 +- pkg/interface/src/types/local-update.ts | 4 ++ pkg/interface/src/views/apps/launch/app.js | 8 ++- .../settings/components/lib/DisplayForm.tsx | 69 ++++++++++++------- .../settings/components/lib/LeapSettings.tsx | 64 +++++++++++++++++ .../src/views/apps/settings/settings.tsx | 9 +++ .../src/views/components/leap/Omnibox.tsx | 26 +++++-- 8 files changed, 170 insertions(+), 44 deletions(-) create mode 100644 pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index eabb8017e..b775127fe 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -1,7 +1,7 @@ import { cite } from '~/logic/lib/util'; import { isChannelAdmin } from '~/logic/lib/group'; - const indexes = new Map([ +const makeIndexes = () => new Map([ ['ships', []], ['commands', []], ['subscriptions', []], @@ -70,18 +70,30 @@ const appIndex = function (apps) { return applications; }; -const otherIndex = function() { +const otherIndex = function(hide) { const other = []; - other.push(result('My Channels', '/~landscape/home', 'home', null)); - other.push(result('Notifications', '/~notifications', 'inbox', null)); - other.push(result('Profile and Settings', `/~profile/~${window.ship}`, 'profile', null)); + console.log(hide); + const notBanned = (cat) => hide.findIndex(c => c === cat) === -1; + if(notBanned('mychannel')) { + other.push(result('My Channels', '/~landscape/home', 'home', null)); + } + if(notBanned('updates')) { + other.push(result('Notifications', '/~notifications', 'inbox', null)); + } + if(notBanned('profile')) { + other.push(result('Profile and Settings', `/~profile/~${window.ship}`, 'profile', null)); + } + other.push(result('Messages', '/~landscape/messages', 'messages', null)); - other.push(result('Log Out', '/~/logout', 'logout', null)); + if(notBanned('logout')) { + other.push(result('Log Out', '/~/logout', 'logout', null)); + } return other; }; -export default function index(contacts, associations, apps, currentGroup, groups) { +export default function index(contacts, associations, apps, currentGroup, groups, hide) { + const indexes = makeIndexes(); indexes.set('ships', shipIndex(contacts)); // all metadata from all apps is indexed // into subscriptions and landscape @@ -141,7 +153,7 @@ export default function index(contacts, associations, apps, currentGroup, groups indexes.set('subscriptions', subscriptions); indexes.set('groups', landscape); indexes.set('apps', appIndex(apps)); - indexes.set('other', otherIndex()); + indexes.set('other', otherIndex(hide)); return indexes; }; diff --git a/pkg/interface/src/logic/state/local.tsx b/pkg/interface/src/logic/state/local.tsx index 169582477..079fbe6d1 100644 --- a/pkg/interface/src/logic/state/local.tsx +++ b/pkg/interface/src/logic/state/local.tsx @@ -3,7 +3,7 @@ import f from 'lodash/fp'; import create, { State } from 'zustand'; import { persist } from 'zustand/middleware'; import produce from 'immer'; -import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress } from "~/types/local-update"; +import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress, LeapCategories } from "~/types/local-update"; export interface LocalState { @@ -11,10 +11,12 @@ export interface LocalState { hideNicknames: boolean; remoteContentPolicy: RemoteContentPolicy; tutorialProgress: TutorialProgress; + hideGroups: boolean; tutorialRef: HTMLElement | null, hideTutorial: () => void; nextTutStep: () => void; prevTutStep: () => void; + hideLeapCats: LeapCategories[]; setTutorialRef: (el: HTMLElement | null) => void; dark: boolean; background: BackgroundConfig; @@ -34,6 +36,8 @@ const useLocalState = create(persist((set, get) => ({ background: undefined, hideAvatars: false, hideNicknames: false, + hideLeapCats: [], + hideGroups: false, tutorialProgress: 'hidden', tutorialRef: null, setTutorialRef: (el: HTMLElement | null) => set(produce(state => { diff --git a/pkg/interface/src/types/local-update.ts b/pkg/interface/src/types/local-update.ts index 5849931fe..731bd8244 100644 --- a/pkg/interface/src/types/local-update.ts +++ b/pkg/interface/src/types/local-update.ts @@ -1,5 +1,9 @@ export const tutorialProgress = ['hidden', 'start', 'group-desc', 'channels', 'chat', 'link', 'publish', 'profile', 'leap', 'notifications', 'done', 'exit'] as const; +export const leapCategories = ["commands", "mychannel", "messages", "updates", "profile", "logout"] as const; + +export type LeapCategories = typeof leapCategories[number]; + export type TutorialProgress = typeof tutorialProgress[number]; interface LocalUpdateSetDark { setDark: boolean; diff --git a/pkg/interface/src/views/apps/launch/app.js b/pkg/interface/src/views/apps/launch/app.js index 1e0c937e1..887cb5e34 100644 --- a/pkg/interface/src/views/apps/launch/app.js +++ b/pkg/interface/src/views/apps/launch/app.js @@ -38,7 +38,7 @@ const ScrollbarLessBox = styled(Box)` } `; -const tutSelector = f.pick(['tutorialProgress', 'nextTutStep']); +const tutSelector = f.pick(['tutorialProgress', 'nextTutStep', 'hideGroups']); export default function LaunchApp(props) { const history = useHistory(); @@ -82,7 +82,7 @@ export default function LaunchApp(props) { } }, [query]); - const { tutorialProgress, nextTutStep } = useLocalState(tutSelector); + const { tutorialProgress, nextTutStep, hideGroups } = useLocalState(tutSelector); const waiter = useWaitForProps(props); @@ -198,7 +198,9 @@ export default function LaunchApp(props) { > - + {!hideGroups && + () + } {hashBox} diff --git a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx index e3288a689..0c6924a64 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx @@ -1,30 +1,32 @@ -import React from 'react'; +import React from "react"; import { Box, ManagedCheckboxField as Checkbox, Button, Col, - Text -} from '@tlon/indigo-react'; -import { Formik, Form } from 'formik'; -import * as Yup from 'yup'; + Text, + ManagedToggleSwitchField as Toggle, +} from "@tlon/indigo-react"; +import { Formik, Form } from "formik"; +import * as Yup from "yup"; -import GlobalApi from '~/logic/api/global'; -import { uxToHex } from '~/logic/lib/util'; -import { S3State, BackgroundConfig } from '~/types'; -import { BackgroundPicker, BgType } from './BackgroundPicker'; +import GlobalApi from "~/logic/api/global"; +import { uxToHex } from "~/logic/lib/util"; +import { S3State, BackgroundConfig } from "~/types"; +import { BackgroundPicker, BgType } from "./BackgroundPicker"; import { BackButton } from "./BackButton"; -import useLocalState, { LocalState } from '~/logic/state/local'; +import useLocalState, { LocalState } from "~/logic/state/local"; const formSchema = Yup.object().shape({ bgType: Yup.string() - .oneOf(['none', 'color', 'url'], 'invalid') - .required('Required'), + .oneOf(["none", "color", "url"], "invalid") + .required("Required"), bgUrl: Yup.string().url(), bgColor: Yup.string(), avatars: Yup.boolean(), - nicknames: Yup.boolean() + nicknames: Yup.boolean(), + hideGroups: Yup.boolean(), }); interface FormSchema { @@ -33,6 +35,7 @@ interface FormSchema { bgUrl: string | undefined; avatars: boolean; nicknames: boolean; + hideGroups: boolean; } interface DisplayFormProps { @@ -43,16 +46,22 @@ interface DisplayFormProps { export default function DisplayForm(props: DisplayFormProps) { const { api, s3 } = props; - const { hideAvatars, hideNicknames, background, set: setLocalState } = useLocalState(); + const { + hideAvatars, + hideNicknames, + background, + hideGroups, + set: setLocalState, + } = useLocalState(); let bgColor, bgUrl; - if (background?.type === 'url') { + if (background?.type === "url") { bgUrl = background.url; } - if (background?.type === 'color') { + if (background?.type === "color") { bgColor = background.color; } - const bgType = background?.type || 'none'; + const bgType = background?.type || "none"; return ( { const bgConfig: BackgroundConfig = - values.bgType === 'color' - ? { type: 'color', color: `#${uxToHex(values.bgColor || '0x0')}` } - : values.bgType === 'url' - ? { type: 'url', url: values.bgUrl || '' } + values.bgType === "color" + ? { type: "color", color: `#${uxToHex(values.bgColor || "0x0")}` } + : values.bgType === "url" + ? { type: "url", url: values.bgUrl || "" } : undefined; setLocalState((state: LocalState) => { state.background = bgConfig; state.hideAvatars = values.avatars; state.hideNicknames = values.nicknames; + state.hideGroups = values.hideGroups; + }); actions.setSubmitting(false); }} > - {props => ( + {(props) => (
- + Display Preferences - + Customize visual interfaces across your Landscape @@ -100,6 +112,11 @@ export default function DisplayForm(props: DisplayFormProps) { api={api} s3={s3} /> + diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx new file mode 100644 index 000000000..aaa87dc42 --- /dev/null +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -0,0 +1,64 @@ +import React, { useCallback } from "react"; +import _ from 'lodash'; +import { + Col, + Text, + ManagedToggleSwitchField as Toggle, + ManagedCheckboxField, +} from "@tlon/indigo-react"; +import { Form, FormikHelpers } from "formik"; +import { FormikOnBlur } from "~/views/components/FormikOnBlur"; +import { BackButton } from "./BackButton"; +import GlobalApi from "~/logic/api/global"; +import { NotificationGraphConfig, LeapCategories, leapCategories } from "~/types"; +import useLocalState, {selectLocalState} from "~/logic/state/local"; + +type FormSchema = { + [c in LeapCategories]: boolean; +} + +const localSelector = selectLocalState(["hideLeapCats", "set"]); + +export function LeapSettings(props: {}) { + + const { hideLeapCats, set: setLocalState } = useLocalState(localSelector); + + const initialValues: FormSchema = leapCategories.reduce((acc, val, key) => { + return {...acc, [val]: hideLeapCats.findIndex(c => c === val) !== -1 }; + }, {} as FormSchema); + + const onSubmit = useCallback((values: FormSchema) => { + setLocalState(state => { + state.hideLeapCats = _.keys(_.pickBy(values, v => v)) as any; + }); + + }, [setLocalState]); + + return ( + + + + + Leap + + + Customize Leap ordering, omit modules or results + + + + + + + Customize default Leap sections + + + + + + + + + + + ); +} diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index 8c0d78e31..dde98baec 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -11,6 +11,7 @@ import S3Form from "./components/lib/S3Form"; import useLocalState from "~/logic/state/local"; import {CalmPrefs} from "./components/lib/CalmPref"; import SecuritySettings from "./components/lib/Security"; +import {LeapSettings} from "./components/lib/LeapSettings"; export const Skeleton = (props: { children: ReactNode }) => ( @@ -38,6 +39,14 @@ export default function SettingsScreen(props: any) { + { + return ( + + ); + }} + /> { diff --git a/pkg/interface/src/views/components/leap/Omnibox.tsx b/pkg/interface/src/views/components/leap/Omnibox.tsx index 24385f613..a5a2b46f4 100644 --- a/pkg/interface/src/views/components/leap/Omnibox.tsx +++ b/pkg/interface/src/views/components/leap/Omnibox.tsx @@ -6,7 +6,7 @@ import makeIndex from '~/logic/lib/omnibox'; import Mousetrap from 'mousetrap'; import OmniboxInput from './OmniboxInput'; import OmniboxResult from './OmniboxResult'; -import { withLocalState } from '~/logic/state/local'; +import useLocalState, { withLocalState, selectLocalState } from '~/logic/state/local'; import { deSig } from '~/logic/lib/util'; import defaultApps from '~/logic/lib/default-apps'; @@ -28,10 +28,12 @@ interface OmniboxProps { } const SEARCHED_CATEGORIES = ['ships', 'other', 'commands', 'groups', 'subscriptions', 'apps']; +const localSelector = selectLocalState(["hideLeapCats"]); export function Omnibox(props: OmniboxProps) { const location = useLocation(); const history = useHistory(); + const { hideLeapCats } = useLocalState(localSelector); const omniboxRef = useRef(null) const inputRef = useRef(null); @@ -45,18 +47,30 @@ export function Omnibox(props: OmniboxProps) { : props.contacts; }, [props.contacts, query]); - const index = useMemo(() => { - const selectedGroup = location.pathname.startsWith('/~landscape/ship/') + const selectedGroup = useMemo( + () => location.pathname.startsWith('/~landscape/ship/') ? '/' + location.pathname.split('/').slice(2,5).join('/') - : null; + : null, + [location.pathname] + ); + + const index = useMemo(() => { return makeIndex( contacts, props.associations, props.tiles, selectedGroup, - props.groups + props.groups, + hideLeapCats, ); - }, [location.pathname, contacts, props.associations, props.groups, props.tiles]); + }, [ + selectedGroup, + hideLeapCats, + contacts, + props.associations, + props.groups, + props.tiles + ]); const onOutsideClick = useCallback(() => { props.show && props.toggle() From 41604e4cd4dc096b1f4f1fbcf4984a5116ada95e Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 22 Feb 2021 15:13:08 +1000 Subject: [PATCH 03/18] settings: hook reducer into zustand --- pkg/interface/src/logic/api/settings.ts | 6 +- .../src/logic/reducers/settings-update.ts | 79 +++++++++++-------- pkg/interface/src/logic/state/settings.tsx | 56 +++++++++++++ pkg/interface/src/logic/store/store.ts | 2 +- pkg/interface/src/views/App.js | 15 ++-- .../settings/components/lib/DisplayForm.tsx | 74 ++++++++--------- 6 files changed, 144 insertions(+), 88 deletions(-) create mode 100644 pkg/interface/src/logic/state/settings.tsx diff --git a/pkg/interface/src/logic/api/settings.ts b/pkg/interface/src/logic/api/settings.ts index cd0cd736f..d612df35f 100644 --- a/pkg/interface/src/logic/api/settings.ts +++ b/pkg/interface/src/logic/api/settings.ts @@ -51,8 +51,10 @@ export default class SettingsApi extends BaseApi { } async getAll() { - const data = await this.scry("settings-store", "/all"); - this.store.handleEvent({data: {"settings-data": data.all}}); + const { all } = await this.scry("settings-store", "/all"); + this.store.handleEvent({data: + {"settings-data": { all } } + }); } async getBucket(bucket: Key) { diff --git a/pkg/interface/src/logic/reducers/settings-update.ts b/pkg/interface/src/logic/reducers/settings-update.ts index 9716cbb85..fac6515e4 100644 --- a/pkg/interface/src/logic/reducers/settings-update.ts +++ b/pkg/interface/src/logic/reducers/settings-update.ts @@ -1,77 +1,86 @@ import _ from 'lodash'; -import { StoreState } from '../../store/type'; -import { - SettingsUpdate, -} from '~/types/settings'; +import { SettingsUpdate } from '~/types/settings'; +import useSettingsState, { SettingsStateZus } from "~/logic/state/settings"; +import produce from 'immer'; +import { unstable_batchedUpdates } from 'react-dom'; -type SettingsState = Pick; +export default class SettingsStateZusettingsReducer{ + reduce(json: any) { -export default class SettingsReducer{ - reduce(json: Cage, state: S) { - let data = json["settings-event"]; - if (data) { - this.putBucket(data, state); - this.delBucket(data, state); - this.putEntry(data, state); - this.delEntry(data, state); - } - data = json["settings-data"]; - if (data) { - this.getAll(data, state); - this.getBucket(data, state); - this.getEntry(data, state); - } + const old = useSettingsState.getState(); + const newState = produce(old, state => { + let data = json["settings-event"]; + if (data) { + console.log(data); + this.putBucket(data, state); + this.delBucket(data, state); + this.putEntry(data, state); + this.delEntry(data, state); + } + data = json["settings-data"]; + if (data) { + console.log(data); + this.getAll(data, state); + this.getBucket(data, state); + this.getEntry(data, state); + } + }); + console.log(newState); + useSettingsState.setState(newState); } - putBucket(json: SettingsUpdate, state: S) { + putBucket(json: SettingsUpdate, state: SettingsStateZus) { const data = _.get(json, 'put-bucket', false); if (data) { - state.settings[data["bucket-key"]] = data.bucket; + state[data["bucket-key"]] = data.bucket; } } - delBucket(json: SettingsUpdate, state: S) { + delBucket(json: SettingsUpdate, state: SettingsStateZus) { const data = _.get(json, 'del-bucket', false); if (data) { delete state.settings[data["bucket-key"]]; } } - putEntry(json: SettingsUpdate, state: S) { + putEntry(json: SettingsUpdate, state: SettingsStateZus) { const data = _.get(json, 'put-entry', false); if (data) { - if (!state.settings[data["bucket-key"]]) { - state.settings[data["bucket-key"]] = {}; + if (!state[data["bucket-key"]]) { + state[data["bucket-key"]] = {}; } - state.settings[data["bucket-key"]][data["entry-key"]] = data.value; + state[data["bucket-key"]][data["entry-key"]] = data.value; } } - delEntry(json: SettingsUpdate, state: S) { + delEntry(json: SettingsUpdate, state: SettingsStateZus) { const data = _.get(json, 'del-entry', false); if (data) { - delete state.settings[data["bucket-key"]][data["entry-key"]]; + delete state[data["bucket-key"]][data["entry-key"]]; } } - getAll(json: any, state: S) { - state.settings = json; + getAll(json: any, state: SettingsStateZus) { + const data = _.get(json, 'all'); + if(data) { + Object.assign(state, json); + } } - getBucket(json: any, state: S) { + getBucket(json: any, state: SettingsStateZus) { const key = _.get(json, 'bucket-key', false); const bucket = _.get(json, 'bucket', false); if (key && bucket) { - state.settings[key] = bucket; + state[key] = bucket; } } - getEntry(json: any, state: S) { + getEntry(json: any, state: SettingsStateZus) { const bucketKey = _.get(json, 'bucket-key', false); const entryKey = _.get(json, 'entry-key', false); const entry = _.get(json, 'entry', false); if (bucketKey && entryKey && entry) { - state.settings[bucketKey][entryKey] = entry; + state[bucketKey][entryKey] = entry; } } } diff --git a/pkg/interface/src/logic/state/settings.tsx b/pkg/interface/src/logic/state/settings.tsx new file mode 100644 index 000000000..bf2bfb6dc --- /dev/null +++ b/pkg/interface/src/logic/state/settings.tsx @@ -0,0 +1,56 @@ +import React, { ReactNode } from "react"; +import f from 'lodash/fp'; +import create, { State } from 'zustand'; +import { persist } from 'zustand/middleware'; +import produce from 'immer'; +import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress, LeapCategories } from "~/types/local-update"; + + +export interface SettingsState { + display: { + backgroundType: 'none' | 'url' | 'color'; + background?: string; + dark: boolean; + }; + calm: { + hideNicknames: boolean; + hideAvatars: boolean; + remoteContentPolicy: RemoteContentPolicy; + } + set: (fn: (state: SettingsState) => void) => void +}; + +export type SettingsStateZus = SettingsState & State; + +export const selectSettingsState = + (keys: K[]) => f.pick(keys); + +const useSettingsState = create((set) => ({ + display: { + backgroundType: 'none', + background: undefined, + dark: false, + }, + calm: { + hideNicknames: false, + hideAvatars: false, + remoteContentPolicy: { + imageShown: true, + oembedShown: true, + audioShown: true, + videoShown: true + } + }, + set: (fn: (state: SettingsState) => void) => set(produce(fn)) +})); + +function withSettingsState(Component: any, stateMemberKeys?: S[]) { + return React.forwardRef((props: Omit, ref) => { + const localState = stateMemberKeys + ? useSettingsState(selectSettingsState(stateMemberKeys)) + : useSettingsState(); + return + }); +} + +export { useSettingsState as default, withSettingsState }; diff --git a/pkg/interface/src/logic/store/store.ts b/pkg/interface/src/logic/store/store.ts index b2ab60c6c..ca512fcb3 100644 --- a/pkg/interface/src/logic/store/store.ts +++ b/pkg/interface/src/logic/store/store.ts @@ -115,7 +115,7 @@ export default class GlobalStore extends BaseStore { GraphReducer(data, this.state); HarkReducer(data, this.state); ContactReducer(data, this.state); - this.settingsReducer.reduce(data, this.state); + this.settingsReducer.reduce(data); GroupViewReducer(data, this.state); } } diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index 3e1650dc3..0ba7a7079 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -28,19 +28,20 @@ import GlobalApi from '~/logic/api/global'; import { uxToHex } from '~/logic/lib/util'; import { foregroundFromBackground } from '~/logic/lib/sigil'; import { withLocalState } from '~/logic/state/local'; +import { withSettingsState } from '~/logic/state/settings'; -const Root = styled.div` +const Root = withSettingsState(styled.div` font-family: ${p => p.theme.fonts.sans}; height: 100%; width: 100%; padding: 0; margin: 0; - ${p => p.background?.type === 'url' ? ` - background-image: url('${p.background?.url}'); + ${p => p.display.backgroundType === 'url' ? ` + background-image: url('${p.display.background}'); background-size: cover; - ` : p.background?.type === 'color' ? ` - background-color: ${p.background.color}; + ` : p.display.backgroundType === 'color' ? ` + background-color: ${p.display.background}; ` : `background-color: ${p.theme.colors.white};` } display: flex; @@ -64,7 +65,7 @@ const Root = styled.div` border-radius: 1rem; border: 0px solid transparent; } -`; +`, ['display']); const StatusBarWithRouter = withRouter(StatusBar); @@ -148,7 +149,7 @@ class App extends React.Component { ? : null} - + diff --git a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx index 0c6924a64..be5c4f3fb 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx @@ -16,26 +16,20 @@ import { uxToHex } from "~/logic/lib/util"; import { S3State, BackgroundConfig } from "~/types"; import { BackgroundPicker, BgType } from "./BackgroundPicker"; import { BackButton } from "./BackButton"; -import useLocalState, { LocalState } from "~/logic/state/local"; +import useSettingsState, { SettingsState, selectSettingsState } from "~/logic/state/settings"; const formSchema = Yup.object().shape({ bgType: Yup.string() .oneOf(["none", "color", "url"], "invalid") .required("Required"), - bgUrl: Yup.string().url(), - bgColor: Yup.string(), - avatars: Yup.boolean(), - nicknames: Yup.boolean(), - hideGroups: Yup.boolean(), + background: Yup.string(), + }); interface FormSchema { bgType: BgType; bgColor: string | undefined; bgUrl: string | undefined; - avatars: boolean; - nicknames: boolean; - hideGroups: boolean; } interface DisplayFormProps { @@ -43,55 +37,54 @@ interface DisplayFormProps { s3: S3State; } +const settingsSel = selectSettingsState(["display"]); + export default function DisplayForm(props: DisplayFormProps) { const { api, s3 } = props; const { - hideAvatars, - hideNicknames, - background, - hideGroups, - set: setLocalState, - } = useLocalState(); + display: { + background, + backgroundType, + } + } = useSettingsState(settingsSel); + + console.log(backgroundType); let bgColor, bgUrl; - if (background?.type === "url") { - bgUrl = background.url; + if (backgroundType === "url") { + bgUrl = background; } - if (background?.type === "color") { - bgColor = background.color; + if (backgroundType === "color") { + bgColor = background; } - const bgType = background?.type || "none"; + const bgType = backgroundType || "none"; return ( { - const bgConfig: BackgroundConfig = - values.bgType === "color" - ? { type: "color", color: `#${uxToHex(values.bgColor || "0x0")}` } + onSubmit={async (values, actions) => { + let promises = [] as Promise[]; + promises.push(api.settings.putEntry('display', 'backgroundType', values.bgType)); + + promises.push( + api.settings.putEntry('display', 'background', + values.bgType === "color" + ? `#${uxToHex(values.bgColor || "0x0")}` : values.bgType === "url" - ? { type: "url", url: values.bgUrl || "" } - : undefined; + ? values.bgUrl || "" + : false + )); - setLocalState((state: LocalState) => { - state.background = bgConfig; - state.hideAvatars = values.avatars; - state.hideNicknames = values.nicknames; - state.hideGroups = values.hideGroups; + await Promise.all(promises); - }); - actions.setSubmitting(false); }} > {(props) => ( @@ -112,11 +105,6 @@ export default function DisplayForm(props: DisplayFormProps) { api={api} s3={s3} /> - From b6fb575ebc6eca4836b9dd39fb824949d19e3c2d Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 22 Feb 2021 15:25:59 +1000 Subject: [PATCH 04/18] settings: CalmEngine on settings-store --- pkg/interface/src/logic/lib/util.ts | 4 +- .../src/logic/reducers/settings-update.ts | 3 +- pkg/interface/src/logic/state/settings.tsx | 20 ++++--- .../views/apps/profile/components/Profile.tsx | 5 +- .../apps/profile/components/ViewProfile.tsx | 8 +-- .../src/views/apps/profile/profile.tsx | 6 +- .../apps/settings/components/lib/CalmPref.tsx | 58 ++++++++++--------- .../settings/components/lib/LeapSettings.tsx | 17 +----- .../apps/settings/components/settings.tsx | 23 ++++---- .../src/views/apps/settings/settings.tsx | 2 +- .../src/views/components/StatusBar.js | 11 ++-- .../landscape/components/Participants.tsx | 6 +- 12 files changed, 77 insertions(+), 86 deletions(-) diff --git a/pkg/interface/src/logic/lib/util.ts b/pkg/interface/src/logic/lib/util.ts index 670c83d43..bf198bcb9 100644 --- a/pkg/interface/src/logic/lib/util.ts +++ b/pkg/interface/src/logic/lib/util.ts @@ -3,7 +3,7 @@ import _ from "lodash"; import f, { memoize } from "lodash/fp"; import bigInt, { BigInteger } from "big-integer"; import { Contact } from '~/types'; -import useLocalState from '../state/local'; +import useSettingsState from '../state/settings'; export const MOBILE_BROWSER_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i; @@ -377,7 +377,7 @@ export function pluralize(text: string, isPlural = false, vowel = false) { // Hide is an optional second parameter for when this function is used in class components export function useShowNickname(contact: Contact | null, hide?: boolean): boolean { - const hideNicknames = typeof hide !== 'undefined' ? hide : useLocalState(state => state.hideNicknames); + const hideNicknames = typeof hide !== 'undefined' ? hide : useSettingsState(state => state.calm.hideNicknames); return !!(contact && contact.nickname && !hideNicknames); } diff --git a/pkg/interface/src/logic/reducers/settings-update.ts b/pkg/interface/src/logic/reducers/settings-update.ts index fac6515e4..2ad475e72 100644 --- a/pkg/interface/src/logic/reducers/settings-update.ts +++ b/pkg/interface/src/logic/reducers/settings-update.ts @@ -2,7 +2,6 @@ import _ from 'lodash'; import { SettingsUpdate } from '~/types/settings'; import useSettingsState, { SettingsStateZus } from "~/logic/state/settings"; import produce from 'immer'; -import { unstable_batchedUpdates } from 'react-dom'; export default class SettingsStateZusettingsReducer{ reduce(json: any) { @@ -63,7 +62,7 @@ export default class SettingsStateZusettingsReducer{ getAll(json: any, state: SettingsStateZus) { const data = _.get(json, 'all'); if(data) { - Object.assign(state, json); + _.merge(state, data); } } diff --git a/pkg/interface/src/logic/state/settings.tsx b/pkg/interface/src/logic/state/settings.tsx index bf2bfb6dc..ca1f47954 100644 --- a/pkg/interface/src/logic/state/settings.tsx +++ b/pkg/interface/src/logic/state/settings.tsx @@ -15,15 +15,17 @@ export interface SettingsState { calm: { hideNicknames: boolean; hideAvatars: boolean; - remoteContentPolicy: RemoteContentPolicy; - } + }; + remoteContentPolicy: RemoteContentPolicy; set: (fn: (state: SettingsState) => void) => void }; export type SettingsStateZus = SettingsState & State; export const selectSettingsState = - (keys: K[]) => f.pick(keys); +(keys: K[]) => f.pick(keys); + +export const selectCalmState = (s: SettingsState) => s.calm; const useSettingsState = create((set) => ({ display: { @@ -34,12 +36,12 @@ const useSettingsState = create((set) => ({ calm: { hideNicknames: false, hideAvatars: false, - remoteContentPolicy: { - imageShown: true, - oembedShown: true, - audioShown: true, - videoShown: true - } + }, + remoteContentPolicy: { + imageShown: true, + oembedShown: true, + audioShown: true, + videoShown: true }, set: (fn: (state: SettingsState) => void) => set(produce(fn)) })); diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 0072b591b..8887d3360 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -18,12 +18,11 @@ import RichText from '~/views/components/RichText' import useLocalState from "~/logic/state/local"; import { useHistory } from "react-router-dom"; import {useTutorialModal} from "~/views/components/useTutorialModal"; +import useSettingsState, {selectCalmState} from "~/logic/state/settings"; export function Profile(props: any) { - const { hideAvatars } = useLocalState(({ hideAvatars }) => ({ - hideAvatars - })); + const { hideAvatars } = useSettingsState(selectCalmState); const history = useHistory(); if (!props.ship) { diff --git a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx index cd639b135..440d35589 100644 --- a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx @@ -18,14 +18,14 @@ import {GroupSummary} from "~/views/landscape/components/GroupSummary"; import {MetadataUpdatePreview} from "~/types"; import {GroupLink} from "~/views/components/GroupLink"; import {lengthOrder} from "~/logic/lib/util"; -import useLocalState from "~/logic/state/local"; +import useSettingsState, {selectSettingsState} from "~/logic/state/settings"; +const settingsSel = selectSettingsState(["calm"]); + export function ViewProfile(props: any) { const history = useHistory(); - const { hideNicknames } = useLocalState(({ hideNicknames }) => ({ - hideNicknames - })); + const { calm: { hideNicknames } } = useSettingsState(settingsSel); const { api, contact, nacked, isPublic, ship, associations, groups } = props; return ( diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index 977a0975e..08e600ea5 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -7,11 +7,13 @@ import { Box, Text, Row, Col, Icon, BaseImage } from "@tlon/indigo-react"; import { uxToHex } from "~/logic/lib/util"; import { Profile } from "./components/Profile"; -import useLocalState from "~/logic/state/local"; +import useSettingsState, {SettingsState} from "~/logic/state/settings"; + +const settingsSel = (s: SettingsState) => s.calm.hideAvatars; export default function ProfileScreen(props: any) { const { dark } = props; - const hideAvatars = useLocalState(state => state.hideAvatars); + const hideAvatars = useSettingsState(settingsSel); return ( <> diff --git a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx index c36d70f6d..d3c72a8e9 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx @@ -9,7 +9,8 @@ import { import { Formik, Form, FormikHelpers } from "formik"; import * as Yup from "yup"; import { BackButton } from "./BackButton"; -import useLocalState, { selectLocalState } from "~/logic/state/local"; +import useSettingsState, {selectSettingsState} from "~/logic/state/settings"; +import GlobalApi from "~/logic/api/global"; interface FormSchema { hideAvatars: boolean; @@ -17,28 +18,28 @@ interface FormSchema { imageShown: boolean; audioShown: boolean; oembedShown: boolean; + videoShown: boolean; } -const localSelector = selectLocalState([ - "hideAvatars", - "hideNicknames", - "remoteContentPolicy", - "set", -]); +const settingsSel = selectSettingsState(["calm", "remoteContentPolicy"]); -export function CalmPrefs(props: {}) { +export function CalmPrefs(props: { + api: GlobalApi; +}) { + const { api } = props; const { - hideAvatars, - hideNicknames, - remoteContentPolicy, - set: setLocalState, - } = useLocalState(localSelector); - const { - imageShown, - videoShown, - oembedShown, - audioShown, - } = remoteContentPolicy; + calm: { + hideAvatars, + hideNicknames, + }, + remoteContentPolicy: { + imageShown, + videoShown, + oembedShown, + audioShown, + } + } = useSettingsState(settingsSel); + const initialValues: FormSchema = { hideAvatars, @@ -49,15 +50,16 @@ export function CalmPrefs(props: {}) { audioShown, }; - const onSubmit = useCallback(async (values: FormSchema, actions: FormikHelpers) => { - setLocalState(state => { - const { hideAvatars, hideNicknames, ...remote } = values; - Object.assign(state.remoteContentPolicy, remote); - state.hideNicknames = hideNicknames; - state.hideAvatars = hideAvatars; - }); - - }, [setLocalState]); + const onSubmit = useCallback(async (v: FormSchema, actions: FormikHelpers) => { + return Promise.all([ + api.settings.putEntry('calm', 'hideAvatars', v.hideAvatars), + api.settings.putEntry('calm', 'hideNicknames', v.hideNicknames), + api.settings.putEntry('remoteContentPolicy', 'imageShown', v.imageShown), + api.settings.putEntry('remoteContentPolicy', 'videoShown', v.videoShown), + api.settings.putEntry('remoteContentPolicy', 'audioShown', v.audioShown), + api.settings.putEntry('remoteContentPolicy', 'oembedShown', v.oembedShown), + ]); + }, [api]); return ( diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx index aaa87dc42..6c5020679 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -13,26 +13,13 @@ import GlobalApi from "~/logic/api/global"; import { NotificationGraphConfig, LeapCategories, leapCategories } from "~/types"; import useLocalState, {selectLocalState} from "~/logic/state/local"; -type FormSchema = { - [c in LeapCategories]: boolean; -} - -const localSelector = selectLocalState(["hideLeapCats", "set"]); export function LeapSettings(props: {}) { - const { hideLeapCats, set: setLocalState } = useLocalState(localSelector); + //TODO: arrays in settings-store? - const initialValues: FormSchema = leapCategories.reduce((acc, val, key) => { - return {...acc, [val]: hideLeapCats.findIndex(c => c === val) !== -1 }; - }, {} as FormSchema); + return null; - const onSubmit = useCallback((values: FormSchema) => { - setLocalState(state => { - state.hideLeapCats = _.keys(_.pickBy(values, v => v)) as any; - }); - - }, [setLocalState]); return ( diff --git a/pkg/interface/src/views/apps/settings/components/settings.tsx b/pkg/interface/src/views/apps/settings/components/settings.tsx index 218ea0b40..7d7be7b5b 100644 --- a/pkg/interface/src/views/apps/settings/components/settings.tsx +++ b/pkg/interface/src/views/apps/settings/components/settings.tsx @@ -55,11 +55,6 @@ export default function Settings(props: {}) { title="Notifications" description="Set notification visibility and default behaviours for groups and messaging" /> - + + {/* + - + />*/} ); diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index dde98baec..660bbe024 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -78,7 +78,7 @@ export default function SettingsScreen(props: any) { path="/~settings/calm" render={() => { return ( - + ); }} /> diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 16a2e2666..c2cbc6307 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -21,17 +21,18 @@ import { uxToHex } from "~/logic/lib/util"; import { SetStatusBarModal } from './SetStatusBarModal'; import { useTutorialModal } from './useTutorialModal'; -import useLocalState from '~/logic/state/local'; +import useLocalState, { selectLocalState } from '~/logic/state/local'; +import useSettingsState, { selectCalmState } from '~/logic/state/settings'; +const localSel = selectLocalState(['toggleOmnibox']); + const StatusBar = (props) => { const { ourContact, api, ship } = props; const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj))); const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; - const { toggleOmnibox, hideAvatars } = - useLocalState(({ toggleOmnibox, hideAvatars }) => - ({ toggleOmnibox, hideAvatars }) - ); + const { toggleOmnibox } = useLocalState(localSel); + const { hideAvatars } = useSettingsState(selectCalmState); const color = !!ourContact ? `#${uxToHex(props.ourContact.color)}` : '#000'; const xPadding = (!hideAvatars && ourContact?.avatar) ? '0' : '2'; diff --git a/pkg/interface/src/views/landscape/components/Participants.tsx b/pkg/interface/src/views/landscape/components/Participants.tsx index 6ae9275fb..6e311bea3 100644 --- a/pkg/interface/src/views/landscape/components/Participants.tsx +++ b/pkg/interface/src/views/landscape/components/Participants.tsx @@ -31,7 +31,7 @@ import { Dropdown } from '~/views/components/Dropdown'; import GlobalApi from '~/logic/api/global'; import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction'; import styled from 'styled-components'; -import useLocalState from '~/logic/state/local'; +import useSettingsState, { selectCalmState } from '~/logic/state/settings'; const TruncText = styled(Text)` white-space: nowrap; @@ -258,9 +258,7 @@ function Participant(props: { }) { const { contact, association, group, api } = props; const { title } = association.metadata; - const { hideAvatars, hideNicknames } = useLocalState( - ({ hideAvatars, hideNicknames }) => ({ hideAvatars, hideNicknames }) - ); + const { hideAvatars, hideNicknames } = useSettingsState(selectCalmState); const color = uxToHex(contact.color); const isInvite = 'invite' in group.policy; From bcb4791d7243a5ade81e66777a918ebd92f4ccea Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 22 Feb 2021 15:46:22 +1000 Subject: [PATCH 05/18] settings: bring S3 to design --- .../src/views/apps/settings/components/lib/S3Form.tsx | 4 +++- .../src/views/apps/settings/components/settings.tsx | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx index dcbb2180c..ea37b3cef 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx @@ -14,6 +14,7 @@ import { Formik } from "formik"; import GlobalApi from "../../../../api/global"; import { BucketList } from "./BucketList"; import { S3State } from "../../../../types"; +import {BackButton} from "./BackButton"; interface FormSchema { s3bucket: string; @@ -50,6 +51,7 @@ export default function S3Form(props: S3FormProps) { return ( <> +
- + S3 Storage Setup diff --git a/pkg/interface/src/views/apps/settings/components/settings.tsx b/pkg/interface/src/views/apps/settings/components/settings.tsx index 7d7be7b5b..741b9f8c9 100644 --- a/pkg/interface/src/views/apps/settings/components/settings.tsx +++ b/pkg/interface/src/views/apps/settings/components/settings.tsx @@ -66,9 +66,9 @@ export default function Settings(props: {}) { description="Modulate vearious elements across Landscape to maximize calmness" /> Date: Mon, 22 Feb 2021 16:58:52 +1000 Subject: [PATCH 06/18] settings: add leap config --- pkg/interface/src/logic/lib/omnibox.js | 32 ++++---- pkg/interface/src/logic/state/settings.tsx | 8 +- pkg/interface/src/types/local-update.ts | 2 +- .../settings/components/lib/LeapSettings.tsx | 76 +++++++++++++++---- .../apps/settings/components/settings.tsx | 4 +- .../src/views/apps/settings/settings.tsx | 4 +- .../src/views/components/ShuffleFields.tsx | 56 ++++++++++++++ .../src/views/components/leap/Omnibox.tsx | 10 ++- 8 files changed, 153 insertions(+), 39 deletions(-) create mode 100644 pkg/interface/src/views/components/ShuffleFields.tsx diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index b775127fe..7ffbffc06 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -70,25 +70,25 @@ const appIndex = function (apps) { return applications; }; -const otherIndex = function(hide) { +const otherIndex = function(config) { const other = []; - console.log(hide); - const notBanned = (cat) => hide.findIndex(c => c === cat) === -1; - if(notBanned('mychannel')) { - other.push(result('My Channels', '/~landscape/home', 'home', null)); - } - if(notBanned('updates')) { - other.push(result('Notifications', '/~notifications', 'inbox', null)); - } - if(notBanned('profile')) { - other.push(result('Profile and Settings', `/~profile/~${window.ship}`, 'profile', null)); - } - - other.push(result('Messages', '/~landscape/messages', 'messages', null)); - if(notBanned('logout')) { - other.push(result('Log Out', '/~/logout', 'logout', null)); + const idx = { + mychannel: result('My Channels', '/~landscape/home', 'home', null), + updates: result('Notifications', '/~notifications', 'inbox', null), + profile: result('Profile and Settings', `/~profile/~${window.ship}`, 'profile', null), + messages: result('Messages', '/~landscape/messages', 'messages', null), + logout: result('Log Out', '/~/logout', 'logout', null) + }; + + console.log(config); + + for(let cat of JSON.parse(config.categories)) { + if(idx[cat]) { + other.push(idx[cat]); + } } + return other; }; diff --git a/pkg/interface/src/logic/state/settings.tsx b/pkg/interface/src/logic/state/settings.tsx index ca1f47954..97354b605 100644 --- a/pkg/interface/src/logic/state/settings.tsx +++ b/pkg/interface/src/logic/state/settings.tsx @@ -3,7 +3,7 @@ import f from 'lodash/fp'; import create, { State } from 'zustand'; import { persist } from 'zustand/middleware'; import produce from 'immer'; -import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress, LeapCategories } from "~/types/local-update"; +import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress, LeapCategories, leapCategories } from "~/types/local-update"; export interface SettingsState { @@ -17,6 +17,9 @@ export interface SettingsState { hideAvatars: boolean; }; remoteContentPolicy: RemoteContentPolicy; + leap: { + categories: string; + } set: (fn: (state: SettingsState) => void) => void }; @@ -43,6 +46,9 @@ const useSettingsState = create((set) => ({ audioShown: true, videoShown: true }, + leap: { + categories: JSON.stringify(leapCategories), + }, set: (fn: (state: SettingsState) => void) => set(produce(fn)) })); diff --git a/pkg/interface/src/types/local-update.ts b/pkg/interface/src/types/local-update.ts index 731bd8244..9813729f3 100644 --- a/pkg/interface/src/types/local-update.ts +++ b/pkg/interface/src/types/local-update.ts @@ -1,6 +1,6 @@ export const tutorialProgress = ['hidden', 'start', 'group-desc', 'channels', 'chat', 'link', 'publish', 'profile', 'leap', 'notifications', 'done', 'exit'] as const; -export const leapCategories = ["commands", "mychannel", "messages", "updates", "profile", "logout"] as const; +export const leapCategories = ["mychannel", "messages", "updates", "profile", "logout"] as const; export type LeapCategories = typeof leapCategories[number]; diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx index 6c5020679..ef832207f 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -1,26 +1,78 @@ import React, { useCallback } from "react"; -import _ from 'lodash'; +import _ from "lodash"; import { Col, Text, ManagedToggleSwitchField as Toggle, ManagedCheckboxField, + BaseInput, } from "@tlon/indigo-react"; -import { Form, FormikHelpers } from "formik"; +import { Form, FormikHelpers, useField, useFormikContext } from "formik"; import { FormikOnBlur } from "~/views/components/FormikOnBlur"; import { BackButton } from "./BackButton"; import GlobalApi from "~/logic/api/global"; -import { NotificationGraphConfig, LeapCategories, leapCategories } from "~/types"; -import useLocalState, {selectLocalState} from "~/logic/state/local"; +import { + NotificationGraphConfig, + LeapCategories, + leapCategories, +} from "~/types"; +import useSettingsState, { selectSettingsState } from "~/logic/state/settings"; +import { ShuffleFields } from "~/views/components/ShuffleFields"; +const labels: Record = { + mychannel: "My Channel", + updates: "Notifications", + profile: "Profile and Settings", + messages: "Messages", + logout: "Log Out", +}; -export function LeapSettings(props: {}) { +interface FormSchema { + categories: { display: boolean; category: LeapCategories }[]; +} - //TODO: arrays in settings-store? +function CategoryCheckbox(props: { index: number }) { + const { index } = props; + const { values } = useFormikContext(); + const cats = values.categories; + const catNameId = `categories[${index}].category`; + const [field] = useField(catNameId); - return null; + const { category } = cats[index]; + const label = labels[category]; + + return ( + + ); +} + +const settingsSel = selectSettingsState(["leap", "set"]); + +export function LeapSettings(props: { api: GlobalApi; }) { + const { api } = props; + const { leap, set: setSettingsState } = useSettingsState(settingsSel); + const categories = JSON.parse(leap.categories) as LeapCategories[]; + const missing = _.difference(leapCategories, categories); + console.log(categories); + + const initialValues = { + categories: [ + ...categories.map((cat) => ({ + category: cat, + display: true, + })), + ...missing.map((cat) => ({ category: cat, display: false })), + ], + }; + + const onSubmit = async (values: FormSchema) => { + const result = values.categories.reduce( + (acc, { display, category }) => (display ? [...acc, category] : acc), + [] as LeapCategories[] + ); + await api.settings.putEntry('leap', 'categories', JSON.stringify(result)); + }; - return ( @@ -38,11 +90,9 @@ export function LeapSettings(props: {}) { Customize default Leap sections - - - - - + + {(index, helpers) => } + diff --git a/pkg/interface/src/views/apps/settings/components/settings.tsx b/pkg/interface/src/views/apps/settings/components/settings.tsx index 741b9f8c9..9deeec6eb 100644 --- a/pkg/interface/src/views/apps/settings/components/settings.tsx +++ b/pkg/interface/src/views/apps/settings/components/settings.tsx @@ -85,12 +85,12 @@ export default function Settings(props: {}) { to="hosting" title="Hosting Services" description="Hosting-specific service configuration" - /> + />*/} */} + /> ); diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index 660bbe024..9b1bb2690 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -43,7 +43,7 @@ export default function SettingsScreen(props: any) { path={["/~settings/leap"]} render={() => { return ( - + ); }} /> @@ -67,7 +67,7 @@ export default function SettingsScreen(props: any) { }} /> { return ( diff --git a/pkg/interface/src/views/components/ShuffleFields.tsx b/pkg/interface/src/views/components/ShuffleFields.tsx new file mode 100644 index 000000000..b37156dc4 --- /dev/null +++ b/pkg/interface/src/views/components/ShuffleFields.tsx @@ -0,0 +1,56 @@ +import React, { ReactNode, useMemo, useCallback } from "react"; + +import { + FieldArray, + FieldArrayRenderProps, + Field, + useFormikContext, +} from "formik"; +import { Icon, Col, Row } from "@tlon/indigo-react"; + +interface ShuffleFieldsProps { + name: N; + children: (index: number, props: FieldArrayRenderProps) => ReactNode; +} + +type Value = { + [k in I]: T[]; +}; + +export function ShuffleFields>( + props: ShuffleFieldsProps +) { + const { name, children } = props; + const { values } = useFormikContext(); + const fields: T[] = useMemo(() => values[name], [values, name]); + + return ( + { + const goUp = (i: number) => () => { + if(i > 0) { + arrayHelpers.swap(i - 1, i); + } + }; + const goDown = (i: number) => () => { + if(i < fields.length - 1) { + arrayHelpers.swap(i + 1, i); + + } + }; + return ( + + {fields.map((field, i) => ( + + {children(i, arrayHelpers)} + + + + ))} + + ); + }} + /> + ); +} diff --git a/pkg/interface/src/views/components/leap/Omnibox.tsx b/pkg/interface/src/views/components/leap/Omnibox.tsx index a5a2b46f4..865001f0a 100644 --- a/pkg/interface/src/views/components/leap/Omnibox.tsx +++ b/pkg/interface/src/views/components/leap/Omnibox.tsx @@ -13,6 +13,7 @@ import defaultApps from '~/logic/lib/default-apps'; import {Associations, Contacts, Groups, Tile, Invites} from '~/types'; import {useOutsideClick} from '~/logic/lib/useOutsideClick'; import {Portal} from '../Portal'; +import useSettingsState, {SettingsState} from '~/logic/state/settings'; interface OmniboxProps { associations: Associations; @@ -28,12 +29,13 @@ interface OmniboxProps { } const SEARCHED_CATEGORIES = ['ships', 'other', 'commands', 'groups', 'subscriptions', 'apps']; -const localSelector = selectLocalState(["hideLeapCats"]); +const settingsSel = (s: SettingsState) => s.leap; export function Omnibox(props: OmniboxProps) { const location = useLocation(); const history = useHistory(); - const { hideLeapCats } = useLocalState(localSelector); + //const { hideLeapCats } = useLocalState(localSelector); + const leapConfig = useSettingsState(settingsSel); const omniboxRef = useRef(null) const inputRef = useRef(null); @@ -61,11 +63,11 @@ export function Omnibox(props: OmniboxProps) { props.tiles, selectedGroup, props.groups, - hideLeapCats, + leapConfig, ); }, [ selectedGroup, - hideLeapCats, + leapConfig, contacts, props.associations, props.groups, From 13f0ea755b309c2f892eb48405744bccd299f3ee Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 23 Feb 2021 13:32:41 +1000 Subject: [PATCH 07/18] settings: sidebar layout --- pkg/interface/src/logic/lib/omnibox.js | 3 - .../apps/settings/components/lib/CalmPref.tsx | 9 +- .../settings/components/lib/DisplayForm.tsx | 9 +- .../settings/components/lib/LeapSettings.tsx | 1 - .../components/lib/NotificationPref.tsx | 1 - .../apps/settings/components/lib/S3Form.tsx | 1 - .../apps/settings/components/lib/Security.tsx | 2 - .../src/views/apps/settings/settings.tsx | 165 +++++++++++------- .../src/views/components/ShuffleFields.tsx | 14 +- 9 files changed, 117 insertions(+), 88 deletions(-) diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index 7ffbffc06..c53a6af6e 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -80,15 +80,12 @@ const otherIndex = function(config) { logout: result('Log Out', '/~/logout', 'logout', null) }; - console.log(config); - for(let cat of JSON.parse(config.categories)) { if(idx[cat]) { other.push(idx[cat]); } } - return other; }; diff --git a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx index d3c72a8e9..5bf8a3e89 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx @@ -11,6 +11,7 @@ import * as Yup from "yup"; import { BackButton } from "./BackButton"; import useSettingsState, {selectSettingsState} from "~/logic/state/settings"; import GlobalApi from "~/logic/api/global"; +import {AsyncButton} from "~/views/components/AsyncButton"; interface FormSchema { hideAvatars: boolean; @@ -51,7 +52,7 @@ export function CalmPrefs(props: { }; const onSubmit = useCallback(async (v: FormSchema, actions: FormikHelpers) => { - return Promise.all([ + await Promise.all([ api.settings.putEntry('calm', 'hideAvatars', v.hideAvatars), api.settings.putEntry('calm', 'hideNicknames', v.hideNicknames), api.settings.putEntry('remoteContentPolicy', 'imageShown', v.imageShown), @@ -59,13 +60,13 @@ export function CalmPrefs(props: { api.settings.putEntry('remoteContentPolicy', 'audioShown', v.audioShown), api.settings.putEntry('remoteContentPolicy', 'oembedShown', v.oembedShown), ]); + actions.setStatus({ success: null }); }, [api]); return (
- CalmEngine @@ -107,9 +108,9 @@ export function CalmPrefs(props: { caption="Embedded content may contain scripts that can track you" /> - +
diff --git a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx index be5c4f3fb..381a55e46 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx @@ -17,6 +17,7 @@ import { S3State, BackgroundConfig } from "~/types"; import { BackgroundPicker, BgType } from "./BackgroundPicker"; import { BackButton } from "./BackButton"; import useSettingsState, { SettingsState, selectSettingsState } from "~/logic/state/settings"; +import {AsyncButton} from "~/views/components/AsyncButton"; const formSchema = Yup.object().shape({ bgType: Yup.string() @@ -49,7 +50,6 @@ export default function DisplayForm(props: DisplayFormProps) { } } = useSettingsState(settingsSel); - console.log(backgroundType); let bgColor, bgUrl; if (backgroundType === "url") { @@ -85,12 +85,13 @@ export default function DisplayForm(props: DisplayFormProps) { await Promise.all(promises); + actions.setStatus({ success: null }); + }} > {(props) => (
- Display Preferences @@ -105,9 +106,9 @@ export default function DisplayForm(props: DisplayFormProps) { api={api} s3={s3} /> - + )} diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx index ef832207f..ad535062e 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -75,7 +75,6 @@ export function LeapSettings(props: { api: GlobalApi; }) { return ( - Leap diff --git a/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx index cdbfecae1..e68a55f3d 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx @@ -52,7 +52,6 @@ export function NotificationPreferences(props: { return ( - Notification Preferences diff --git a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx index ea37b3cef..f05876557 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx @@ -51,7 +51,6 @@ export default function S3Form(props: S3FormProps) { return ( <> - - Security Preferences diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index 9b1bb2690..a9b749450 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -1,17 +1,20 @@ import React, { ReactNode } from "react"; -import { Route, Link, Switch } from "react-router-dom"; +import { Route, Link, Switch, useLocation } from "react-router-dom"; import Helmet from "react-helmet"; import { Box, Text, Row, Col, Icon, BaseImage } from "@tlon/indigo-react"; import Settings from "./components/settings"; import { NotificationPreferences } from "./components/lib/NotificationPref"; -import DisplayForm from './components/lib/DisplayForm'; +import DisplayForm from "./components/lib/DisplayForm"; import S3Form from "./components/lib/S3Form"; import useLocalState from "~/logic/state/local"; -import {CalmPrefs} from "./components/lib/CalmPref"; +import { CalmPrefs } from "./components/lib/CalmPref"; import SecuritySettings from "./components/lib/Security"; -import {LeapSettings} from "./components/lib/LeapSettings"; +import { LeapSettings } from "./components/lib/LeapSettings"; +import { useHashLink } from "~/logic/lib/useHashLink"; +import { SidebarItem as BaseSidebarItem } from "~/views/landscape/components/SidebarItem"; +import { PropFunc } from "~/types"; export const Skeleton = (props: { children: ReactNode }) => ( @@ -22,81 +25,113 @@ export const Skeleton = (props: { children: ReactNode }) => ( bg="white" border={1} borderColor="washedGray" - overflowY="auto" > {props.children} ); +type ProvSideProps = "to" | "selected"; +type BaseProps = PropFunc; +function SidebarItem(props: { hash: string } & Omit) { + const { hash, icon, text, ...rest } = props; + + const to = `/~settings#${hash}`; + + const location = useLocation(); + const selected = location.hash.slice(1) === hash; + + return ( + + ); +} + +function SettingsItem(props: { hash: string; children: ReactNode }) { + const { hash, children } = props; + + return ( + + {children} + + ); +} + export default function SettingsScreen(props: any) { const { ship, dark } = props; - const hideAvatars = useLocalState((state) => state.hideAvatars); + + useHashLink(); + return ( <> Landscape - Settings - - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - { - return ; - }} - /> - + + + + System Preferences + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); diff --git a/pkg/interface/src/views/components/ShuffleFields.tsx b/pkg/interface/src/views/components/ShuffleFields.tsx index b37156dc4..2f991a17b 100644 --- a/pkg/interface/src/views/components/ShuffleFields.tsx +++ b/pkg/interface/src/views/components/ShuffleFields.tsx @@ -6,7 +6,7 @@ import { Field, useFormikContext, } from "formik"; -import { Icon, Col, Row } from "@tlon/indigo-react"; +import { Icon, Col, Row, Box } from "@tlon/indigo-react"; interface ShuffleFieldsProps { name: N; @@ -40,15 +40,15 @@ export function ShuffleFields>( } }; return ( - + {fields.map((field, i) => ( - + + + {children(i, arrayHelpers)} - - - + ))} - + ); }} /> From 6fa0cd29e2ab08baaab5f5dfe335a80e49f21e4d Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 26 Feb 2021 13:03:46 +1000 Subject: [PATCH 08/18] settings: migrate --- .../src/logic/lib/migrateSettings.ts | 72 +++++++++++++++++++ .../src/views/landscape/components/Content.js | 12 +++- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 pkg/interface/src/logic/lib/migrateSettings.ts diff --git a/pkg/interface/src/logic/lib/migrateSettings.ts b/pkg/interface/src/logic/lib/migrateSettings.ts new file mode 100644 index 000000000..eddd67a9e --- /dev/null +++ b/pkg/interface/src/logic/lib/migrateSettings.ts @@ -0,0 +1,72 @@ +import useLocalState, { LocalState } from "~/logic/state/local"; +import useSettingsState from "~/logic/state/settings"; +import GlobalApi from "../api/global"; +import { BackgroundConfig, RemoteContentPolicy } from "~/types"; + +const getBackgroundString = (bg: BackgroundConfig) => { + if (bg?.type === "url") { + return bg.url; + } else if (bg?.type === "color") { + return bg.color; + } else { + return ""; + } +}; + +export function useMigrateSettings(api: GlobalApi) { + const local = useLocalState(); + const { display, remoteContentPolicy, calm } = useSettingsState(); + + return async () => { + if (!localStorage.has("localReducer")) { + return; + } + + let promises: Promise[] = []; + + if (local.hideAvatars !== calm.hideAvatars) { + promises.push( + api.settings.putEntry("calm", "hideAvatars", local.hideAvatars) + ); + } + + if (local.hideNicknames !== calm.hideNicknames) { + promises.push( + api.settings.putEntry("calm", "hideNicknames", local.hideNicknames) + ); + } + + if ( + local?.background?.type && + display.background !== getBackgroundString(local.background) + ) { + promises.push( + api.settings.putEntry( + "display", + "background", + getBackgroundString(local.background) + ) + ); + promises.push( + api.settings.putEntry( + "display", + "backgroundType", + local.background?.type + ) + ); + } + + Object.keys(local.remoteContentPolicy).forEach((_key) => { + const key = _key as keyof RemoteContentPolicy; + const localVal = local.remoteContentPolicy[key]; + if (localVal !== remoteContentPolicy[key]) { + promises.push( + api.settings.putEntry("remoteContentPolicy", key, localVal) + ); + } + }); + + await Promise.all(promises); + localStorage.removeItem("localReducer"); + }; +} diff --git a/pkg/interface/src/views/landscape/components/Content.js b/pkg/interface/src/views/landscape/components/Content.js index a507e4f3b..a0f5cbe93 100644 --- a/pkg/interface/src/views/landscape/components/Content.js +++ b/pkg/interface/src/views/landscape/components/Content.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { Component, useEffect } from 'react'; import { Box } from '@tlon/indigo-react'; import { Route, Switch } from 'react-router-dom'; import styled from 'styled-components'; @@ -12,6 +12,8 @@ import ErrorComponent from '~/views/components/Error'; import Notifications from '~/views/apps/notifications/notifications'; import GraphApp from '../../apps/graph/app'; +import { useMigrateSettings } from '~/logic/lib/migrateSettings'; + export const Container = styled(Box)` flex-grow: 1; @@ -22,6 +24,14 @@ export const Container = styled(Box)` export const Content = (props) => { + + const doMigrate = useMigrateSettings(); + useEffect(() => { + setTimeout(() => { + doMigrate(); + }, 10000); + }, []); + return ( From 639372e58fd899a51e2951c4842350c1e9fb7413 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:06:48 -0500 Subject: [PATCH 09/18] leap: add missing import --- pkg/interface/src/views/components/leap/Omnibox.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/interface/src/views/components/leap/Omnibox.tsx b/pkg/interface/src/views/components/leap/Omnibox.tsx index cf63163ed..683adce1f 100644 --- a/pkg/interface/src/views/components/leap/Omnibox.tsx +++ b/pkg/interface/src/views/components/leap/Omnibox.tsx @@ -10,6 +10,7 @@ import makeIndex from '~/logic/lib/omnibox'; import OmniboxInput from './OmniboxInput'; import OmniboxResult from './OmniboxResult'; import { deSig } from '~/logic/lib/util'; +import { withLocalState } from '~/logic/state/local'; import defaultApps from '~/logic/lib/default-apps'; import {useOutsideClick} from '~/logic/lib/useOutsideClick'; @@ -51,8 +52,8 @@ export function Omnibox(props: OmniboxProps) { }, [props.contacts, query]); const selectedGroup = useMemo( - () => location.pathname.startsWith('/~landscape/ship/') - ? '/' + location.pathname.split('/').slice(2,5).join('/') + () => location.pathname.startsWith('/~landscape/ship/') + ? '/' + location.pathname.split('/').slice(2,5).join('/') : null, [location.pathname] ); From 9841e92325bc35d14df48fc8c7015e77192d2911 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:07:09 -0500 Subject: [PATCH 10/18] settings: switch to multipage layout --- .../apps/settings/components/lib/CalmPref.tsx | 2 +- .../settings/components/lib/DisplayForm.tsx | 8 +-- .../settings/components/lib/LeapSettings.tsx | 8 +-- .../components/lib/NotificationPref.tsx | 2 +- .../apps/settings/components/lib/S3Form.tsx | 4 +- .../apps/settings/components/lib/Security.tsx | 10 ++-- .../src/views/apps/settings/settings.tsx | 51 ++++++++++--------- 7 files changed, 44 insertions(+), 41 deletions(-) diff --git a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx index 5bf8a3e89..f9ea6225c 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx @@ -66,7 +66,7 @@ export function CalmPrefs(props: { return (
- + CalmEngine diff --git a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx index a93f179ba..ce856ecb6 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx @@ -48,7 +48,7 @@ export default function DisplayForm(props: DisplayFormProps) { let bgColor, bgUrl; if (backgroundType === "url") { - bgUrl = background; + bgUrl = background; } if (backgroundType === "color") { bgColor = background; @@ -70,11 +70,11 @@ export default function DisplayForm(props: DisplayFormProps) { promises.push(api.settings.putEntry('display', 'backgroundType', values.bgType)); promises.push( - api.settings.putEntry('display', 'background', + api.settings.putEntry('display', 'background', values.bgType === "color" ? `#${uxToHex(values.bgColor || "0x0")}` : values.bgType === "url" - ? values.bgUrl || "" + ? values.bgUrl || "" : false )); @@ -86,7 +86,7 @@ export default function DisplayForm(props: DisplayFormProps) { > {(props) => ( - + Display Preferences diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx index ad535062e..fd9a7f1c4 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -74,19 +74,19 @@ export function LeapSettings(props: { api: GlobalApi; }) { }; return ( - - + + Leap - + Customize Leap ordering, omit modules or results - + Customize default Leap sections diff --git a/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx index e68a55f3d..e1d4cfe5c 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/NotificationPref.tsx @@ -51,7 +51,7 @@ export function NotificationPreferences(props: { }, [api]); return ( - + Notification Preferences diff --git a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx index 93c10679f..b62887c54 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx @@ -48,7 +48,7 @@ export default function S3Form(props: S3FormProps): ReactElement { ); return ( <> - + - + S3 Storage Setup diff --git a/pkg/interface/src/views/apps/settings/components/lib/Security.tsx b/pkg/interface/src/views/apps/settings/components/lib/Security.tsx index 5dc343b88..a20d68459 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/Security.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/Security.tsx @@ -16,8 +16,8 @@ interface SecuritySettingsProps { export default function SecuritySettings({ api }: SecuritySettingsProps) { const [allSessions, setAllSessions] = useState(false); return ( - - + + Security Preferences @@ -26,10 +26,10 @@ export default function SecuritySettings({ api }: SecuritySettingsProps) { - + Log out of this session - + {allSessions ? "You will be logged out of all browsers that have currently logged into your Urbit." : "You will be logged out of your Urbit on this browser."} @@ -39,7 +39,7 @@ export default function SecuritySettings({ api }: SecuritySettingsProps) { selected={allSessions} onChange={() => setAllSessions((s) => !s)} > - Log out of all sessions + Log out of all sessions {allSessions && } diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index 303fed0c5..84b606234 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -50,11 +50,11 @@ function SidebarItem(props: { hash: string } & Omit) { ); } -function SettingsItem(props: { hash: string; children: ReactNode }) { - const { hash, children } = props; +function SettingsItem(props: { children: ReactNode }) { + const { children } = props; return ( - + {children} ); @@ -62,7 +62,8 @@ function SettingsItem(props: { hash: string; children: ReactNode }) { export default function SettingsScreen(props: any) { - useHashLink(); + const location = useLocation(); + const hash = location.hash.slice(1) return ( <> @@ -106,26 +107,28 @@ export default function SettingsScreen(props: any) { - - - - - - - - - - - - - - - - - + + {hash === "notifications" && ( + + )} + {hash === "display" && ( + + )} + {hash === "s3" && ( + + )} + {hash === "leap" && ( + + )} + {hash === "calm" && ( + + )} + {hash === "security" && ( + + )} From e266273ff1583b47f3e2a931278e90ae2a55536d Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:21:27 -0500 Subject: [PATCH 11/18] ChatMessage, Sidebar: use new hideAvatars import --- .../src/views/apps/chat/components/ChatMessage.tsx | 4 +++- .../views/landscape/components/Sidebar/SidebarItem.tsx | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx index 5cf286f5a..9b24f65b7 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx @@ -16,6 +16,7 @@ import { cite, writeText, useShowNickname, + useHideAvatar, useHovering } from '~/logic/lib/util'; import { @@ -32,6 +33,7 @@ import RemoteContent from '~/views/components/RemoteContent'; import { Mention } from '~/views/components/MentionText'; import styled from 'styled-components'; import useLocalState from '~/logic/state/local'; +import useSettingsState, {selectCalmState} from "~/logic/state/settings"; import Timestamp from '~/views/components/Timestamp'; export const DATESTAMP_FORMAT = '[~]YYYY.M.D'; @@ -239,7 +241,7 @@ export const MessageAuthor = ({ const contact = `~${msg.author}` in contacts ? contacts[`~${msg.author}`] : false; const showNickname = useShowNickname(contact); - const { hideAvatars } = useLocalState(({ hideAvatars }) => ({ hideAvatars })); + const { hideAvatars } = useSettingsState(selectCalmState); const shipName = showNickname ? contact.nickname : cite(msg.author); const copyNotice = 'Copied'; const color = contact diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx index 1d52a9fca..5521b19ce 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx @@ -8,10 +8,11 @@ import { HoverBoxLink } from '~/views/components/HoverBox'; import { Sigil } from '~/logic/lib/sigil'; import { getModuleIcon, getItemTitle, uxToHex } from '~/logic/lib/util'; import { useTutorialModal } from '~/views/components/useTutorialModal'; -import useLocalState from '~/logic/state/local'; import { TUTORIAL_HOST, TUTORIAL_GROUP } from '~/logic/lib/tutorialModal'; import { SidebarAppConfigs, SidebarItemStatus } from './types'; import { Workspace } from '~/types/workspace'; +import useSettingsState, { selectCalmState } from '~/logic/state/settings'; + function SidebarItemIndicator(props: { status?: SidebarItemStatus }) { switch (props.status) { @@ -56,9 +57,8 @@ export function SidebarItem(props: { return null; } const DM = (isUnmanaged && props.workspace?.type === 'messages'); - const { hideAvatars, hideNicknames } = useLocalState(({ hideAvatars, hideNicknames }) => ({ - hideAvatars, hideNicknames - })); + const { hideAvatars, hideNicknames } = useSettingsState(selectCalmState); + const itemStatus = app.getStatus(path); const hasUnread = itemStatus === 'unread' || itemStatus === 'mention'; From f0e1a24b42e1da6783bda3384a435dfb8b0ab840 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:25:50 -0500 Subject: [PATCH 12/18] SidebarItem: centre icons --- pkg/interface/src/views/landscape/components/SidebarItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/interface/src/views/landscape/components/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/SidebarItem.tsx index 71aea2a02..667ed2fed 100644 --- a/pkg/interface/src/views/landscape/components/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/SidebarItem.tsx @@ -34,7 +34,7 @@ export const SidebarItem = ({ justifyContent="space-between" {...rest} > - + {text} From 9f64db52c19eb42a4ca51155cb6c7b14667ac973 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:25:57 -0500 Subject: [PATCH 13/18] settings: replace calm icon --- pkg/interface/src/views/apps/settings/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index 84b606234..9b9fef668 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -98,7 +98,7 @@ export default function SettingsScreen(props: any) { - + Date: Fri, 26 Feb 2021 13:26:32 -0500 Subject: [PATCH 14/18] leap: "Profile and Settings" -> "profile" --- pkg/interface/src/logic/lib/omnibox.js | 2 +- .../src/views/apps/settings/components/lib/LeapSettings.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index c53a6af6e..08e4fff42 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -75,7 +75,7 @@ const otherIndex = function(config) { const idx = { mychannel: result('My Channels', '/~landscape/home', 'home', null), updates: result('Notifications', '/~notifications', 'inbox', null), - profile: result('Profile and Settings', `/~profile/~${window.ship}`, 'profile', null), + profile: result('Profile', `/~profile/~${window.ship}`, 'profile', null), messages: result('Messages', '/~landscape/messages', 'messages', null), logout: result('Log Out', '/~/logout', 'logout', null) }; diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx index fd9a7f1c4..9e1e9e877 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -22,7 +22,7 @@ import { ShuffleFields } from "~/views/components/ShuffleFields"; const labels: Record = { mychannel: "My Channel", updates: "Notifications", - profile: "Profile and Settings", + profile: "Profile", messages: "Messages", logout: "Log Out", }; From 2db485705eab2584506753c546d06ee5257f8b8e Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:41:12 -0500 Subject: [PATCH 15/18] npm/api: add array type to settings value --- pkg/npm/api/settings/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/npm/api/settings/index.d.ts b/pkg/npm/api/settings/index.d.ts index f0f50df49..eed8cda71 100644 --- a/pkg/npm/api/settings/index.d.ts +++ b/pkg/npm/api/settings/index.d.ts @@ -1,5 +1,5 @@ export type Key = string; -export type Value = string | boolean | number; +export type Value = string | string[] | boolean | number; export type Bucket = Map; export type Settings = Map; From b4d82a9e8aad6976e4804370fc941838c5b18783 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 13:41:40 -0500 Subject: [PATCH 16/18] leap, settings: store leap settings as array --- pkg/interface/src/logic/lib/migrateSettings.ts | 2 +- pkg/interface/src/logic/lib/omnibox.js | 2 +- pkg/interface/src/logic/state/settings.tsx | 10 +++++----- .../apps/settings/components/lib/LeapSettings.tsx | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/interface/src/logic/lib/migrateSettings.ts b/pkg/interface/src/logic/lib/migrateSettings.ts index eddd67a9e..bcfe44bfc 100644 --- a/pkg/interface/src/logic/lib/migrateSettings.ts +++ b/pkg/interface/src/logic/lib/migrateSettings.ts @@ -18,7 +18,7 @@ export function useMigrateSettings(api: GlobalApi) { const { display, remoteContentPolicy, calm } = useSettingsState(); return async () => { - if (!localStorage.has("localReducer")) { + if (!localStorage?.has("localReducer")) { return; } diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index 08e4fff42..74488c4b0 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -80,7 +80,7 @@ const otherIndex = function(config) { logout: result('Log Out', '/~/logout', 'logout', null) }; - for(let cat of JSON.parse(config.categories)) { + for(let cat of config.categories) { if(idx[cat]) { other.push(idx[cat]); } diff --git a/pkg/interface/src/logic/state/settings.tsx b/pkg/interface/src/logic/state/settings.tsx index 97354b605..91343c921 100644 --- a/pkg/interface/src/logic/state/settings.tsx +++ b/pkg/interface/src/logic/state/settings.tsx @@ -18,14 +18,14 @@ export interface SettingsState { }; remoteContentPolicy: RemoteContentPolicy; leap: { - categories: string; + categories: LeapCategories[]; } set: (fn: (state: SettingsState) => void) => void }; export type SettingsStateZus = SettingsState & State; -export const selectSettingsState = +export const selectSettingsState = (keys: K[]) => f.pick(keys); export const selectCalmState = (s: SettingsState) => s.calm; @@ -47,14 +47,14 @@ const useSettingsState = create((set) => ({ videoShown: true }, leap: { - categories: JSON.stringify(leapCategories), + categories: leapCategories, }, set: (fn: (state: SettingsState) => void) => set(produce(fn)) -})); +})); function withSettingsState(Component: any, stateMemberKeys?: S[]) { return React.forwardRef((props: Omit, ref) => { - const localState = stateMemberKeys + const localState = stateMemberKeys ? useSettingsState(selectSettingsState(stateMemberKeys)) : useSettingsState(); return diff --git a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx index 9e1e9e877..7dac9b621 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/LeapSettings.tsx @@ -51,7 +51,7 @@ const settingsSel = selectSettingsState(["leap", "set"]); export function LeapSettings(props: { api: GlobalApi; }) { const { api } = props; const { leap, set: setSettingsState } = useSettingsState(settingsSel); - const categories = JSON.parse(leap.categories) as LeapCategories[]; + const categories = leap.categories as LeapCategories[]; const missing = _.difference(leapCategories, categories); console.log(categories); @@ -70,7 +70,7 @@ export function LeapSettings(props: { api: GlobalApi; }) { (acc, { display, category }) => (display ? [...acc, category] : acc), [] as LeapCategories[] ); - await api.settings.putEntry('leap', 'categories', JSON.stringify(result)); + await api.settings.putEntry('leap', 'categories', result); }; return ( From 356d517b4a55611290a33facc6e174e47cd64673 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 26 Feb 2021 14:12:24 -0500 Subject: [PATCH 17/18] CalmEngine: add "hideUnreads" and "hideGroups" --- pkg/interface/src/logic/state/settings.tsx | 4 ++++ pkg/interface/src/views/apps/launch/app.js | 6 +++++- .../views/apps/launch/components/Groups.tsx | 6 ++++-- .../apps/settings/components/lib/CalmPref.tsx | 19 +++++++++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pkg/interface/src/logic/state/settings.tsx b/pkg/interface/src/logic/state/settings.tsx index 91343c921..5a8dc5446 100644 --- a/pkg/interface/src/logic/state/settings.tsx +++ b/pkg/interface/src/logic/state/settings.tsx @@ -15,6 +15,8 @@ export interface SettingsState { calm: { hideNicknames: boolean; hideAvatars: boolean; + hideUnreads: boolean; + hideGroups: boolean; }; remoteContentPolicy: RemoteContentPolicy; leap: { @@ -39,6 +41,8 @@ const useSettingsState = create((set) => ({ calm: { hideNicknames: false, hideAvatars: false, + hideUnreads: false, + hideGroups: false }, remoteContentPolicy: { imageShown: true, diff --git a/pkg/interface/src/views/apps/launch/app.js b/pkg/interface/src/views/apps/launch/app.js index c84c7f87f..465e08234 100644 --- a/pkg/interface/src/views/apps/launch/app.js +++ b/pkg/interface/src/views/apps/launch/app.js @@ -29,6 +29,8 @@ import { TUTORIAL_CHAT, TUTORIAL_LINKS } from '~/logic/lib/tutorialModal'; +import useSettingsState, { selectCalmState } from '~/logic/state/settings'; + const ScrollbarLessBox = styled(Box)` scrollbar-width: none !important; @@ -81,7 +83,9 @@ export default function LaunchApp(props) { } }, [query]); - const { tutorialProgress, nextTutStep, hideGroups } = useLocalState(tutSelector); + const { tutorialProgress, nextTutStep } = useLocalState(tutSelector); + let { hideGroups } = useLocalState(tutSelector); + !hideGroups ? { hideGroups } = useSettingsState(selectCalmState) : null; const waiter = useWaitForProps(props); diff --git a/pkg/interface/src/views/apps/launch/components/Groups.tsx b/pkg/interface/src/views/apps/launch/components/Groups.tsx index f5e1d70e3..3a7a5485b 100644 --- a/pkg/interface/src/views/apps/launch/components/Groups.tsx +++ b/pkg/interface/src/views/apps/launch/components/Groups.tsx @@ -9,6 +9,7 @@ import { getUnreadCount, getNotificationCount } from '~/logic/lib/hark'; import Tile from '../components/tiles/tile'; import { useTutorialModal } from '~/views/components/useTutorialModal'; import { TUTORIAL_HOST, TUTORIAL_GROUP } from '~/logic/lib/tutorialModal'; +import useSettingsState, { selectCalmState } from '~/logic/state/settings'; interface GroupsProps { associations: Associations; @@ -80,11 +81,12 @@ function Group(props: GroupProps) { isTutorialGroup, anchorRef.current ); + const { hideUnreads } = useSettingsState(selectCalmState) return ( {title} - + {!hideUnreads && ( {updates > 0 && ({updates} update{updates !== 1 && 's'} ) } @@ -92,7 +94,7 @@ function Group(props: GroupProps) { ({unreads}) } - + )} ); diff --git a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx index f9ea6225c..bb14e9a7d 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx @@ -16,6 +16,8 @@ import {AsyncButton} from "~/views/components/AsyncButton"; interface FormSchema { hideAvatars: boolean; hideNicknames: boolean; + hideUnreads: boolean; + hideGroups: boolean; imageShown: boolean; audioShown: boolean; oembedShown: boolean; @@ -32,6 +34,8 @@ export function CalmPrefs(props: { calm: { hideAvatars, hideNicknames, + hideUnreads, + hideGroups }, remoteContentPolicy: { imageShown, @@ -45,6 +49,8 @@ export function CalmPrefs(props: { const initialValues: FormSchema = { hideAvatars, hideNicknames, + hideUnreads, + hideGroups, imageShown, videoShown, oembedShown, @@ -55,6 +61,8 @@ export function CalmPrefs(props: { await Promise.all([ api.settings.putEntry('calm', 'hideAvatars', v.hideAvatars), api.settings.putEntry('calm', 'hideNicknames', v.hideNicknames), + api.settings.putEntry('calm', 'hideUnreads', v.hideUnreads), + api.settings.putEntry('calm', 'hideGroups', v.hideGroups), api.settings.putEntry('remoteContentPolicy', 'imageShown', v.imageShown), api.settings.putEntry('remoteContentPolicy', 'videoShown', v.videoShown), api.settings.putEntry('remoteContentPolicy', 'audioShown', v.audioShown), @@ -75,6 +83,17 @@ export function CalmPrefs(props: { Modulate various elemednts across Landscape to maximize calmness + Home screen + + User-set identity Date: Fri, 26 Feb 2021 14:29:39 -0500 Subject: [PATCH 18/18] settings: fix typos --- .../apps/settings/components/lib/CalmPref.tsx | 2 +- .../apps/settings/components/lib/DisplayForm.tsx | 2 +- .../views/apps/settings/components/lib/S3Form.tsx | 14 +++++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx index bb14e9a7d..7d4352f78 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/CalmPref.tsx @@ -80,7 +80,7 @@ export function CalmPrefs(props: { CalmEngine - Modulate various elemednts across Landscape to maximize calmness + Modulate various elements across Landscape to maximize calmness Home screen diff --git a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx index ce856ecb6..8f5390941 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx @@ -91,7 +91,7 @@ export default function DisplayForm(props: DisplayFormProps) { Display Preferences - + Customize visual interfaces across your Landscape diff --git a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx index b62887c54..1e5d7bbf6 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx @@ -7,7 +7,8 @@ import { Box, Text, Button, - Col + Col, + Anchor } from '@tlon/indigo-react'; import GlobalApi from "~/logic/api/global"; @@ -69,8 +70,15 @@ export default function S3Form(props: S3FormProps): ReactElement { Store credentials for your S3 object storage buckets on your - Urbit ship, and upload media freely to various modules. Learn - more + Urbit ship, and upload media freely to various modules. + + Learn more +