kopia lustrzana https://github.com/Tldraw/Tldraw
feat: share via link
rodzic
56747e67a5
commit
f6073f3021
|
@ -49,6 +49,7 @@
|
|||
"@tldraw/vec": "^1.7.1",
|
||||
"browser-fs-access": "^0.31.0",
|
||||
"idb-keyval": "^6.1.0",
|
||||
"jsoncrush": "^1.1.8",
|
||||
"lz-string": "^1.4.4",
|
||||
"perfect-freehand": "^1.1.0",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
|
@ -99,7 +100,10 @@
|
|||
"moduleNameMapper": {
|
||||
"@tldraw/tldraw": "<rootDir>/src",
|
||||
"\\~(.*)": "<rootDir>/src/$1"
|
||||
}
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!jsoncrush/JSONCrush.js)"
|
||||
]
|
||||
},
|
||||
"gitHead": "4b1137849ad07da36fc8f0f19cb64e7535a79296"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Renderer } from '@tldraw/core'
|
||||
import JSONCrush from 'jsoncrush'
|
||||
import * as React from 'react'
|
||||
import { ErrorBoundary as _Errorboundary } from 'react-error-boundary'
|
||||
import { IntlProvider } from 'react-intl'
|
||||
|
@ -24,7 +25,7 @@ import { TDCallbacks, TldrawApp } from '~state'
|
|||
import { TLDR } from '~state/TLDR'
|
||||
import { shapeUtils } from '~state/shapes'
|
||||
import { dark, styled } from '~styles'
|
||||
import { TDDocument, TDStatus } from '~types'
|
||||
import { TDDocument, TDPage, TDShape, TDStatus } from '~types'
|
||||
|
||||
const ErrorBoundary = _Errorboundary as any
|
||||
|
||||
|
@ -363,6 +364,23 @@ const InnerTldraw = React.memo(function InnerTldraw({
|
|||
}: InnerTldrawProps) {
|
||||
const app = useTldrawApp()
|
||||
const [dialogContainer, setDialogContainer] = React.useState<any>(null)
|
||||
const entry =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? window.location.hash.replace('#/develop/', '')
|
||||
: window.location.search
|
||||
const urlSearchParams = new URLSearchParams(entry)
|
||||
const encodedPage = urlSearchParams.get('d')
|
||||
|
||||
const decodedPage = JSONCrush.uncrush((encodedPage as string) ?? '')
|
||||
|
||||
React.useEffect(() => {
|
||||
if (decodedPage.length) {
|
||||
const state = JSON.parse(decodedPage) as Record<'page' | 'pageState', any>
|
||||
if (Object.keys(state).length) {
|
||||
app.pastePageContent(state.page, state.pageState)
|
||||
}
|
||||
}
|
||||
}, [decodedPage])
|
||||
|
||||
const rWrapper = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import JSONCrush from 'jsoncrush'
|
||||
import * as React from 'react'
|
||||
import { Panel } from '~components/Primitives/Panel'
|
||||
import { ToolButton } from '~components/Primitives/ToolButton'
|
||||
|
@ -28,6 +29,27 @@ 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>
|
||||
|
@ -41,6 +63,7 @@ export function TopPanel({
|
|||
<StyledSpacer />
|
||||
{(showStyles || showZoom) && (
|
||||
<Panel side="right">
|
||||
<ShareButton onClick={copyShareableLink}>Share page</ShareButton>
|
||||
{app.readOnly ? (
|
||||
<ReadOnlyLabel>Read Only</ReadOnlyLabel>
|
||||
) : (
|
||||
|
@ -91,3 +114,21 @@ const ReadOnlyLabel = styled('div', {
|
|||
paddingRight: '$1',
|
||||
userSelect: 'none',
|
||||
})
|
||||
|
||||
const ShareButton = styled('button', {
|
||||
all: 'unset',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: '$2',
|
||||
padding: '0 15px',
|
||||
fontSize: '$1',
|
||||
lineHeight: 1,
|
||||
fontWeight: 'normal',
|
||||
height: 36,
|
||||
cursor: 'pointer',
|
||||
minWidth: 48,
|
||||
backgroundColor: '#2F80ED',
|
||||
color: 'White',
|
||||
marginTop: 2,
|
||||
})
|
||||
|
|
|
@ -1387,6 +1387,40 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
return this
|
||||
}
|
||||
|
||||
pastePageContent = (page: TDPage, pageState: Record<string, TLPageState>): this => {
|
||||
const { currentPageId } = this
|
||||
const state = {
|
||||
id: 'create_page',
|
||||
before: {
|
||||
appState: {
|
||||
currentPageId,
|
||||
},
|
||||
document: {
|
||||
pages: {
|
||||
[page.id]: undefined,
|
||||
},
|
||||
pageStates: {
|
||||
[page.id]: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
after: {
|
||||
appState: {
|
||||
currentPageId: page.id,
|
||||
},
|
||||
document: {
|
||||
pages: {
|
||||
[page.id]: page,
|
||||
},
|
||||
pageStates: {
|
||||
[page.id]: pageState,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return this.setState(state)
|
||||
}
|
||||
|
||||
// Should we move this to the app layer? onSave, onSaveAs, etc?
|
||||
|
||||
/**
|
||||
|
|
|
@ -485,6 +485,7 @@ TldrawTestApp {
|
|||
],
|
||||
"pan": [Function],
|
||||
"paste": [Function],
|
||||
"pastePageContent": [Function],
|
||||
"patchCreate": [Function],
|
||||
"patchState": [Function],
|
||||
"persist": [Function],
|
||||
|
|
Ładowanie…
Reference in New Issue