Skip to content

Commit

Permalink
[dotcom] Delete service worker, cache tldraw assets (#2552)
Browse files Browse the repository at this point in the history
A few things happening here

- Delete our service worker. Turns out that a couple of years back
browsers decided that a service worker is no longer required for a PWA
so you can just have the manifest and still install on the user's
device.
- Cache tldraw's assets as part of the dotcom vite asset pipeline. This
allows them to participate in the asset coalescing (preserving old
versions of asset files so old clients don't stop working when you
deploy new versions of things, see
tldraw/brivate#3132 for more context).
- Add a new 'imports.vite.js' file to the assets package, because we
import a bunch of json translation files, and vite imports .json files
as parsed json objects instead of string urls, and there's no good way
to tell it not to. Even if there was we wouldn't want to impose that
config on our users. So another way to tell vite to load any asset as a
url string is to append `?url` to the end of the import path. That's
what this file does.

closes [#2486](#2486)

### Change Type

- [x] `minor` — New feature


[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Release Notes

- Fix 'could not load assets' error that we often see on tldraw.com
after a deploy
  • Loading branch information
ds300 committed Jan 19, 2024
1 parent be927df commit ade3824
Show file tree
Hide file tree
Showing 16 changed files with 581 additions and 704 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ apps/vscode/extension/editor/tldraw-assets.json
**/sentry.server.config.js
**/scripts/upload-sourcemaps.js
**/coverage/**/*

apps/dotcom/public/sw.js
1 change: 0 additions & 1 deletion apps/dotcom/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
/out/

# PWA build artifacts
/public/*.js
/dev-dist

# production
Expand Down
1 change: 0 additions & 1 deletion apps/dotcom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"fast-glob": "^3.3.1",
"lazyrepo": "0.0.0-alpha.27",
"vite": "^5.0.0",
"vite-plugin-pwa": "^0.17.0",
"ws": "^8.16.0"
},
"jest": {
Expand Down
38 changes: 38 additions & 0 deletions apps/dotcom/public/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "tldraw",
"short_name": "tldraw",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"lang": "en",
"scope": "/",
"description": "a very good free whiteboard",
"icons": [
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/android-chrome-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/android-chrome-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
}
],
"theme_color": "#ffffff",
"orientation": "any"
}
19 changes: 19 additions & 0 deletions apps/dotcom/public/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Self-destroying service worker
// Taken from https://www.benjaminrancourt.ca/how-to-remove-a-service-worker/
// Inspired from https://github.com/NekR/self-destroying-sw
self.addEventListener("install", (event) => {
self.skipWaiting();
});

self.addEventListener("activate", (event) => {
self.registration
.unregister()
.then(() => self.clients.matchAll())
.then((clients) => {
clients.forEach((client) => {
if (client.url && "navigate" in client) {
client.navigate(client.url);
}
});
});
});
17 changes: 1 addition & 16 deletions apps/dotcom/src/components/LocalEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Editor, Tldraw } from '@tldraw/tldraw'
import { useCallback, useEffect } from 'react'
import { useCallback } from 'react'
import { assetUrls } from '../utils/assetUrls'
import { createAssetFromUrl } from '../utils/createAssetFromUrl'
import { isPreviewEnv } from '../utils/env'
import { linksUiOverrides } from '../utils/links'
import { DebugMenuItems } from '../utils/migration/DebugMenuItems'
import { LocalMigration } from '../utils/migration/LocalMigration'
Expand All @@ -14,8 +13,6 @@ import { ShareMenu } from './ShareMenu'
import { SneakyOnDropOverride } from './SneakyOnDropOverride'
import { ThemeUpdater } from './ThemeUpdater/ThemeUpdater'

const TLDRAW_REDIRECTED_TO_SIGN_IN = 'tldraw-redirected-to-sign-in'

export function LocalEditor() {
const handleUiEvent = useHandleUiEvents()
const sharingUiOverrides = useSharing({ isMultiplayer: false })
Expand All @@ -25,18 +22,6 @@ export function LocalEditor() {
editor.registerExternalAssetHandler('url', createAssetFromUrl)
}, [])

// Redirect to sign in if in preview mode
useEffect(() => {
if (isPreviewEnv) {
const alreadyRedirected = localStorage.getItem(TLDRAW_REDIRECTED_TO_SIGN_IN)
// We only want to redirect once so that we can still test the editor
if (alreadyRedirected && alreadyRedirected === 'true') return
localStorage.setItem(TLDRAW_REDIRECTED_TO_SIGN_IN, 'true')
const url = new URL(window.location.href)
window.location.assign(`${url.origin}/sign-in`)
}
}, [])

return (
<div className="tldraw__editor">
<Tldraw
Expand Down
12 changes: 12 additions & 0 deletions apps/dotcom/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,15 @@ createRoot(document.getElementById('root')!).render(
<VercelAnalytics debug={false} />
</HelmetProvider>
)

try {
// we have a dummy service worker that unregisters itself immediately
// this was needed to remove the service worker we used to have from the cache
// we can remove this if we ever need a service worker again, or if enough time passes that
// anybody returning to tldraw.com should not have a service worker running
navigator.serviceWorker.register('/sw.js', {
scope: '/',
})
} catch (e) {
// ignore
}
18 changes: 2 additions & 16 deletions apps/dotcom/src/utils/assetUrls.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
// eslint-disable-next-line import/no-internal-modules
import type { AssetUrls } from '@tldraw/assets/types'
// eslint-disable-next-line import/no-internal-modules
import { getAssetUrlsByMetaUrl } from '@tldraw/assets/urls'
import { isProductionEnv } from './env'

const _assetUrls = getAssetUrlsByMetaUrl()
import { getAssetUrlsByImport } from '@tldraw/assets/imports.vite'

export const assetUrls: AssetUrls = isProductionEnv
? _assetUrls
: // let's try out shantell sans 'informal' style for a bit on staging/dev
{
..._assetUrls,
fonts: {
..._assetUrls.fonts,
draw: '/Shantell_Sans-Tldrawish.woff2',
},
}
export const assetUrls = getAssetUrlsByImport()

let didPreloadIcons = false
async function preloadIcons() {
Expand Down
51 changes: 1 addition & 50 deletions apps/dotcom/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import react from '@vitejs/plugin-react-swc'
import { config } from 'dotenv'
import { defineConfig } from 'vite'
import { VitePWA, VitePWAOptions } from 'vite-plugin-pwa'

config({
path: './.env.local',
Expand All @@ -11,57 +10,9 @@ export const getMultiplayerServerURL = () => {
return process.env.MULTIPLAYER_SERVER?.replace(/^ws/, 'http') ?? 'http://127.0.0.1:8787'
}

const pwaConfig: Partial<VitePWAOptions> = {
registerType: 'autoUpdate',
// Make sure the service worker doesn't try to handle API requests
workbox: {
navigateFallbackDenylist: [/^\/api/],
runtimeCaching: [{ handler: 'NetworkFirst', urlPattern: /\/.*/ }],
},
// Uncomment this to test the PWA install flow locally
// devOptions: { enabled: true },
manifest: {
name: 'tldraw',
short_name: 'tldraw',
description: 'a very good free whiteboard',

icons: [
{
src: '/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any',
},
{
src: '/android-chrome-maskable-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
{
src: '/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any',
},
{
src: '/android-chrome-maskable-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any maskable',
},
],
theme_color: '#ffffff',
background_color: '#ffffff',
start_url: '/',
display: 'standalone',
orientation: 'any',
},
}

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react({ tsDecorators: true }), VitePWA(pwaConfig)],
plugins: [react({ tsDecorators: true })],
publicDir: './public',
build: {
// output source maps to .map files and include //sourceMappingURL comments in JavaScript files
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"build-types": "lazy inherit",
"build-api": "lazy build-api",
"build-package": "lazy build-package",
"preview-app": "VITE_PREVIEW=1 yarn dev-app",
"lint": "lazy lint",
"format": "prettier --write --cache \"**/*.{ts,tsx,js,jsx,json}\"",
"typecheck": "yarn refresh-assets && tsx scripts/typecheck.ts",
Expand Down
2 changes: 2 additions & 0 deletions packages/assets/imports.vite.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { AssetUrlOptions, AssetUrls } from './types'
export function getAssetUrlsByImport(opts?: AssetUrlOptions): AssetUrls

1 comment on commit ade3824

@vercel
Copy link

@vercel vercel bot commented on ade3824 Jan 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.