share project and current page
This commit is contained in:
parent
f6073f3021
commit
5eb3dfc204
5 changed files with 130 additions and 52 deletions
|
@ -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])
|
||||||
|
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -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?
|
||||||
|
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue