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(() => {
|
||||
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) {
|
||||
app.pastePageContent(state.page, state.pageState)
|
||||
if ('page' in state) {
|
||||
app.loadDocumentFromURL(undefined, state.page, state.pageState)
|
||||
} else {
|
||||
app.loadDocumentFromURL(state as TDDocument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [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 * as React from 'react'
|
||||
import { FormattedMessage } from 'react-intl'
|
||||
import { DMContent, DMItem } from '~components/Primitives/DropdownMenu'
|
||||
import { Panel } from '~components/Primitives/Panel'
|
||||
import { SmallIcon } from '~components/Primitives/SmallIcon'
|
||||
import { ToolButton } from '~components/Primitives/ToolButton'
|
||||
import { UndoIcon } from '~components/Primitives/icons'
|
||||
import { useTldrawApp } from '~hooks'
|
||||
|
@ -29,27 +34,6 @@ export function TopPanel({
|
|||
showMultiplayerMenu,
|
||||
}: TopPanelProps) {
|
||||
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 (
|
||||
<StyledTopPanel>
|
||||
|
@ -63,7 +47,7 @@ export function TopPanel({
|
|||
<StyledSpacer />
|
||||
{(showStyles || showZoom) && (
|
||||
<Panel side="right">
|
||||
<ShareButton onClick={copyShareableLink}>Share page</ShareButton>
|
||||
<ShareMenu />
|
||||
{app.readOnly ? (
|
||||
<ReadOnlyLabel>Read Only</ReadOnlyLabel>
|
||||
) : (
|
||||
|
@ -115,7 +99,7 @@ const ReadOnlyLabel = styled('div', {
|
|||
userSelect: 'none',
|
||||
})
|
||||
|
||||
const ShareButton = styled('button', {
|
||||
const ShareButton = styled(DropdownMenu.Trigger, {
|
||||
all: 'unset',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
@ -132,3 +116,71 @@ const ShareButton = styled('button', {
|
|||
color: 'White',
|
||||
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
|
||||
*/
|
||||
loadDocument = (document: TDDocument): this => {
|
||||
this.setIsLoading(true)
|
||||
this.selectNone()
|
||||
this.resetHistory()
|
||||
this.clearSelectHistory()
|
||||
|
@ -1384,10 +1385,26 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
this.replaceState(migrate(state, TldrawApp.version), 'loaded_document')
|
||||
const { point, zoom } = this.camera
|
||||
this.updateViewport(point, zoom)
|
||||
this.setIsLoading(false)
|
||||
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 state = {
|
||||
id: 'create_page',
|
||||
|
@ -1397,29 +1414,31 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
},
|
||||
document: {
|
||||
pages: {
|
||||
[page.id]: undefined,
|
||||
[page!.id]: undefined,
|
||||
},
|
||||
pageStates: {
|
||||
[page.id]: undefined,
|
||||
[page!.id]: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
after: {
|
||||
appState: {
|
||||
currentPageId: page.id,
|
||||
currentPageId: page!.id,
|
||||
},
|
||||
document: {
|
||||
pages: {
|
||||
[page.id]: page,
|
||||
[page!.id]: page,
|
||||
},
|
||||
pageStates: {
|
||||
[page.id]: pageState,
|
||||
[page!.id]: pageState,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
this.setIsLoading(false)
|
||||
return this.setState(state)
|
||||
}
|
||||
}
|
||||
|
||||
// Should we move this to the app layer? onSave, onSaveAs, etc?
|
||||
|
||||
|
|
|
@ -406,6 +406,7 @@ TldrawTestApp {
|
|||
"isPointing": false,
|
||||
"justSent": false,
|
||||
"loadDocument": [Function],
|
||||
"loadDocumentFromURL": [Function],
|
||||
"loadRoom": [Function],
|
||||
"mergeDocument": [Function],
|
||||
"metaKey": false,
|
||||
|
@ -485,7 +486,6 @@ TldrawTestApp {
|
|||
],
|
||||
"pan": [Function],
|
||||
"paste": [Function],
|
||||
"pastePageContent": [Function],
|
||||
"patchCreate": [Function],
|
||||
"patchState": [Function],
|
||||
"persist": [Function],
|
||||
|
|
|
@ -117,5 +117,8 @@
|
|||
"distribute.x": "Distribute Horizontal",
|
||||
"distribute.y": "Distribute Vertical",
|
||||
"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