share project and current page

This commit is contained in:
Judicael 2022-08-18 08:11:26 +03:00
parent f6073f3021
commit 5eb3dfc204
5 changed files with 130 additions and 52 deletions

View file

@ -375,9 +375,13 @@ const InnerTldraw = React.memo(function InnerTldraw({
React.useEffect(() => { React.useEffect(() => {
if (decodedPage.length) { if (decodedPage.length) {
const state = JSON.parse(decodedPage) as Record<'page' | 'pageState', any> const state = JSON.parse(decodedPage) as Record<string, any>
if (Object.keys(state).length) { if (Object.keys(state).length) {
app.pastePageContent(state.page, state.pageState) if ('page' in state) {
app.loadDocumentFromURL(undefined, state.page, state.pageState)
} else {
app.loadDocumentFromURL(state as TDDocument)
}
} }
} }
}, [decodedPage]) }, [decodedPage])

View file

@ -1,6 +1,11 @@
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { ClipboardIcon } from '@radix-ui/react-icons'
import JSONCrush from 'jsoncrush' import JSONCrush from 'jsoncrush'
import * as React from 'react' import * as React from 'react'
import { FormattedMessage } from 'react-intl'
import { DMContent, DMItem } from '~components/Primitives/DropdownMenu'
import { Panel } from '~components/Primitives/Panel' import { Panel } from '~components/Primitives/Panel'
import { SmallIcon } from '~components/Primitives/SmallIcon'
import { ToolButton } from '~components/Primitives/ToolButton' import { ToolButton } from '~components/Primitives/ToolButton'
import { UndoIcon } from '~components/Primitives/icons' import { UndoIcon } from '~components/Primitives/icons'
import { useTldrawApp } from '~hooks' import { useTldrawApp } from '~hooks'
@ -29,27 +34,6 @@ export function TopPanel({
showMultiplayerMenu, showMultiplayerMenu,
}: TopPanelProps) { }: TopPanelProps) {
const app = useTldrawApp() const app = useTldrawApp()
const currentPageId = app.appState.currentPageId
const pageDocument = app.document.pages[currentPageId]
const pageState = app.document.pageStates[currentPageId]
const copyShareableLink = () => {
try {
const state = {
page: {
...pageDocument,
},
pageState: {
...pageState,
},
}
const crushed = JSONCrush.crush(JSON.stringify(state))
const link = `${window.location.href}/?d=${encodeURIComponent(crushed)}`
navigator.clipboard.writeText(link)
} catch (err) {
console.error(err)
}
}
return ( return (
<StyledTopPanel> <StyledTopPanel>
@ -63,7 +47,7 @@ export function TopPanel({
<StyledSpacer /> <StyledSpacer />
{(showStyles || showZoom) && ( {(showStyles || showZoom) && (
<Panel side="right"> <Panel side="right">
<ShareButton onClick={copyShareableLink}>Share page</ShareButton> <ShareMenu />
{app.readOnly ? ( {app.readOnly ? (
<ReadOnlyLabel>Read Only</ReadOnlyLabel> <ReadOnlyLabel>Read Only</ReadOnlyLabel>
) : ( ) : (
@ -115,7 +99,7 @@ const ReadOnlyLabel = styled('div', {
userSelect: 'none', userSelect: 'none',
}) })
const ShareButton = styled('button', { const ShareButton = styled(DropdownMenu.Trigger, {
all: 'unset', all: 'unset',
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center', alignItems: 'center',
@ -132,3 +116,71 @@ const ShareButton = styled('button', {
color: 'White', color: 'White',
marginTop: 2, marginTop: 2,
}) })
const ShareMenu = () => {
const app = useTldrawApp()
const currentPageId = app.appState.currentPageId
const pageDocument = app.document.pages[currentPageId]
const pageState = app.document.pageStates[currentPageId]
const copyCurrentPageLink = () => {
const hasAsset = Object.entries(pageDocument.shapes).filter(
([_, value]) => value.assetId
).length
if (hasAsset) {
alert('too big to fit in an url')
} else {
try {
const state = {
page: {
...pageDocument,
},
pageState: {
...pageState,
},
}
const crushed = JSONCrush.crush(JSON.stringify(state))
const link = `${window.location.href}/?d=${encodeURIComponent(crushed)}`
navigator.clipboard.writeText(link)
} catch (err) {
console.error(err)
}
}
}
const copyProjectLink = () => {
if (Object.keys(app.document.assets).length) {
alert('too big to fit in an url')
} else {
try {
const crushed = JSONCrush.crush(JSON.stringify(app.document))
const link = `${window.location.href}/?d=${encodeURIComponent(crushed)}`
navigator.clipboard.writeText(link)
} catch (e) {
console.error(e)
}
}
}
return (
<DropdownMenu.Root dir="ltr">
<ShareButton id="TD-MultiplayerMenuIcon">
<FormattedMessage id="share" />
</ShareButton>
<DMContent variant="menu" id="TD-MultiplayerMenu" side="bottom" align="start" sideOffset={4}>
<DMItem id="TD-Multiplayer-CopyInviteLink" onClick={copyCurrentPageLink}>
<FormattedMessage id="copy.current.project.link" />
<SmallIcon>
<ClipboardIcon />
</SmallIcon>
</DMItem>
<DMItem id="TD-Multiplayer-CopyReadOnlyLink" onClick={copyProjectLink}>
<FormattedMessage id="copy.project.link" />
<SmallIcon>
<ClipboardIcon />
</SmallIcon>
</DMItem>
</DMContent>
</DropdownMenu.Root>
)
}

View file

@ -1362,6 +1362,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
* @param document The document to load * @param document The document to load
*/ */
loadDocument = (document: TDDocument): this => { loadDocument = (document: TDDocument): this => {
this.setIsLoading(true)
this.selectNone() this.selectNone()
this.resetHistory() this.resetHistory()
this.clearSelectHistory() this.clearSelectHistory()
@ -1384,10 +1385,26 @@ export class TldrawApp extends StateManager<TDSnapshot> {
this.replaceState(migrate(state, TldrawApp.version), 'loaded_document') this.replaceState(migrate(state, TldrawApp.version), 'loaded_document')
const { point, zoom } = this.camera const { point, zoom } = this.camera
this.updateViewport(point, zoom) this.updateViewport(point, zoom)
this.setIsLoading(false)
return this return this
} }
pastePageContent = (page: TDPage, pageState: Record<string, TLPageState>): this => { /**
* load content from URL
* @param document
* @param page
* @param pageState
* @returns
*/
loadDocumentFromURL = (
document?: TDDocument,
page?: TDPage,
pageState?: Record<string, TLPageState>
): this => {
if (document) {
return this.loadDocument(document)
} else {
this.setIsLoading(true)
const { currentPageId } = this const { currentPageId } = this
const state = { const state = {
id: 'create_page', id: 'create_page',
@ -1397,29 +1414,31 @@ export class TldrawApp extends StateManager<TDSnapshot> {
}, },
document: { document: {
pages: { pages: {
[page.id]: undefined, [page!.id]: undefined,
}, },
pageStates: { pageStates: {
[page.id]: undefined, [page!.id]: undefined,
}, },
}, },
}, },
after: { after: {
appState: { appState: {
currentPageId: page.id, currentPageId: page!.id,
}, },
document: { document: {
pages: { pages: {
[page.id]: page, [page!.id]: page,
}, },
pageStates: { pageStates: {
[page.id]: pageState, [page!.id]: pageState,
}, },
}, },
}, },
} }
this.setIsLoading(false)
return this.setState(state) return this.setState(state)
} }
}
// Should we move this to the app layer? onSave, onSaveAs, etc? // Should we move this to the app layer? onSave, onSaveAs, etc?

View file

@ -406,6 +406,7 @@ TldrawTestApp {
"isPointing": false, "isPointing": false,
"justSent": false, "justSent": false,
"loadDocument": [Function], "loadDocument": [Function],
"loadDocumentFromURL": [Function],
"loadRoom": [Function], "loadRoom": [Function],
"mergeDocument": [Function], "mergeDocument": [Function],
"metaKey": false, "metaKey": false,
@ -485,7 +486,6 @@ TldrawTestApp {
], ],
"pan": [Function], "pan": [Function],
"paste": [Function], "paste": [Function],
"pastePageContent": [Function],
"patchCreate": [Function], "patchCreate": [Function],
"patchState": [Function], "patchState": [Function],
"persist": [Function], "persist": [Function],

View file

@ -117,5 +117,8 @@
"distribute.x": "Distribute Horizontal", "distribute.x": "Distribute Horizontal",
"distribute.y": "Distribute Vertical", "distribute.y": "Distribute Vertical",
"stretch.x": "Stretch Horizontal", "stretch.x": "Stretch Horizontal",
"stretch.y": "Stretch Vertical" "stretch.y": "Stretch Vertical",
"share": "Share",
"copy.current.page.link": "Copy Current Page Link",
"copy.project.link": "Copy Project Link"
} }