diff --git a/frontend/apps/site/pages/invite/[inviteToken].tsx b/frontend/apps/site/pages/invite/[inviteToken].tsx deleted file mode 100644 index 90845986bc..0000000000 --- a/frontend/apps/site/pages/invite/[inviteToken].tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { - Button, - Check, - Copy, - H3, - PageSection, - Paragraph, - SizableText, - XStack, - YStack, -} from '@mintter/ui' -import {GetServerSideProps} from 'next' -import {useRouter} from 'next/router' -import {useState} from 'react' -import Footer from '../../footer' -import {getSiteInfo} from '../../get-site-info' -import {SiteHead} from '../../site-head' - -export default function InvitePage({ - hostname = 'demo.com', -}: { - hostname: string -}) { - const inviteToken = useRouter().query.inviteToken as string - const [wasPressed, setPress] = useState(false) - - return ( - - - - - - - - You got invited to collaborate on this site - - - Please follow the next instructions to add this site to your - desktop application and start - - - -

Start here

- - Before you start, make sure you have the Mintter application - installed. If not, you can click here and download the appropiate - version for your operating system. - - - Once you have your account created, you can go to the settings - page of your app and open the “Web sites” section - - {/* IMAGE OF SETTINGS */} - - On this section, press the button on the top right corner to add a - new site. This will open a form with one input, in which you have - to paste the next invite URL: - - - - - {hostname}invite/{inviteToken} - - - - - - - ) -} -export function usePublicationDialog() { - const route = useNavRoute() - const [openSiteHostname, setOpenSiteHostname] = useState(null) - function open(hostname: string) { - setOpenSiteHostname(hostname) - } - - return { - content: ( - { - if (!isOpen) setOpenSiteHostname(null) - }} - > - - - - {openSiteHostname && ( - { - setOpenSiteHostname(null) - }} - /> - )} - - - - ), - open, - } -} diff --git a/frontend/packages/app/src/components/titlebar/publish-share.tsx b/frontend/packages/app/src/components/titlebar/publish-share.tsx index f1464e8ba3..6502f6c71e 100644 --- a/frontend/packages/app/src/components/titlebar/publish-share.tsx +++ b/frontend/packages/app/src/components/titlebar/publish-share.tsx @@ -128,56 +128,6 @@ export function RenameShortnameDialog({ ) } -// function DraftPublicationDialog({ -// draft, -// }: { -// draft?: EditorDraftState | undefined -// }) { -// const sites = useSiteList() -// const sitesList = sites.data || [] -// const foundSiteHostname = sitesList.find( -// (site) => site.hostname === draft?.webUrl, -// ) -// const writeSiteUrl = useWriteDraftWebUrl(draft?.id) - -// return ( -// <> -// -// Publish to: -// -// -// {sitesList?.map((site) => { -// return ( -// -// ) -// })} -// -// ) -// } - function GroupPublishDialog({ input, dialogState, diff --git a/frontend/packages/app/src/components/unpublish-dialog.tsx b/frontend/packages/app/src/components/unpublish-dialog.tsx deleted file mode 100644 index 258223b2be..0000000000 --- a/frontend/packages/app/src/components/unpublish-dialog.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import {useSiteUnpublish} from '@mintter/app/src/models/sites' -import {usePopoverState} from '@mintter/app/src/use-popover-state' -import {DeleteDialog} from '@mintter/app/src/components/delete-dialog' -import {WebPublicationRecord} from '@mintter/shared' -import {Button} from '@mintter/ui' -import {toast} from 'react-hot-toast' - -export function useUnpublishDialog({ - pub = null, - hostname, - trigger, -}: { - pub: WebPublicationRecord | null - hostname: string - trigger?: (props: {onPress: () => void}) => JSX.Element -}) { - const dialogState = usePopoverState() - const unpublish = useSiteUnpublish() - - return { - ...dialogState, - deleteDialog: !pub ? null : ( - { - dialogState.onOpenChange(false) - }} - chromeless - > - Cancel - - } - actionButton={ - - } - /> - ), - } -} diff --git a/frontend/packages/app/src/models/changes.ts b/frontend/packages/app/src/models/changes.ts index 1e53bfa362..18529275ff 100644 --- a/frontend/packages/app/src/models/changes.ts +++ b/frontend/packages/app/src/models/changes.ts @@ -9,7 +9,6 @@ import {useQueries, useQuery} from '@tanstack/react-query' import {useMemo} from 'react' import {useDocumentVersions, usePublicationList} from './documents' import {queryKeys} from './query-keys' -import {useDocWebPublications} from './sites' import {useGRPCClient} from '../app-context' function createDocChangesQuery( @@ -69,14 +68,12 @@ export function getTextOfBlock(block: BlockNode): string { // this involves loading every version of the doc, and using block revisions to see what changed export function useSmartChanges(docId?: string, version?: string) { const docChanges = useDocChanges(docId) - const docPubs = useDocWebPublications(docId) // const loadedDocs = useQueries() const changes = docChanges.data?.changes const versionPublications = useDocumentVersions( docId, changes?.map((change) => change.version) || [], ) - const sitePublications = docPubs.data const versionPubData = versionPublications.map((pub) => pub.data) return { versionPublications, @@ -166,17 +163,11 @@ export function useSmartChanges(docId?: string, version?: string) { return { ...change, summary, - webPubs: docPubs.data?.filter( - (webPub) => - webPub.version === change.version && - webPub.documentId === docId, - ), } as SmartChangeInfo }), rawChanges: changes, - sitePublications, } as const - }, [changes, sitePublications, ...versionPubData]), + }, [changes, ...versionPubData]), } } @@ -192,6 +183,18 @@ export function useChange(changeId?: string) { }) } +export function useAllChanges(entityId?: string) { + const grpcClient = useGRPCClient() + return useQuery({ + queryFn: () => + grpcClient.entities.getEntityTimeline({ + id: entityId || '', + }), + queryKey: [queryKeys.ALL_ENTITY_CHANGES, entityId], + enabled: !!entityId, + }) +} + export function useAllPublicationChanges() { const allPublications = usePublicationList({trustedOnly: false}) const pubs = allPublications?.data?.publications || [] diff --git a/frontend/packages/app/src/models/documents.ts b/frontend/packages/app/src/models/documents.ts index 70ee87af79..a54df721ba 100644 --- a/frontend/packages/app/src/models/documents.ts +++ b/frontend/packages/app/src/models/documents.ts @@ -331,9 +331,7 @@ export function usePublishDraft( invalidate([queryKeys.GET_DRAFT_LIST]) invalidate([queryKeys.GET_PUBLICATION, documentId]) invalidate([queryKeys.PUBLICATION_CHANGES, documentId]) - invalidate([queryKeys.GET_DOC_SITE_PUBLICATIONS, documentId]) invalidate([queryKeys.PUBLICATION_CITATIONS]) - invalidate([queryKeys.GET_SITE_PUBLICATIONS]) if (draftGroupContext) { invalidate([queryKeys.GET_GROUP_CONTENT, draftGroupContext.groupId]) invalidate([queryKeys.GET_GROUPS_FOR_DOCUMENT, documentId]) diff --git a/frontend/packages/app/src/models/groups.ts b/frontend/packages/app/src/models/groups.ts index a9ecd368c4..78b5015452 100644 --- a/frontend/packages/app/src/models/groups.ts +++ b/frontend/packages/app/src/models/groups.ts @@ -281,12 +281,14 @@ export function useMyGroups() { return groups } -export function useGroupSite(groupId: string) { +export function useHostGroup(hostname: string) { const grpcClient = useGRPCClient() return useQuery({ - queryKey: [queryKeys.GET_GROUP_SITE, groupId], + queryKey: [queryKeys.GET_HOST_GROUP, hostname], queryFn: async () => { - // return await grpcClient.groups.getSiteInfo({}) + return await grpcClient.groups.getSiteInfo({ + hostname, + }) }, }) } diff --git a/frontend/packages/app/src/models/query-keys.ts b/frontend/packages/app/src/models/query-keys.ts index d3aecf133e..551f8dfa2b 100644 --- a/frontend/packages/app/src/models/query-keys.ts +++ b/frontend/packages/app/src/models/query-keys.ts @@ -20,21 +20,14 @@ export const queryKeys = { GET_ALL_ACCOUNTS: 'GET_ALL_ACCOUNTS', GET_ACCOUNT: 'GET_ACCOUNT', // , accountId: string - // sites - GET_SITES: 'GET_SITES', - GET_SITE_INFO: 'GET_SITE_INFO', // , hostname: string - GET_SITE_MEMBERS: 'GET_SITE_MEMBERS', // , hostname: string - GET_SITE_PUBLICATIONS: 'GET_SITE_PUBLICATIONS', //, hostname: string - GET_DOC_SITE_PUBLICATIONS: 'GET_DOC_SITE_PUBLICATIONS', // , docId: string - // groups GET_GROUPS: 'GET_GROUPS', GET_GROUP: 'GET_GROUP', // , groupId: string GET_GROUP_CONTENT: 'GET_GROUP_CONTENT', // , groupId: string GET_GROUP_MEMBERS: 'GET_GROUP_MEMBERS', // , groupId: string - GET_GROUP_SITE: 'GET_GROUP_SITE', // , groupId: string GET_GROUPS_FOR_DOCUMENT: 'GET_GROUPS_FOR_DOCUMENT', // , documentId: string GET_GROUPS_FOR_ACCOUNT: 'GET_GROUPS_FOR_ACCOUNT', // , accountId: string + GET_HOST_GROUP: 'GET_HOST_GROUP', // , hostname: string // documents GET_DRAFT_LIST: 'GET_DRAFT_LIST', // @@ -56,6 +49,7 @@ export const queryKeys = { // changes CHANGE: 'CHANGE', //, changeId: string + ALL_ENTITY_CHANGES: 'ALL_ENTITY_CHANGES', //, entityId: string } as const export function labelOfQueryKey(key: QueryKey) { @@ -78,18 +72,6 @@ export function labelOfQueryKey(key: QueryKey) { case queryKeys.GET_ACCOUNT: return `Account ${abbreviateCid(arg1)}` - // sites - case queryKeys.GET_SITES: - return 'Sites' - case queryKeys.GET_SITE_INFO: - return `Site ${hostnameStripProtocol(arg1)}` - case queryKeys.GET_SITE_MEMBERS: - return `Site Members ${hostnameStripProtocol(arg1)}` - case queryKeys.GET_SITE_PUBLICATIONS: - return `Site Publications ${hostnameStripProtocol(arg1)}` - case queryKeys.GET_DOC_SITE_PUBLICATIONS: - return `Web Publication ${abbreviateCid(arg1)}` - // documents case queryKeys.GET_DRAFT_LIST: return 'Drafts' diff --git a/frontend/packages/app/src/models/sites.ts b/frontend/packages/app/src/models/sites.ts deleted file mode 100644 index d7e47b7ab1..0000000000 --- a/frontend/packages/app/src/models/sites.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { - Block, - Document, - GRPCClient, - Member, - Member_Role, - ReferencedDocument, - SiteConfig, - SiteInfo, - unpackDocId, -} from '@mintter/shared' -import {useMutation, UseMutationOptions, useQuery} from '@tanstack/react-query' -import {queryKeys} from './query-keys' -import {useNavigate} from '@mintter/app/src/utils/navigation' -import {toast} from '@mintter/app/src/toast' -import {useAppContext, useQueryInvalidator} from '@mintter/app/src/app-context' -import {useGRPCClient} from '../app-context' - -function blockExtractReferencedDocs( - block: Block, -): Partial[] { - const docIds: Array = [] - block.annotations.forEach((annotation) => { - if (annotation.type === 'embed' || annotation.type === 'link') { - const ids = unpackDocId(annotation.attributes.url) - - if (ids?.scheme === 'hm') { - docIds.push({documentId: ids?.docId, version: ids?.version}) - } - } - }) - return docIds -} - -export function extractReferencedDocs(doc: Document) { - return doc.children - .map((child) => - child.block ? blockExtractReferencedDocs(child.block) : [], - ) - .flat() -} - -async function getDocWebPublications( - grpcClient: GRPCClient, - documentId: string, -) { - const result = await grpcClient.webPublishing.listWebPublicationRecords({ - documentId, - }) - return result.publications -} - -export function useDocWebPublications(docId?: string) { - const grpcClient = useGRPCClient() - return useQuery({ - queryKey: [queryKeys.GET_DOC_SITE_PUBLICATIONS, docId], - queryFn: async () => { - if (!docId) return [] - return await getDocWebPublications(grpcClient, docId) - }, - }) -} - -export function useSiteList() { - const grpcClient = useGRPCClient() - return useQuery({ - queryKey: [queryKeys.GET_SITES], - queryFn: async () => { - const result = await grpcClient.webPublishing.listSites({}) - return result.sites - }, - }) -} - -export function useAddSite( - options?: UseMutationOptions< - null, - void, - {hostname: string; inviteToken?: string}, - unknown - >, -) { - const grpcClient = useGRPCClient() - const invalidate = useQueryInvalidator() - return useMutation( - async (input: {hostname: string; inviteToken?: string}) => { - await grpcClient.webPublishing.addSite(input) - return null - }, - { - ...options, - onSuccess: (_result, _hostname, ctx) => { - invalidate([queryKeys.GET_SITES]) - options?.onSuccess?.(_result, _hostname, ctx) - }, - }, - ) -} - -export function useSiteInfo(hostname: string) { - const grpcClient = useGRPCClient() - return useQuery({ - queryKey: [queryKeys.GET_SITE_INFO, hostname], - queryFn: async () => { - return await grpcClient.getRemoteWebClient(hostname).getSiteInfo({}) - }, - }) -} - -export function useWriteSiteInfo( - hostname: string, - opts?: UseMutationOptions>, -) { - const invalidate = useQueryInvalidator() - const grpcClient = useGRPCClient() - return useMutation( - async (info: Partial) => { - await grpcClient.getRemoteWebClient(hostname).updateSiteInfo(info) - }, - { - ...opts, - onSuccess: (response, input, ctx) => { - invalidate([queryKeys.GET_SITE_INFO, hostname]) - opts?.onSuccess?.(response, input, ctx) - }, - }, - ) -} - -export function useSiteMembers(hostname: string) { - const grpcClient = useGRPCClient() - return useQuery({ - queryKey: [queryKeys.GET_SITE_MEMBERS, hostname], - queryFn: async () => { - const site = await grpcClient.getRemoteWebClient(hostname) - const result = await site.listMembers({}).catch((e) => { - console.error(e) - return {members: []} - }) - return result.members - }, - }) -} - -export function useInviteMember( - hostname: string, - opts?: UseMutationOptions, -) { - const grpcClient = useGRPCClient() - return useMutation( - async () => { - const token = await grpcClient - .getRemoteWebClient(hostname) - .createInviteToken({ - role: Member_Role.EDITOR, - }) - return token.token - }, - { - ...opts, - onSuccess: (response, input, ctx) => { - // invalidate, refetch? members list probably wont change yet - opts?.onSuccess?.(response, input, ctx) - }, - }, - ) -} - -export function useRemoveMember( - hostname: string, - opts?: UseMutationOptions, -) { - const grpcClient = useGRPCClient() - const invalidate = useQueryInvalidator() - return useMutation( - async (accountId: string) => { - await grpcClient.getRemoteWebClient(hostname).deleteMember({ - accountId, - }) - return - }, - { - ...opts, - onSuccess: (response, input, ctx) => { - invalidate([queryKeys.GET_SITE_MEMBERS, hostname]) - opts?.onSuccess?.(response, input, ctx) - }, - }, - ) -} - -export function useRemoveSite(hostname: string, opts: UseMutationOptions) { - const invalidate = useQueryInvalidator() - const grpcClient = useGRPCClient() - return useMutation( - async () => { - await grpcClient.webPublishing.removeSite({hostname}) - }, - { - ...opts, - onSuccess: (response, input, ctx) => { - invalidate([queryKeys.GET_SITES]) - opts?.onSuccess?.(response, input, ctx) - }, - }, - ) -} - -export function useSitePublications(hostname: string | undefined) { - const grpcClient = useGRPCClient() - return useQuery({ - queryKey: [queryKeys.GET_SITE_PUBLICATIONS, hostname], - queryFn: async () => { - if (!hostname) return {publications: []} - const site = grpcClient.getRemoteWebClient(hostname) - return await site.listWebPublications({}) - }, - }) -} - -async function performWebPublish( - grpcClient: GRPCClient, - document: Document, - hostname: string, - path: string, - version: string, -) { - // 3. get referenced dependencies of the document - const referencedDocs = extractReferencedDocs(document) - - // 4. publish the document to the site - const site = grpcClient.getRemoteWebClient(hostname) - await site.publishDocument({ - documentId: document.id, - path: path, - referencedDocuments: referencedDocs, - version, - }) -} - -export function useSitePublish(draftId: string | undefined) { - const {invalidate, client} = useAppContext().queryClient - const navigate = useNavigate('replace') - const grpcClient = useGRPCClient() - return useMutation( - async ({path}: {path: string}) => { - // welcome to the "initial" Web Publish flow, when the path is set - - const draft = await grpcClient.drafts.getDraft({documentId: draftId}) - if (!draft) throw new Error('no draft found') - const site = grpcClient.getRemoteWebClient(draft.webUrl) - - const pubs = await site.listWebPublications({}).catch((e) => { - if (e.message.includes('failed to dial to site')) { - throw new Error('Cannot connect to ' + draft.webUrl) - } - }) - if (!pubs) throw new Error('Cannot connect to ' + draft.webUrl) - - if (pubs.publications.find((pub) => pub.path === path)) { - throw new Error(`Path ${path} already exists on ${draft.webUrl}`) - } - - const docId = draftId - if (!docId) throw new Error('No draftId provided to useSitePublish') - const publication = await grpcClient.drafts.publishDraft({ - documentId: docId, - }) - const document = publication.document - if (!document) throw new Error('No document in new publication?!') - - // 1. get the account ID of the publisher - const webUrl = publication.document?.webUrl - - if (!webUrl) { - // Bailing because no webUrl on this draft. this should not happen because useSitePublish should only be called on drafts that have a webUrl set - return { - publication, - docId, - hostname: undefined, - } - } - - await performWebPublish( - grpcClient, - document, - webUrl, - path, - publication.version, - ).catch((e) => { - console.error('Caught webPub failure', e) - toast.error('Failed to publish on web.') - }) - - return { - publication, - docId, - hostname: webUrl, - } - }, - { - onSuccess: ({publication, docId, hostname}, input) => { - invalidate([queryKeys.PUBLICATION_CHANGES, docId]) - invalidate([queryKeys.GET_PUBLICATION, docId]) - invalidate([queryKeys.GET_PUBLICATION_LIST]) - invalidate([queryKeys.GET_DRAFT_LIST]) - client.setQueryData([queryKeys.EDITOR_DRAFT, docId], () => null) - navigate({ - key: 'publication', - documentId: docId, - versionId: publication.version, - }) - if (hostname) invalidate([queryKeys.GET_SITE_PUBLICATIONS, hostname]) - invalidate([queryKeys.GET_DOC_SITE_PUBLICATIONS, docId]) - }, - }, - ) -} - -export function useSiteUnpublish() { - const invalidate = useQueryInvalidator() - const grpcClient = useGRPCClient() - return useMutation( - async ({ - hostname, - documentId, - version, - }: { - hostname: string - documentId: string - version: string - }) => { - const site = grpcClient.getRemoteWebClient(hostname) - await site.unpublishDocument({ - documentId, - version, - }) - }, - { - onSuccess: (a, input) => { - invalidate([queryKeys.GET_SITE_PUBLICATIONS, input.hostname]) - invalidate([queryKeys.GET_DOC_SITE_PUBLICATIONS]) - }, - }, - ) -} diff --git a/frontend/packages/app/src/pages/group.tsx b/frontend/packages/app/src/pages/group.tsx index 1117897b32..bec0666649 100644 --- a/frontend/packages/app/src/pages/group.tsx +++ b/frontend/packages/app/src/pages/group.tsx @@ -1,5 +1,5 @@ import Footer from '@mintter/app/src/components/footer' -import {Document, Role, unpackDocId, unpackHmId} from '@mintter/shared' +import {Document, Role, pluralS, unpackDocId, unpackHmId} from '@mintter/shared' import { Button, Container, @@ -24,7 +24,7 @@ import { Store, Trash, } from '@tamagui/lucide-icons' -import {useState} from 'react' +import {useMemo, useState} from 'react' import {toast} from 'react-hot-toast' import {AccountLinkAvatar} from '../components/account-link-avatar' import {useAppDialog} from '../components/dialog' @@ -39,13 +39,16 @@ import { useRemoveDocFromGroup, useRenameGroupDoc, } from '../models/groups' -import {useNavRoute, useNavigate} from '../utils/navigation' +import {GroupRoute, useNavRoute, useNavigate} from '../utils/navigation' import {AppLinkText} from '../components/link' import {pathNameify} from '../utils/path' import {useOpenDraft} from '../utils/open-draft' import {StaticBlockNode} from '@mintter/editor' import {EditDocActions} from '../components/titlebar/common' import {useEditGroupInfoDialog} from '../components/edit-group-info' +import {Allotment} from 'allotment' +import {FooterButton} from '../components/footer' +import {useAllChanges} from '../models/changes' export function RenamePubDialog({ input: {groupId, pathName, docTitle}, @@ -428,3 +431,24 @@ export default function GroupPage() { ) } + +function ChangesFooterItem({route}: {route: GroupRoute}) { + const changes = useAllChanges(route.groupId) + const count = useMemo( + () => Object.keys(changes?.data?.changes || {}).length || 0, + [changes.data], + ) + const replace = useNavigate('replace') + // if (route.accessory?.key !== 'versions') return null + return ( + { + if (route.accessory) return replace({...route, accessory: null}) + replace({...route, accessory: {key: 'versions'}}) + }} + /> + ) +} diff --git a/frontend/packages/app/src/pages/main.tsx b/frontend/packages/app/src/pages/main.tsx index 7cd7bb9d44..9ac5b90ce7 100644 --- a/frontend/packages/app/src/pages/main.tsx +++ b/frontend/packages/app/src/pages/main.tsx @@ -9,7 +9,6 @@ import { useNavRoute, } from '@mintter/app/src/utils/navigation' import {Spinner, YStack} from '@mintter/ui' -import {ProsemirrorAdapterProvider} from '@prosemirror-adapter/react' import {lazy, Suspense, useMemo} from 'react' import {ErrorBoundary} from 'react-error-boundary' import {NotFoundPage} from './base' @@ -27,7 +26,6 @@ var Account = lazy(() => import('@mintter/app/src/pages/account-page')) var Contacts = lazy(() => import('@mintter/app/src/pages/contacts-page')) var Group = lazy(() => import('@mintter/app/src/pages/group')) var Groups = lazy(() => import('@mintter/app/src/pages/groups')) -var Site = lazy(() => import('@mintter/app/src/pages/site-page')) var Publication = lazy(() => import('@mintter/app/src/pages/publication')) var Draft = lazy(() => import('@mintter/app/src/pages/draft')) var Settings = lazy(() => import('@mintter/app/src/pages/settings')) @@ -65,11 +63,6 @@ function getPageComponent(navRoute: NavRoute) { PageComponent: DraftList, Fallback: BaseLoading, } - case 'site': - return { - PageComponent: Site, - Fallback: BaseLoading, - } case 'contacts': return { PageComponent: Contacts, diff --git a/frontend/packages/app/src/pages/settings.tsx b/frontend/packages/app/src/pages/settings.tsx index c5804e35b6..e7a2177086 100644 --- a/frontend/packages/app/src/pages/settings.tsx +++ b/frontend/packages/app/src/pages/settings.tsx @@ -9,16 +9,6 @@ import { import {useDaemonInfo} from '@mintter/app/src/models/daemon' import {usePeerInfo} from '@mintter/app/src/models/networking' import {useInvoicesBywallet, useWallets} from '@mintter/app/src/models/payments' -import { - useAddSite, - useInviteMember, - useRemoveMember, - useRemoveSite, - useSiteInfo, - useSiteList, - useSiteMembers, - useWriteSiteInfo, -} from '@mintter/app/src/models/sites' import {ObjectKeys} from '@mintter/app/src/utils/object-keys' import {hostnameStripProtocol} from '@mintter/app/src/utils/site-hostname' import { @@ -97,11 +87,6 @@ export default function Settings() { Settings - - - Web Sites - - {/* Wallets @@ -130,9 +115,6 @@ export default function Settings() { - - - {/* */} @@ -401,156 +383,6 @@ function SettingsNavBack({ ) } -function InviteMemberDialog({url, onDone}: {url: string; onDone: () => void}) { - return ( - - Copy and send this secret editor invite URL - {url && } - - - ) -} -export function useInviteDialog(hostname: string) { - const [isOpen, setIsOpen] = useState(null) - const invite = useInviteMember(hostname) - - function open() { - invite.mutateAsync().then((inviteToken) => { - setIsOpen(`${hostname}/invite/${inviteToken}`) - }) - } - return { - content: ( - setIsOpen(null)}> - - - - Invite Code - {isOpen && ( - setIsOpen(null)} /> - )} - - - - ), - open, - } -} - -function getNameOfRole(role: Member_Role): string { - if (role === Member_Role.OWNER) return 'Owner' - if (role === Member_Role.EDITOR) return 'Editor' - return 'Unauthorized' -} - -function SiteMemberRow({ - member, - hostname, - isOwner, -}: { - member: Member - hostname: string - isOwner: boolean -}) { - const {data: account} = useAccount(member.accountId) - const remove = useRemoveMember(hostname) - const [hovering, setHover] = useState(false) - - let hoverProps = useMemo(() => { - if (!isOwner && member.accountId == account?.id) return {} - - return { - onMouseEnter: () => setHover(true), - onMouseLeave: () => setHover(false), - } - }, [member.accountId, isOwner, account]) - - return ( - setHover(true)} - // onPressOut={() => setHover(false)} - > - - {account?.profile?.alias || - `...${member.accountId.substring(member.accountId.length - 16)}`} - - {member.role === Member_Role.OWNER && ( - - )} - {hovering && isOwner && member.accountId !== account?.id ? ( - - ) : null} - - ) -} -function SiteMembers({ - hostname, - accountId, - isOwner, -}: { - hostname: string - accountId: string - isOwner: boolean -}) { - const {content, open} = useInviteDialog(hostname) - - const {data: members} = useSiteMembers(hostname) - - return ( - - - - Members - - {isOwner ? ( - - ) : null} - {content} - - {members?.map((member) => ( - - ))} - - ) -} function SettingsSection({ title, @@ -567,332 +399,6 @@ function SettingsSection({ ) } -function SiteInfoForm({ - info, - onSubmit, - isOwner, -}: { - info: SiteInfo - onSubmit: (s: Partial) => void - isOwner: boolean -}) { - const [title, setTitle] = useState(info.title) - const [description, setDescription] = useState(info.description) - return ( - - - - setTitle(val) : undefined} - /> - - - -