diff --git a/pkg/interface/config/webpack.dev.js b/pkg/interface/config/webpack.dev.js index 66bf83ce9..0f85b58a9 100644 --- a/pkg/interface/config/webpack.dev.js +++ b/pkg/interface/config/webpack.dev.js @@ -94,7 +94,11 @@ module.exports = { use: { loader: 'babel-loader', options: { - presets: ['@babel/preset-env', '@babel/typescript', '@babel/preset-react'], + presets: ['@babel/preset-env', '@babel/typescript', ['@babel/preset-react', { + runtime: 'automatic', + development: true, + importSource: '@welldone-software/why-did-you-render', + }]], plugins: [ '@babel/transform-runtime', '@babel/plugin-proposal-object-rest-spread', diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json index 61b37093d..0c5535474 100644 --- a/pkg/interface/package-lock.json +++ b/pkg/interface/package-lock.json @@ -1995,6 +1995,15 @@ "@xtuc/long": "4.2.2" } }, + "@welldone-software/why-did-you-render": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.1.0.tgz", + "integrity": "sha512-0s+PuKQ4v9VV1SZSM6iS7d2T7X288T3DF+K8yfkFAhI31HhJGGH1SY1ssVm+LqjSMyrVWT60ZF5r0qUsO0Z9Lw==", + "dev": true, + "requires": { + "lodash": "^4" + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", diff --git a/pkg/interface/package.json b/pkg/interface/package.json index 8b8c7853f..566510cca 100644 --- a/pkg/interface/package.json +++ b/pkg/interface/package.json @@ -71,6 +71,7 @@ "@types/yup": "^0.29.11", "@typescript-eslint/eslint-plugin": "^4.15.0", "@urbit/eslint-config": "file:../npm/eslint-config", + "@welldone-software/why-did-you-render": "^6.1.0", "babel-eslint": "^10.1.0", "babel-loader": "^8.2.2", "babel-plugin-lodash": "^3.3.4", diff --git a/pkg/interface/src/index.js b/pkg/interface/src/index.js index ded85a8f5..e62b46b2f 100644 --- a/pkg/interface/src/index.js +++ b/pkg/interface/src/index.js @@ -1,3 +1,4 @@ +import './wdyr'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; diff --git a/pkg/interface/src/logic/api/metadata.ts b/pkg/interface/src/logic/api/metadata.ts index c2d388dfc..cae4fc6d6 100644 --- a/pkg/interface/src/logic/api/metadata.ts +++ b/pkg/interface/src/logic/api/metadata.ts @@ -77,7 +77,6 @@ export default class MetadataApi extends BaseApi { tempChannel.delete(); }, (ev: any) => { - console.log(ev); if ('metadata-hook-update' in ev) { done = true; tempChannel.delete(); diff --git a/pkg/interface/src/logic/lib/sigil.js b/pkg/interface/src/logic/lib/sigil.js index f2444068e..e47eff93f 100644 --- a/pkg/interface/src/logic/lib/sigil.js +++ b/pkg/interface/src/logic/lib/sigil.js @@ -23,9 +23,10 @@ export const Sigil = memo( size, svgClass = '', icon = false, - padding = 0 + padding = 0, + display = 'inline-block' }) => { - const innerSize = Number(size) - 2*padding; + const innerSize = Number(size) - 2 * padding; const paddingPx = `${padding}px`; const foregroundColor = foreground ? foreground @@ -34,14 +35,14 @@ export const Sigil = memo( ) : ( data.time); + state = updateNotificationStats(state, data.index, 'last', () => data.time); } return state; } function readEach(json: any, state: HarkState): HarkState { const data = _.get(json, 'read-each'); - if(data) { - updateUnreads(state, data.index, u => u.delete(data.target)); + if (data) { + state = updateUnreads(state, data.index, u => u.delete(data.target)); } return state; } @@ -165,7 +165,7 @@ function readEach(json: any, state: HarkState): HarkState { function readSince(json: any, state: HarkState): HarkState { const data = _.get(json, 'read-count'); if(data) { - updateUnreadCount(state, data, () => 0); + state = updateUnreadCount(state, data, () => 0); } return state; } @@ -173,7 +173,7 @@ function readSince(json: any, state: HarkState): HarkState { function unreadSince(json: any, state: HarkState): HarkState { const data = _.get(json, 'unread-count'); if(data) { - updateUnreadCount(state, data.index, u => u + 1); + state = updateUnreadCount(state, data.index, u => u + 1); } return state; } @@ -181,7 +181,7 @@ function unreadSince(json: any, state: HarkState): HarkState { function unreadEach(json: any, state: HarkState): HarkState { const data = _.get(json, 'unread-each'); if(data) { - updateUnreads(state, data.index, us => us.add(data.target)); + state = updateUnreads(state, data.index, us => us.add(data.target)); } return state; } @@ -191,13 +191,14 @@ function unreads(json: any, state: HarkState): HarkState { if(data) { data.forEach(({ index, stats }) => { const { unreads, notifications, last } = stats; - updateNotificationStats(state, index, 'notifications', x => x + notifications); - updateNotificationStats(state, index, 'last', () => last); + state = updateNotificationStats(state, index, 'notifications', x => x + notifications); + state = updateNotificationStats(state, index, 'last', () => last); if('count' in unreads) { - updateUnreadCount(state, index, (u = 0) => u + unreads.count); + state = updateUnreadCount(state, index, (u = 0) => u + unreads.count); } else { + state = updateUnreads(state, index, s => new Set()); unreads.each.forEach((u: string) => { - updateUnreads(state, index, s => s.add(u)); + state = updateUnreads(state, index, s => s.add(u)); }); } }); @@ -205,7 +206,7 @@ function unreads(json: any, state: HarkState): HarkState { return state; } -function clearState(state: HarkState) { +function clearState(state: HarkState): HarkState { const initialState = { notifications: new BigIntOrderedMap(), archivedNotifications: new BigIntOrderedMap(), @@ -225,6 +226,7 @@ function clearState(state: HarkState) { Object.keys(initialState).forEach((key) => { state[key] = initialState[key]; }); + return state; } function updateUnreadCount(state: HarkState, index: NotifIndex, count: (c: number) => number): HarkState { @@ -242,10 +244,9 @@ function updateUnreads(state: HarkState, index: NotifIndex, f: (us: Set) if(!('graph' in index)) { return state; } - const unreads = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], new Set()); - const oldSize = unreads.size; + let unreads = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], new Set()); f(unreads); - const newSize = unreads.size; + _.set(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], unreads); return state; } @@ -254,7 +255,7 @@ function updateNotificationStats(state: HarkState, index: NotifIndex, statField: if(statField === 'notifications') { state.notificationsCount = f(state.notificationsCount); } - if('graph' in index) { + if ('graph' in index) { const curr = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, statField], 0); _.set(state.unreads.graph, [index.graph.graph, index.graph.index, statField], f(curr)); } else if('group' in index) { @@ -276,7 +277,6 @@ function added(json: any, state: HarkState): HarkState { ); if (arrIdx !== -1) { if (timebox[arrIdx]?.notification?.read) { - // TODO this is additive, and with a persistent state it keeps incrementing state = updateNotificationStats(state, index, 'notifications', x => x+1); } timebox[arrIdx] = { index, notification }; @@ -361,7 +361,7 @@ function read(json: any, state: HarkState): HarkState { const data = _.get(json, 'read-note', false); if (data) { const { time, index } = data; - updateNotificationStats(state, index, 'notifications', x => x-1); + state = updateNotificationStats(state, index, 'notifications', x => x-1); setRead(time, index, true, state); } return state; @@ -371,7 +371,7 @@ function unread(json: any, state: HarkState): HarkState { const data = _.get(json, 'unread-note', false); if (data) { const { time, index } = data; - updateNotificationStats(state, index, 'notifications', x => x+1); + state = updateNotificationStats(state, index, 'notifications', x => x+1); setRead(time, index, false, state); } return state; @@ -397,7 +397,7 @@ function archive(json: any, state: HarkState): HarkState { state.notifications.set(time, unarchived); } const newlyRead = archived.filter(x => !x.notification.read).length; - updateNotificationStats(state, index, 'notifications', x => x - newlyRead); + state = updateNotificationStats(state, index, 'notifications', x => x - newlyRead); } return state; } diff --git a/pkg/interface/src/logic/state/base.ts b/pkg/interface/src/logic/state/base.ts index e1add80e3..72af05e92 100644 --- a/pkg/interface/src/logic/state/base.ts +++ b/pkg/interface/src/logic/state/base.ts @@ -34,7 +34,7 @@ export const reduceState = < export let stateStorageKeys: string[] = []; export const stateStorageKey = (stateName: string) => { - stateName = `Landcape${stateName}State`; + stateName = `Landscape${stateName}State`; stateStorageKeys = [...new Set([...stateStorageKeys, stateName])]; return stateName; }; diff --git a/pkg/interface/src/views/apps/chat/components/ChatInput.tsx b/pkg/interface/src/views/apps/chat/components/ChatInput.tsx index 65803ebc3..8e9cfef29 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatInput.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatInput.tsx @@ -24,7 +24,7 @@ type ChatInputProps = IuseStorage & { message: string; deleteMessage(): void; hideAvatars: boolean; -} +}; interface ChatInputState { inCodeMode: boolean; @@ -60,20 +60,23 @@ class ChatInput extends Component { submit(text) { const { props, state } = this; - const [,,ship,name] = props.station.split('/'); + const [, , ship, name] = props.station.split('/'); if (state.inCodeMode) { - this.setState({ - inCodeMode: false - }, async () => { - const output = await props.api.graph.eval(text); - const contents: Content[] = [{ code: { output, expression: text } }]; - const post = createPost(contents); - props.api.graph.addPost(ship, name, post); - }); + this.setState( + { + inCodeMode: false + }, + async () => { + const output = await props.api.graph.eval(text); + const contents: Content[] = [{ code: { output, expression: text } }]; + const post = createPost(contents); + props.api.graph.addPost(ship, name, post); + } + ); return; } - const post = createPost(tokenizeMessage((text))); + const post = createPost(tokenizeMessage(text)); props.deleteMessage(); @@ -86,8 +89,8 @@ class ChatInput extends Component { this.chatEditor.current.editor.setValue(url); this.setState({ uploadingPaste: false }); } else { - const [,,ship,name] = props.station.split('/'); - props.api.graph.addPost(ship,name, createPost([{ url }])); + const [, , ship, name] = props.station.split('/'); + props.api.graph.addPost(ship, name, createPost([{ url }])); } } @@ -110,7 +113,8 @@ class ChatInput extends Component { return; } Array.from(files).forEach((file) => { - this.props.uploadDefault(file) + this.props + .uploadDefault(file) .then(this.uploadSuccess) .catch(this.uploadError); }); @@ -119,32 +123,40 @@ class ChatInput extends Component { render() { const { props, state } = this; - const color = props.ourContact - ? uxToHex(props.ourContact.color) : '000000'; + const color = props.ourContact ? uxToHex(props.ourContact.color) : '000000'; - const sigilClass = props.ourContact - ? '' : 'mix-blend-diff'; + const sigilClass = props.ourContact ? '' : 'mix-blend-diff'; - const avatar = ( - props.ourContact && - ((props.ourContact?.avatar) && !props.hideAvatars) - ) - ? - : ; + ) : ( + + + + ); return ( { className='cf' zIndex={0} > - + {avatar} { onPaste={this.onPaste.bind(this)} placeholder='Message...' /> - - {this.props.canUpload - ? this.props.uploading - ? - : this.props.promptUpload().then(this.uploadSuccess)} - /> - : null - } + + {this.props.canUpload ? ( + this.props.uploading ? ( + + ) : ( + + this.props.promptUpload().then(this.uploadSuccess) + } + /> + ) + ) : null} - + { } } -export default withLocalState(withStorage(ChatInput, { accept: 'image/*' }), ['hideAvatars']); +export default withLocalState(withStorage(ChatInput, { accept: 'image/*' }), [ + 'hideAvatars' +]); diff --git a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx index c3359bdf5..18aee5057 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx @@ -10,7 +10,7 @@ import React, { import moment from 'moment'; import _ from 'lodash'; import VisibilitySensor from 'react-visibility-sensor'; -import { Box, Row, Text, Rule, BaseImage } from '@tlon/indigo-react'; +import { Box, Row, Text, Rule, BaseImage, Icon, Col } from '@tlon/indigo-react'; import { Sigil } from '~/logic/lib/sigil'; import OverlaySigil from '~/views/components/OverlaySigil'; import { @@ -33,12 +33,13 @@ import TextContent from './content/text'; import CodeContent from './content/code'; import RemoteContent from '~/views/components/RemoteContent'; import { Mention } from '~/views/components/MentionText'; +import { Dropdown } from '~/views/components/Dropdown'; import styled from 'styled-components'; import useLocalState from '~/logic/state/local'; -import useSettingsState, {selectCalmState} from "~/logic/state/settings"; +import useSettingsState, { selectCalmState } from '~/logic/state/settings'; import Timestamp from '~/views/components/Timestamp'; import useContactState from '~/logic/state/contact'; -import {useIdlingState} from '~/logic/lib/idling'; +import { useIdlingState } from '~/logic/lib/idling'; export const DATESTAMP_FORMAT = '[~]YYYY.M.D'; @@ -64,39 +65,156 @@ export const DayBreak = ({ when, shimTop = false }: DayBreakProps) => ( ); -export const UnreadMarker = React.forwardRef(({ dayBreak, when, api, association }, ref) => { - const [visible, setVisible] = useState(false); - const idling = useIdlingState(); - const dismiss = useCallback(() => { - api.hark.markCountAsRead(association, '/', 'message'); - }, [api, association]); +export const UnreadMarker = React.forwardRef( + ({ dayBreak, when, api, association }, ref) => { + const [visible, setVisible] = useState(false); + const idling = useIdlingState(); + const dismiss = useCallback(() => { + api.hark.markCountAsRead(association, '/', 'message'); + }, [api, association]); - useEffect(() => { - if(visible && !idling) { - dismiss(); - } - }, [visible, idling]); + useEffect(() => { + if (visible && !idling) { + dismiss(); + } + }, [visible, idling]); + return ( + + + + + New messages below + + + + + ); + } +); + +const MessageActionItem = (props) => { return ( - - - - - New messages below - - - - -)}); + + + {props.children} + + + ); +}; + +const MessageActions = ({ api, history, msg, group }) => { + const isAdmin = () => group.tags.role.admin.has(window.ship); + const isOwn = () => msg.author === window.ship; + return ( + + + {isOwn() ? ( + console.log(e)} + > + + + ) : null} + console.log(e)} + > + + + + {isOwn() ? ( + console.log(e)}> + Edit Message + + ) : null} + console.log(e)}> + Reply + + console.log(e)}> + Copy Message Link + + {isAdmin() || isOwn() ? ( + console.log(e)} color='red'> + Delete Message + + ) : null} + console.log(e)}> + View Signature + + + } + > + + + + + + + ); +}; + +const MessageWrapper = (props) => { + const { hovering, bind } = useHovering(); + return ( + + {props.children} + {/* {hovering ? : null} */} + + ); +}; interface ChatMessageProps { msg: Post; @@ -126,8 +244,7 @@ class ChatMessage extends Component { this.divRef = React.createRef(); } - componentDidMount() { - } + componentDidMount() {} render() { const { @@ -146,7 +263,7 @@ class ChatMessage extends Component { history, api, highlighted, - fontSize, + fontSize } = this.props; let { renderSigil } = this.props; @@ -170,7 +287,6 @@ class ChatMessage extends Component { .unix(msg['time-sent'] / 1000) .format(renderSigil ? 'h:mm A' : 'h:mm'); - const messageProps = { msg, timestamp, @@ -183,7 +299,7 @@ class ChatMessage extends Component { api, scrollWindow, highlighted, - fontSize, + fontSize }; const unreadContainerStyle = { @@ -194,7 +310,7 @@ class ChatMessage extends Component { { ) : null} {renderSigil ? ( - <> - - - + + + + ) : ( - + + + )} {isLastRead ? ( @@ -226,7 +344,9 @@ class ChatMessage extends Component { } } -export default React.forwardRef((props, ref) => ); +export default React.forwardRef((props, ref) => ( + +)); export const MessageAuthor = ({ timestamp, @@ -239,9 +359,9 @@ export const MessageAuthor = ({ }) => { const osDark = useLocalState((state) => state.dark); - const theme = useSettingsState(s => s.display.theme); + const theme = useSettingsState((s) => s.display.theme); const dark = theme === 'dark' || (theme === 'auto' && osDark); - const contacts = useContactState(state => state.contacts); + const contacts = useContactState((state) => state.contacts); const datestamp = moment .unix(msg['time-sent'] / 1000) @@ -291,19 +411,30 @@ export const MessageAuthor = ({ display='inline-block' style={{ objectFit: 'cover' }} src={contact.avatar} - height={16} - width={16} + height={24} + width={24} borderRadius={1} /> ) : ( - + + + ); return ( @@ -311,9 +442,9 @@ export const MessageAuthor = ({ onClick={() => { setShowOverlay(true); }} - height={16} + height={24} pr={2} - pl={2} + pl={'12px'} cursor='pointer' position='relative' > @@ -340,10 +471,10 @@ export const MessageAuthor = ({ pt={1} pb={1} display='flex' - alignItems='center' + alignItems='baseline' > { const { hovering, bind } = useHovering(); - const contacts = useContactState(state => state.contacts); + const contacts = useContactState((state) => state.contacts); return ( {timestampHover ? ( ); case 'code': - return ; + return ; case 'url': return ( ); case 'mention': - const first = (i) => (i === 0); + const first = (i) => i === 0; return ( { }, []); return lines.map((line, i) => ( - <> + {i !== 0 && } { ] ]} /> - + )); }); diff --git a/pkg/interface/src/views/apps/launch/app.js b/pkg/interface/src/views/apps/launch/app.js index dda418ddf..4ea158cad 100644 --- a/pkg/interface/src/views/apps/launch/app.js +++ b/pkg/interface/src/views/apps/launch/app.js @@ -32,6 +32,7 @@ import { } from '~/logic/lib/tutorialModal'; import useLaunchState from '~/logic/state/launch'; import useSettingsState, { selectCalmState } from '~/logic/state/settings'; +import useMetadataState from '~/logic/state/metadata'; const ScrollbarLessBox = styled(Box)` @@ -48,6 +49,7 @@ export default function LaunchApp(props) { const baseHash = useLaunchState(state => state.baseHash); const [hashText, setHashText] = useState(baseHash); const [exitingTut, setExitingTut] = useState(false); + const associations = useMetadataState(s => s.associations); const hashBox = ( { if(query.get('tutorial')) { - if(hasTutorialGroup(props)) { + if(hasTutorialGroup({ associations })) { nextTutStep(); } else { showModal(); @@ -92,7 +94,7 @@ export default function LaunchApp(props) { let { hideGroups } = useLocalState(tutSelector); !hideGroups ? { hideGroups } = calmState : null; - const waiter = useWaitForProps(props); + const waiter = useWaitForProps({ ...props, associations }); const { modal, showModal } = useModal({ position: 'relative', @@ -105,7 +107,7 @@ export default function LaunchApp(props) { }; const onContinue = async (e) => { e.stopPropagation(); - if(!hasTutorialGroup(props)) { + if(!hasTutorialGroup({ associations })) { await props.api.groups.join(TUTORIAL_HOST, TUTORIAL_GROUP); await props.api.settings.putEntry('tutorial', 'joined', Date.now()); await waiter(hasTutorialGroup); diff --git a/pkg/interface/src/views/apps/links/components/LinkItem.tsx b/pkg/interface/src/views/apps/links/components/LinkItem.tsx index d1922b0f3..9579f4f54 100644 --- a/pkg/interface/src/views/apps/links/components/LinkItem.tsx +++ b/pkg/interface/src/views/apps/links/components/LinkItem.tsx @@ -148,17 +148,13 @@ export const LinkItem = (props: LinkItemProps): ReactElement => { - - - + /> diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index a8b774383..d59bd98a8 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -85,7 +85,7 @@ export function ProfileHeaderImageEdit(props: any): ReactElement { export function EditProfile(props: any): ReactElement { const { contact, ship, api } = props; - const isPublic = useContactState(state => state.isContactPublic); + const isPublic = useContactState((state) => state.isContactPublic); const [hideCover, setHideCover] = useState(false); const handleHideCover = (value) => { @@ -150,7 +150,7 @@ export function EditProfile(props: any): ReactElement {
- + - - - + + + S3 Buckets diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx index 0fb9d94d3..b2ae9f855 100644 --- a/pkg/interface/src/views/apps/settings/settings.tsx +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -1,35 +1,42 @@ -import React, { ReactNode } from "react"; -import { useLocation } from "react-router-dom"; -import Helmet from "react-helmet"; +import React, { ReactNode } from 'react'; +import { useLocation } from 'react-router-dom'; +import Helmet from 'react-helmet'; import { Text, Box, Col, Row } from '@tlon/indigo-react'; -import { NotificationPreferences } from "./components/lib/NotificationPref"; -import DisplayForm from "./components/lib/DisplayForm"; -import S3Form from "./components/lib/S3Form"; -import { CalmPrefs } from "./components/lib/CalmPref"; -import SecuritySettings from "./components/lib/Security"; -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"; +import { NotificationPreferences } from './components/lib/NotificationPref'; +import DisplayForm from './components/lib/DisplayForm'; +import S3Form from './components/lib/S3Form'; +import { CalmPrefs } from './components/lib/CalmPref'; +import SecuritySettings from './components/lib/Security'; +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 }) => ( - + {props.children} ); -type ProvSideProps = "to" | "selected"; +type ProvSideProps = 'to' | 'selected'; type BaseProps = PropFunc; function SidebarItem(props: { hash: string } & Omit) { const { hash, icon, text, ...rest } = props; @@ -54,16 +61,15 @@ function SettingsItem(props: { children: ReactNode }) { const { children } = props; return ( - + {children} ); } export default function SettingsScreen(props: any) { - const location = useLocation(); - const hash = location.hash.slice(1) + const hash = location.hash.slice(1); return ( <> @@ -71,68 +77,49 @@ export default function SettingsScreen(props: any) { Landscape - Settings - - - - System Preferences - - - - - - - - - + + + System Preferences + + + + + + + + - - - {hash === "notifications" && ( - - )} - {hash === "display" && ( - - )} - {hash === "s3" && ( - - )} - {hash === "leap" && ( - - )} - {hash === "calm" && ( - - )} - {hash === "security" && ( - - )} - - - + + + + {hash === 'notifications' && ( + + )} + {hash === 'display' && } + {hash === 's3' && } + {hash === 'leap' && } + {hash === 'calm' && } + {hash === 'security' && } + + ); diff --git a/pkg/interface/src/views/components/Author.tsx b/pkg/interface/src/views/components/Author.tsx index 33fc3df54..063061a91 100644 --- a/pkg/interface/src/views/components/Author.tsx +++ b/pkg/interface/src/views/components/Author.tsx @@ -11,7 +11,6 @@ import useSettingsState, {selectCalmState} from "~/logic/state/settings"; import useLocalState from "~/logic/state/local"; import OverlaySigil from './OverlaySigil'; import { Sigil } from '~/logic/lib/sigil'; -import GlobalApi from '~/logic/api/global'; import Timestamp from './Timestamp'; import useContactState from '~/logic/state/contact'; @@ -22,7 +21,6 @@ interface AuthorProps { children?: ReactNode; unread?: boolean; group: Group; - api?: GlobalApi; } // eslint-disable-next-line max-lines-per-function diff --git a/pkg/interface/src/views/components/CommentItem.tsx b/pkg/interface/src/views/components/CommentItem.tsx index c0054c3f5..50493f6f6 100644 --- a/pkg/interface/src/views/components/CommentItem.tsx +++ b/pkg/interface/src/views/components/CommentItem.tsx @@ -10,6 +10,7 @@ import { Group } from '@urbit/api'; import GlobalApi from '~/logic/api/global'; import Author from '~/views/components/Author'; import { MentionText } from '~/views/components/MentionText'; +import { roleForShip } from '~/logic/lib/group'; import { getLatestCommentRevision } from '~/logic/lib/publish'; const ClickBox = styled(Box)` @@ -31,7 +32,7 @@ interface CommentItemProps { export function CommentItem(props: CommentItemProps): ReactElement { const { ship, name, api, comment, group } = props; const [, post] = getLatestCommentRevision(comment); - const disabled = props.pending || window.ship !== post?.author; + const disabled = props.pending; const onDelete = async () => { await api.graph.removeNodes(ship, name, [comment.post?.index]); @@ -41,6 +42,29 @@ export function CommentItem(props: CommentItemProps): ReactElement { const commentIndex = commentIndexArray[commentIndexArray.length - 1]; const updateUrl = `${props.baseUrl}/${commentIndex}`; + const adminLinks: JSX.Element[] = []; + const ourRole = roleForShip(group, window.ship); + if (window.ship == post?.author && !disabled) { + adminLinks.push( + + + Update + + + ) + }; + + if ((window.ship == post?.author || ourRole == "admin") && !disabled) { + adminLinks.push( + + Delete + + ) + }; + return ( @@ -50,23 +74,10 @@ export function CommentItem(props: CommentItemProps): ReactElement { date={post?.['time-sent']} unread={props.unread} group={group} - api={api} > - {!disabled && ( - - - - Update - - - - Delete - - - )} + + {adminLinks} + diff --git a/pkg/interface/src/views/components/RemoteContent.tsx b/pkg/interface/src/views/components/RemoteContent.tsx index 39ddb0e3f..33fda99da 100644 --- a/pkg/interface/src/views/components/RemoteContent.tsx +++ b/pkg/interface/src/views/components/RemoteContent.tsx @@ -51,7 +51,6 @@ class RemoteContent extends Component { } save = () => { - console.log(`saving for: ${this.props.url}`); if(this.saving) { return; } @@ -60,7 +59,6 @@ class RemoteContent extends Component { }; restore = () => { - console.log(`restoring for: ${this.props.url}`); this.saving = false; this.props.restore(); } diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 82250028c..478d98d22 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -9,6 +9,7 @@ import { Button, BaseImage } from '@tlon/indigo-react'; + import ReconnectButton from './ReconnectButton'; import { Dropdown } from './Dropdown'; import { StatusBarItem } from './StatusBarItem'; @@ -19,6 +20,7 @@ import { useTutorialModal } from './useTutorialModal'; import useHarkState from '~/logic/state/hark'; import useInviteState from '~/logic/state/invite'; +import useContactState from '~/logic/state/contact'; import { useHistory } from 'react-router-dom'; import useLocalState, { selectLocalState } from '~/logic/state/local'; import useSettingsState, { selectCalmState } from '~/logic/state/settings'; @@ -26,8 +28,9 @@ import useSettingsState, { selectCalmState } from '~/logic/state/settings'; const localSel = selectLocalState(['toggleOmnibox']); const StatusBar = (props) => { - const { ourContact, api, ship } = props; + const { api, ship } = props; const history = useHistory(); + const ourContact = useContactState((state) => state.contacts[`~${ship}`]); const notificationsCount = useHarkState((state) => state.notificationsCount); const doNotDisturb = useHarkState((state) => state.doNotDisturb); const inviteState = useInviteState((state) => state.invites); @@ -38,7 +41,7 @@ const StatusBar = (props) => { const { toggleOmnibox } = useLocalState(localSel); const { hideAvatars } = useSettingsState(selectCalmState); - const color = !!ourContact ? `#${uxToHex(props.ourContact.color)}` : '#000'; + const color = !!ourContact ? `#${uxToHex(ourContact.color)}` : '#000'; const xPadding = !hideAvatars && ourContact?.avatar ? '0' : '2'; const bgColor = !hideAvatars && ourContact?.avatar ? '' : color; const profileImage = diff --git a/pkg/interface/src/views/components/UnjoinedResource.tsx b/pkg/interface/src/views/components/UnjoinedResource.tsx index ab14aed2d..8e9156a7c 100644 --- a/pkg/interface/src/views/components/UnjoinedResource.tsx +++ b/pkg/interface/src/views/components/UnjoinedResource.tsx @@ -9,15 +9,13 @@ import { StatelessAsyncButton as AsyncButton, StatelessAsyncButton } from './StatelessAsyncButton'; -import { Notebooks, Graphs, Inbox } from '@urbit/api'; +import { Graphs } from '@urbit/api'; import useGraphState from '~/logic/state/graph'; interface UnjoinedResourceProps { association: Association; api: GlobalApi; baseUrl: string; - notebooks: Notebooks; - inbox: Inbox; } function isJoined(path: string) { @@ -31,7 +29,7 @@ function isJoined(path: string) { } export function UnjoinedResource(props: UnjoinedResourceProps) { - const { api, notebooks, inbox } = props; + const { api } = props; const history = useHistory(); const rid = props.association.resource; const appName = props.association['app-name']; @@ -52,7 +50,7 @@ export function UnjoinedResource(props: UnjoinedResourceProps) { if (isJoined(rid)({ graphKeys })) { history.push(`${props.baseUrl}/resource/${app}${rid}`); } - }, [props.association, inbox, graphKeys, notebooks]); + }, [props.association, graphKeys]); return (
diff --git a/pkg/interface/src/views/components/VirtualScroller.tsx b/pkg/interface/src/views/components/VirtualScroller.tsx index f36fd0334..588633418 100644 --- a/pkg/interface/src/views/components/VirtualScroller.tsx +++ b/pkg/interface/src/views/components/VirtualScroller.tsx @@ -40,7 +40,7 @@ interface VirtualScrollerProps { data: BigIntOrderedMap; /** * The component to render the items - * + * * @remarks * * This component must be referentially stable, so either use `useCallback` or @@ -157,7 +157,7 @@ export default class VirtualScroller extends Component extends Component {!IS_IOS && ( { this.scrollRef = el; }} right="0" height="50px" position="absolute" width="4px" backgroundColor="lightGray" />)} - + {(isTop ? !atStart : !atEnd) && (
diff --git a/pkg/interface/src/views/components/useTutorialModal.tsx b/pkg/interface/src/views/components/useTutorialModal.tsx index e9a2162ad..94aad20be 100644 --- a/pkg/interface/src/views/components/useTutorialModal.tsx +++ b/pkg/interface/src/views/components/useTutorialModal.tsx @@ -16,9 +16,7 @@ export function useTutorialModal( setTutorialRef(anchorRef.current); } - return () => { - console.log(tutorialProgress); - } + return () => {} }, [tutorialProgress, show, anchorRef]); return show && onProgress === tutorialProgress; diff --git a/pkg/interface/src/views/css/fonts.css b/pkg/interface/src/views/css/fonts.css index 9880e92de..1b50126e0 100644 --- a/pkg/interface/src/views/css/fonts.css +++ b/pkg/interface/src/views/css/fonts.css @@ -10,16 +10,14 @@ font-family: 'Inter'; font-style: normal; font-weight: 500; - src: url("/~landscape/fonts/inter-medium.woff2") format("woff2"), - url("https://media.urbit.org/fonts/Inter-Medium.woff2") format("woff2"); + src: url("https://media.urbit.org/fonts/Inter-Medium.woff2") format("woff2"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; - src: url("/~landscape/fonts/inter-semibold.woff2") format("woff2"), - url("https://media.urbit.org/fonts/Inter-SemiBold.woff2") format("woff2"); + src: url("https://media.urbit.org/fonts/Inter-SemiBold.woff2") format("woff2"); } @font-face { diff --git a/pkg/interface/src/views/landscape/components/GroupsPane.tsx b/pkg/interface/src/views/landscape/components/GroupsPane.tsx index 9f66d516e..658f0e599 100644 --- a/pkg/interface/src/views/landscape/components/GroupsPane.tsx +++ b/pkg/interface/src/views/landscape/components/GroupsPane.tsx @@ -158,8 +158,6 @@ export function GroupsPane(props: GroupsPaneProps) { baseUrl={baseUrl} > { - const hasDescription = groupAssociation?.metadata?.description; - const channelCount = Object.keys(props?.associations?.graph ?? {}).filter((e) => { - return props?.associations?.graph?.[e]?.['group'] === groupPath; + const channelCount = Object.keys(associations?.graph ?? {}).filter((e) => { + return associations?.graph?.[e]?.['group'] === groupPath; }).length; let summary: ReactNode; if(groupAssociation?.group) { diff --git a/pkg/interface/src/views/landscape/components/MetadataIcon.tsx b/pkg/interface/src/views/landscape/components/MetadataIcon.tsx index e4ab45d63..97ef54426 100644 --- a/pkg/interface/src/views/landscape/components/MetadataIcon.tsx +++ b/pkg/interface/src/views/landscape/components/MetadataIcon.tsx @@ -15,7 +15,7 @@ export function MetadataIcon(props: MetadataIconProps) { const bgColor = metadata.picture ? {} : { bg: `#${uxToHex(metadata.color)}` }; return ( - + {metadata.picture && } ); diff --git a/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx b/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx index 62d2103af..ceb741a0e 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx @@ -37,7 +37,6 @@ interface SidebarProps { api: GlobalApi; selected?: string; selectedGroup?: string; - includeUnmanaged?: boolean; apps: SidebarAppConfigs; baseUrl: string; mobileHide?: boolean; diff --git a/pkg/interface/src/views/landscape/components/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/SidebarItem.tsx index 667ed2fed..74cacbff8 100644 --- a/pkg/interface/src/views/landscape/components/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/SidebarItem.tsx @@ -30,7 +30,7 @@ export const SidebarItem = ({ bgActive="washedGray" display="flex" px="3" - py="1" + py="2" justifyContent="space-between" {...rest} > diff --git a/pkg/interface/src/views/landscape/components/Skeleton.tsx b/pkg/interface/src/views/landscape/components/Skeleton.tsx index b1971041d..ea1280a92 100644 --- a/pkg/interface/src/views/landscape/components/Skeleton.tsx +++ b/pkg/interface/src/views/landscape/components/Skeleton.tsx @@ -5,7 +5,6 @@ import { Associations } from '@urbit/api/metadata'; import { Sidebar } from './Sidebar/Sidebar'; import GlobalApi from '~/logic/api/global'; -import GlobalSubscription from '~/logic/subscription/global'; import { useGraphModule } from './Sidebar/Apps'; import { Body } from '~/views/components/Body'; import { Workspace } from '~/types/workspace'; @@ -16,14 +15,11 @@ import ErrorBoundary from '~/views/components/ErrorBoundary'; interface SkeletonProps { children: ReactNode; recentGroups: string[]; - linkListening: Set; selected?: string; selectedApp?: AppName; baseUrl: string; mobileHide?: boolean; api: GlobalApi; - subscription: GlobalSubscription; - includeUnmanaged: boolean; workspace: Workspace; } diff --git a/pkg/interface/src/wdyr.js b/pkg/interface/src/wdyr.js new file mode 100644 index 000000000..db32b5d19 --- /dev/null +++ b/pkg/interface/src/wdyr.js @@ -0,0 +1,8 @@ +import React from 'react'; + +if (process.env.NODE_ENV === 'development') { + const whyDidYouRender = require('@welldone-software/why-did-you-render'); + whyDidYouRender(React, { + trackAllPureComponents: true, + }); +} \ No newline at end of file