From 4644a4135512c05bd0d3151d07067c8fa5591475 Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Fri, 21 Apr 2023 14:32:01 -0400 Subject: [PATCH] settings: refactor system resource panel --- ui/src/preferences/ShipPrefs.tsx | 249 ------------------ ui/src/preferences/SystemPreferences.tsx | 7 +- .../system-resources/IdentityPrefs.tsx | 102 +++++++ .../system-resources/LoomPrefs.tsx | 41 +++ .../system-resources/P2PServicePrefs.tsx | 96 +++++++ .../system-resources/SystemResourcePrefs.tsx | 12 + ui/src/state/azimuth.ts | 54 ++-- ui/src/state/connectivity.ts | 54 ++-- ui/src/state/invites.ts | 28 +- ui/src/state/loom.ts | 24 ++ 10 files changed, 344 insertions(+), 323 deletions(-) delete mode 100644 ui/src/preferences/ShipPrefs.tsx create mode 100644 ui/src/preferences/system-resources/IdentityPrefs.tsx create mode 100644 ui/src/preferences/system-resources/LoomPrefs.tsx create mode 100644 ui/src/preferences/system-resources/P2PServicePrefs.tsx create mode 100644 ui/src/preferences/system-resources/SystemResourcePrefs.tsx create mode 100644 ui/src/state/loom.ts diff --git a/ui/src/preferences/ShipPrefs.tsx b/ui/src/preferences/ShipPrefs.tsx deleted file mode 100644 index 1c50c0d7..00000000 --- a/ui/src/preferences/ShipPrefs.tsx +++ /dev/null @@ -1,249 +0,0 @@ -import classNames from 'classnames'; -import clipboardCopy from 'clipboard-copy'; -import React, { - ComponentProps, - useCallback, - useEffect, - useRef, - useState, -} from 'react'; -import { Avatar } from '../components/Avatar'; -import { Button } from '../components/Button'; -import { Spinner } from '../components/Spinner'; -import { Bullet } from '../components/icons/Bullet'; -import { Cross } from '../components/icons/Cross'; -import { useAzimuthBlock } from '../state/azimuth'; -import { - AvailabilityStatus, - usePeerDiscoveryShips, - useShipAvailability, -} from '../state/connectivity'; - -export const SystemPrefs = () => ( - <> - - - - -); - -export const ShipPrefs = () => { - const { block, isLoading, isStale, forceUpdate } = useAzimuthBlock(); - - return ( -
-

Identity

-
- -
-

~{window.ship}

- {!isLoading && ( -
- - Azimuth block: {block}{' '} - - {!isLoading && isStale && ( - - Stale - - )} -
- )} -
-
- {block && ( - - )} - {!isLoading && isStale && ( - - )} -
-
-
- ); -}; - -type CopyButtonProps = Omit< - ComponentProps, - 'children' | 'onClick' ->; - -const CopyButton = ({ - label = 'copy', - content, - ...buttonProps -}: { - label: string; - content: string; -} & CopyButtonProps) => { - const [successMessageActive, setSuccessMesageActive] = useState(false); - const copyTimeout = useRef | null>(null); - - const copy = useCallback(() => { - if (copyTimeout.current) clearTimeout(copyTimeout.current); - clipboardCopy(content); - setSuccessMesageActive(true); - copyTimeout.current = setTimeout(() => setSuccessMesageActive(false), 1000); - }, []); - - // ensure timeout is cleared when component unmounts - useEffect(() => { - () => { - if (copyTimeout.current) clearTimeout(copyTimeout.current); - }; - }, []); - - return ( - - ); -}; - -const P2PServicePrefs = () => { - const { peerDiscoveryShips, isLoading: isLoadingPeerDiscoveryShips } = - usePeerDiscoveryShips(); - - return ( -
-

P2P Services

- -
-
-

Peer Discovery

-

Ships your Urbit uses to find peers

-
-
- {peerDiscoveryShips?.map((ship) => ( - - ))} -
-
-
- ); -}; - -const AvailabilityIndicator = ({ - isChecking, - status, - className, -}: { - isChecking: boolean; - status: AvailabilityStatus; - className?: string; -}) => { - return ( -
- {isChecking ? ( - - ) : ( -
- {status === 'available' && ( - <> - Available - - - )} - {status === 'unavailable' && ( - <> - Unavailable - - - )} -
- )} -
- ); -}; - -const ConnectivityTester = ({ shipName }: { shipName: string }) => { - const { isChecking, availabilityStatus, refresh } = - useShipAvailability(shipName); - - return ( -
-
- -

{shipName}

- - -
-
- ); -}; - -const useStorage = () => { - return { - free: 3000000, - total: 4096000, - }; -}; - -const StoragePrefs = () => { - const { free, total } = useStorage(); - - return ( -
-

Loom

-
-
-
-
-
-
- {Math.round(free / 1000)}/{Math.round(total / 1000)}mb free -
-
-
- -
-

Optimize Your Urbit's Loom

-

- Deduplicate objects in storage to free up space using |pack.{' '} - - Learn more - -

- -
-
- ); -}; diff --git a/ui/src/preferences/SystemPreferences.tsx b/ui/src/preferences/SystemPreferences.tsx index aa99a71b..0e86fd55 100644 --- a/ui/src/preferences/SystemPreferences.tsx +++ b/ui/src/preferences/SystemPreferences.tsx @@ -39,7 +39,7 @@ import SearchSystemPreferences from './SearchSystemPrefences'; import { ShortcutPrefs } from './ShortcutPrefs'; import { AttentionAndPrivacy } from './AttentionAndPrivacy'; import { Avatar } from '../components/Avatar'; -import { SystemPrefs } from './ShipPrefs'; +import { SystemResourcePrefs } from './system-resources/SystemResourcePrefs'; interface SystemPreferencesSectionProps { url: string; @@ -248,7 +248,10 @@ export const SystemPreferences = ( - + { + const { state, stateLoadStatus, forceUpdate } = useAzimuthState(); + + return ( +
+

Identity

+
+ +
+

~{window.ship}

+ {stateLoadStatus === 'loading' && ( +
+ + Azimuth block: {state?.block} + + {state?.stale && ( + + Stale + + )} +
+ )} +
+
+ {state?.block && ( + + )} + {stateLoadStatus === 'success' && state?.stale && ( + + )} +
+
+
+ ); +}; + +type CopyButtonProps = Omit< + ComponentProps, + 'children' | 'onClick' +>; + +const CopyButton = ({ + label = 'copy', + content, + ...buttonProps +}: { + label: string; + content: string; +} & CopyButtonProps) => { + const [successMessageActive, setSuccessMesageActive] = useState(false); + const copyTimeout = useRef | null>(null); + + const copy = useCallback(() => { + if (copyTimeout.current) clearTimeout(copyTimeout.current); + clipboardCopy(content); + setSuccessMesageActive(true); + copyTimeout.current = setTimeout(() => setSuccessMesageActive(false), 1000); + }, []); + + // ensure timeout is cleared when component unmounts + useEffect(() => { + () => { + if (copyTimeout.current) clearTimeout(copyTimeout.current); + }; + }, []); + + return ( + + ); +}; diff --git a/ui/src/preferences/system-resources/LoomPrefs.tsx b/ui/src/preferences/system-resources/LoomPrefs.tsx new file mode 100644 index 00000000..d4dcbb0d --- /dev/null +++ b/ui/src/preferences/system-resources/LoomPrefs.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Button } from '../../components/Button'; +import { useLoom } from '../../state/loom'; + +export const LoomPrefs = () => { + const { free, total, pack } = useLoom(); + + return ( +
+

Loom

+
+
+
+
+ +
+ +
+

Optimize Your Urbit's Loom

+

+ Deduplicate objects in storage to free up space using |pack.{' '} + + Learn more + +

+ +
+
+ ); +}; diff --git a/ui/src/preferences/system-resources/P2PServicePrefs.tsx b/ui/src/preferences/system-resources/P2PServicePrefs.tsx new file mode 100644 index 00000000..bf3e2272 --- /dev/null +++ b/ui/src/preferences/system-resources/P2PServicePrefs.tsx @@ -0,0 +1,96 @@ +import classNames from 'classnames'; +import React from 'react'; +import { Avatar } from '../../components/Avatar'; +import { Button } from '../../components/Button'; +import { Spinner } from '../../components/Spinner'; +import { Bullet } from '../../components/icons/Bullet'; +import { Cross } from '../../components/icons/Cross'; +import { + AvailabilityStatus, + usePeerDiscoveryShips, + useShipAvailability, +} from '../../state/connectivity'; + +export const P2PServicePrefs = () => { + const { peerDiscoveryShips } = usePeerDiscoveryShips(); + + return ( +
+

P2P Services

+
+
+

Peer Discovery

+

Ships your Urbit uses to find peers

+
+
    + {peerDiscoveryShips?.map((ship) => ( +
  • + +
  • + ))} +
+
+
+ ); +}; + +const ConnectivityTester = ({ shipName }: { shipName: string }) => { + const { status, availability, checkAvailability } = + useShipAvailability(shipName); + + return ( +
+
+ +

{shipName}

+ + +
+
+ ); +}; + +const AvailabilityIndicator = ({ + isChecking, + status, + className, +}: { + isChecking: boolean; + status: AvailabilityStatus; + className?: string; +}) => { + return ( +
+ {isChecking ? ( + + ) : ( +
+ {status === 'available' && ( + <> + Available + + + )} + {status === 'unavailable' && ( + <> + Unavailable + + + )} +
+ )} +
+ ); +}; diff --git a/ui/src/preferences/system-resources/SystemResourcePrefs.tsx b/ui/src/preferences/system-resources/SystemResourcePrefs.tsx new file mode 100644 index 00000000..0dece75e --- /dev/null +++ b/ui/src/preferences/system-resources/SystemResourcePrefs.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { IdentityPrefs } from './IdentityPrefs'; +import { P2PServicePrefs } from './P2PServicePrefs'; +import { LoomPrefs } from './LoomPrefs'; + +export const SystemResourcePrefs = () => ( + <> + + + + +); diff --git a/ui/src/state/azimuth.ts b/ui/src/state/azimuth.ts index 42c3f4b1..6b59caab 100644 --- a/ui/src/state/azimuth.ts +++ b/ui/src/state/azimuth.ts @@ -1,44 +1,48 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useEffect } from 'react'; +import { useAsyncCall } from '../logic/useAsyncCall'; import { fakeRequest } from './util'; const api = { forceUpdate() { return fakeRequest(null, 1000); }, - async getAzimuthBlock() { - return fakeRequest('16.514.728', 1000); + async getAzimuthState() { + return fakeRequest( + { + block: '16.514.728', + stale: false, + }, + 1000 + ); }, }; -export const useAzimuthBlock = () => { - const [isLoading, setIsLoading] = useState(false); - const [isStale, setIsStale] = useState(true); - const [block, setBlock] = useState(null); +export const useAzimuthState = () => { + const { + status: stateLoadStatus, + call: loadState, + error: stateLoadError, + result: state, + } = useAsyncCall(api.getAzimuthState); - const loadBlock = useCallback(async () => { - setIsLoading(true); - const block = await api.getAzimuthBlock(); - setBlock(block); - setIsLoading(false); - }, []); - - const forceUpdate = useCallback(async () => { - setIsLoading(true); - setIsStale(false); - await api.forceUpdate(); - setIsLoading(false); - }, []); + const { + status: forceUpdateStatus, + call: forceUpdate, + error: forceUpdateError, + } = useAsyncCall(api.forceUpdate); + // attempt to load initial State on mount useEffect(() => { - // attempt to load initial block on mount - loadBlock(); + loadState(); }, []); return { - isLoading, - isStale, - block, + state, + stateLoadStatus, + stateLoadError, forceUpdate, + forceUpdateStatus, + forceUpdateError, }; }; []; diff --git a/ui/src/state/connectivity.ts b/ui/src/state/connectivity.ts index 9111d4cb..11944a95 100644 --- a/ui/src/state/connectivity.ts +++ b/ui/src/state/connectivity.ts @@ -1,10 +1,13 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useEffect } from 'react'; +import { useAsyncCall } from '../logic/useAsyncCall'; import { fakeRequest } from './util'; export type AvailabilityStatus = 'initial' | 'available' | 'unavailable'; const api = { - checkShipAvailability(shipName: string) { + checkShipAvailability( + shipName: string + ): Promise<{ status: AvailabilityStatus }> { return fakeRequest({ status: 'available', }); @@ -14,49 +17,32 @@ const api = { }, }; -const checkShipAvailability = async ( - shipName: string -): Promise<{ status: AvailabilityStatus }> => { - return fakeRequest({ - status: 'available', - }); -}; - export const useShipAvailability = (shipName: string) => { - const [availabilityStatus, setAvailabilityStatus] = - useState('initial'); - const [isChecking, setIsChecking] = useState(false); - - const checkAvailability = useCallback(async () => { - setIsChecking(true); - const { status } = await checkShipAvailability(shipName); - setAvailabilityStatus(status); - setIsChecking(false); - }, []); + const { + call: checkAvailability, + result: availability, + status, + error, + } = useAsyncCall(api.checkShipAvailability); useEffect(() => { - checkAvailability(); + checkAvailability(shipName); }, []); - return { isChecking, availabilityStatus, refresh: checkAvailability }; + return { status, availability, error, checkAvailability }; }; export const usePeerDiscoveryShips = () => { - const [isLoading, setIsLoading] = useState(false); - const [peerDiscoveryShips, setPeerDiscoverShips] = useState( - null - ); - - const getShips = useCallback(async () => { - setIsLoading(true); - const ships = await api.getPeerDiscoveryShips(); - setPeerDiscoverShips(ships); - setIsLoading(false); - }, []); + const { + call: getShips, + result: peerDiscoveryShips, + status, + error, + } = useAsyncCall(api.getPeerDiscoveryShips); useEffect(() => { getShips(); }, []); - return { isLoading, peerDiscoveryShips, getShips }; + return { status, error, peerDiscoveryShips, getShips }; }; diff --git a/ui/src/state/invites.ts b/ui/src/state/invites.ts index a0c95756..9b9b2eb9 100644 --- a/ui/src/state/invites.ts +++ b/ui/src/state/invites.ts @@ -3,23 +3,25 @@ import { useState, useEffect } from 'react'; export default function useInviteState() { const [baitURL, setBaitURL] = useState(''); - const [loaded, setLoaded] = useState(false) + const [loaded, setLoaded] = useState(false); useEffect(() => { - api.scry<{url: string}>({ - app: 'reel', - path: '/bait' - }).then(({url}) => { - setBaitURL(url); - setLoaded(true); - }); + api + .scry<{ url: string }>({ + app: 'reel', + path: '/bait', + }) + .then(({ url }) => { + setBaitURL(url); + setLoaded(true); + }); }, []); return { baitURL: baitURL, setBaitURL: setBaitURL, loaded: loaded, - save: async (data: {url: string}) => { + save: async (data: { url: string }) => { const fixedUrl = data.url.substr(-1) === '/' ? data.url : data.url + '/'; await api.poke({ @@ -27,8 +29,8 @@ export default function useInviteState() { mark: 'reel-command', json: { url: fixedUrl, - } - }) - } - } + }, + }); + }, + }; } diff --git a/ui/src/state/loom.ts b/ui/src/state/loom.ts new file mode 100644 index 00000000..06dd896f --- /dev/null +++ b/ui/src/state/loom.ts @@ -0,0 +1,24 @@ +import { useAsyncCall } from '../logic/useAsyncCall'; +import { fakeRequest } from './util'; + +const pack = () => { + return fakeRequest(null, 1000); +}; + +export const useLoom = () => { + const { + call: runPack, + status: isPacking, + error: packError, + } = useAsyncCall(() => { + return pack(); + }); + + return { + free: 3000000, + total: 4096000, + pack: runPack, + isPacking, + packError, + }; +};