diff --git a/.gitignore b/.gitignore index 4dc8d0e52..c10c31114 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ coverage apps/www/public/worker-* apps/www/public/sw.js apps/www/public/sw.js.map +.env \ No newline at end of file diff --git a/README.md b/README.md index 0dcb937ad..743be0a25 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
- +
Welcome to the [tldraw](https://tldraw.com) monorepo. Here you'll find the source code for [@tldraw/tldraw](https://www.npmjs.com/package/@tldraw/tldraw), [@tldraw/core](https://www.npmjs.com/package/@tldraw/core), and the tldraw.com website. @@ -8,6 +8,14 @@ Welcome to the [tldraw](https://tldraw.com) monorepo. Here you'll find the sourc 💕 Love this project? Consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok). +Thanks to our corporate sponsors: + + + + + +...and to our [individual sponsors](https://github.com/sponsors/steveruizok#sponsors)! + ## Contents This repository is a monorepo containing two packages: @@ -27,18 +35,18 @@ This repository is a monorepo containing two packages: - [**examples/core-example-advanced**](https://github.com/tldraw/tldraw/tree/main/examples/core-example-advanced) is a second example for `@tldraw/core`. - [**examples/tldraw-example**](https://github.com/tldraw/tldraw/tree/main/examples/tldraw-example) is an example for `@tldraw/tldraw`. +## Discussion + +Want to connect? Visit the [Discord channel](https://discord.gg/SBBEVCA4PG). + ## Contribution -See the [contributing guide](/CONTRIBUTING.md). +Interested in contributing? See the [contributing guide](/CONTRIBUTING.md). ## Support Need help? Please [open an issue](https://github.com/tldraw/tldraw/issues/new) for support. -## Discussion - -Want to connect with other devs? Visit the [Discord channel](https://discord.gg/SBBEVCA4PG). - ## License This project is licensed under MIT. diff --git a/apps/www/.env b/apps/www/.env deleted file mode 100644 index b298903cd..000000000 --- a/apps/www/.env +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY=pk_live_1LJGGaqBSNLjLT-4Jalkl-U9 \ No newline at end of file diff --git a/apps/www/components/Editor.tsx b/apps/www/components/Editor.tsx index 4962cd3f4..cd6ec81e3 100644 --- a/apps/www/components/Editor.tsx +++ b/apps/www/components/Editor.tsx @@ -11,7 +11,7 @@ interface EditorProps { isSponsor?: boolean } -export default function Editor({ id = 'home', isSponsor = false }: EditorProps) { +export default function Editor({ id = 'home', isUser = false, isSponsor = false }: EditorProps) { const handleMount = React.useCallback((app: TldrawApp) => { window.app = app }, []) @@ -39,7 +39,7 @@ export default function Editor({ id = 'home', isSponsor = false }: EditorProps) onPersist={handlePersist} showSponsorLink={!isSponsor} onSignIn={isSponsor ? undefined : onSignIn} - onSignOut={onSignOut} + onSignOut={isUser ? onSignOut : undefined} {...fileSystemEvents} /> diff --git a/apps/www/components/MultiplayerEditor.tsx b/apps/www/components/MultiplayerEditor.tsx index 3d8c45b46..e3d5fa006 100644 --- a/apps/www/components/MultiplayerEditor.tsx +++ b/apps/www/components/MultiplayerEditor.tsx @@ -38,7 +38,15 @@ export default function MultiplayerEditor({ // Inner Editor -function Editor({ roomId, isSponsor }: { roomId: string; isUser; isSponsor: boolean }) { +function Editor({ + roomId, + isUser, + isSponsor, +}: { + roomId: string + isUser: boolean + isSponsor: boolean +}) { const [docId] = React.useState(() => Utils.uniqueId()) const [app, setApp] = React.useState() @@ -175,7 +183,7 @@ function Editor({ roomId, isSponsor }: { roomId: string; isUser; isSponsor: bool showPages={false} showSponsorLink={isSponsor} onSignIn={isSponsor ? undefined : onSignIn} - onSignOut={onSignOut} + onSignOut={isUser ? onSignOut : undefined} {...fileSystemEvents} /> diff --git a/apps/www/pages/api/auth/[...nextauth].ts b/apps/www/pages/api/auth/[...nextauth].ts index 949f09489..9583f9509 100644 --- a/apps/www/pages/api/auth/[...nextauth].ts +++ b/apps/www/pages/api/auth/[...nextauth].ts @@ -1,3 +1,4 @@ +import { isSponsoringMe } from '-utils/isSponsoringMe' import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' import NextAuth from 'next-auth' import Providers from 'next-auth/providers' @@ -30,28 +31,3 @@ export default function Auth( }, }) } - -const whitelist = ['steveruizok'] - -async function isSponsoringMe(login: string) { - if (whitelist.includes(login)) return true - - const res = await fetch('https://api.github.com/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: 'bearer ' + process.env.GITHUB_API_SECRET, - }, - body: JSON.stringify({ - query: ` - query { - user(login: "steveruizok") { - isSponsoredBy(accountLogin: "${login}") - } - } - `, - }), - }).then((res) => res.json()) - - return res?.data?.user?.isSponsoredBy -} diff --git a/apps/www/pages/api/sponsors.ts b/apps/www/pages/api/sponsors.ts new file mode 100644 index 000000000..1b22ea817 --- /dev/null +++ b/apps/www/pages/api/sponsors.ts @@ -0,0 +1,96 @@ +import { NextApiRequest, NextApiResponse } from 'next' + +const AV_SIZE = 32 +const PADDING = 4 +const COLS = 16 + +type SponsorResult = { url: string; login: string } +type QueryResult = { + node: { sponsorEntity: { avatarUrl: string; login: string } } +} + +function getXY(i: number) { + return [(i % COLS) * (AV_SIZE + PADDING), Math.floor(i / COLS) * (AV_SIZE + PADDING)] +} + +export default async function GetSponsors(req: NextApiRequest, res: NextApiResponse) { + const sponsorInfo = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'bearer ' + process.env.GITHUB_API_SECRET, + }, + body: JSON.stringify({ + query: `{ + viewer { + sponsors(first: 0) { + totalCount + } + sponsorshipsAsMaintainer(first: 100, orderBy: { + field:CREATED_AT, + direction:DESC + }) { + edges { + node { + sponsorEntity { + ...on User { + avatarUrl + login + } + } + } + } + } + } + }`, + }), + }).then((res) => res.json()) + + const totalCount: number = sponsorInfo.data.viewer.sponsors.totalCount + + const results = ( + sponsorInfo.data.viewer.sponsorshipsAsMaintainer.edges as QueryResult[] + ).map((edge) => ({ + url: edge.node.sponsorEntity.avatarUrl?.replaceAll('&', '&') ?? '', + login: edge.node.sponsorEntity.login, + })) + + if (results.length % COLS <= 2) { + results.pop() + results.pop() + results.pop() + } + + // Avatars + + const avatars = results + .map(({ url, login }, i) => { + const [x, y] = getXY(i) + return `${login}` + }) + .join('') + + // More text + + const [x, y] = getXY(results.length) + const width = (AV_SIZE + PADDING) * 3 + const more = ` + ...and ${totalCount - 100} more!` + + const svgImage = ` +${avatars}${more}` + + // const html = ` + //
+ // ${images.join(` + // `)} + //
` + + res + .status(200) + .setHeader('Cache-Control', 'max-age=604800') + .setHeader('Content-Type', 'image/svg+xml') + .send(svgImage) +} diff --git a/apps/www/pages/index.tsx b/apps/www/pages/index.tsx index ae2483613..784bf855d 100644 --- a/apps/www/pages/index.tsx +++ b/apps/www/pages/index.tsx @@ -26,7 +26,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { - isUser: false, + isUser: session?.user, isSponsor: session?.user ? true : false, }, } diff --git a/apps/www/pages/r/[id].tsx b/apps/www/pages/r/[id].tsx index a228b21e7..fc4b09af4 100644 --- a/apps/www/pages/r/[id].tsx +++ b/apps/www/pages/r/[id].tsx @@ -22,7 +22,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { id, - isUser: false, + isUser: session?.user, isSponsor: session?.user ? true : false, }, } diff --git a/apps/www/utils/isSponsoringMe.ts b/apps/www/utils/isSponsoringMe.ts new file mode 100644 index 000000000..0e21fc88e --- /dev/null +++ b/apps/www/utils/isSponsoringMe.ts @@ -0,0 +1,24 @@ +const whitelist = ['steveruizok'] + +export async function isSponsoringMe(login: string) { + if (whitelist.includes(login)) return true + + const res = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'bearer ' + process.env.GITHUB_API_SECRET, + }, + body: JSON.stringify({ + query: ` + query { + user(login: "steveruizok") { + isSponsoredBy(accountLogin: "${login}") + } + } + `, + }), + }).then((res) => res.json()) + + return res?.data?.user?.isSponsoredBy +} diff --git a/assets/sentry.svg b/assets/sentry.svg new file mode 100644 index 000000000..bfc892b42 --- /dev/null +++ b/assets/sentry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/card-repo.png b/assets/tldraw.png similarity index 100% rename from card-repo.png rename to assets/tldraw.png diff --git a/assets/vercel.svg b/assets/vercel.svg new file mode 100644 index 000000000..c7dc242a9 --- /dev/null +++ b/assets/vercel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/examples/tldraw-example/.env b/examples/tldraw-example/.env deleted file mode 100644 index cd40368bd..000000000 --- a/examples/tldraw-example/.env +++ /dev/null @@ -1 +0,0 @@ -LIVEBLOCKS_PUBLIC_API_KEY=pk_live_1LJGGaqBSNLjLT-4Jalkl-U9 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 93f9db273..e8d1cf21a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9341,7 +9341,7 @@ jose@^1.27.2: dependencies: "@panva/asn1.js" "^1.0.0" -jpeg-js@^0.4.2: +jpeg-js@^0.4.1, jpeg-js@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b" integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q== @@ -11183,6 +11183,11 @@ only@~0.0.2: resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= +opentype.js@^0.4.3: + version "0.4.11" + resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-0.4.11.tgz#281a2390639cc15931c955d8d63c14a7c7772b41" + integrity sha1-KBojkGOcwVkxyVXY1jwUp8d3K0E= + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -11711,6 +11716,11 @@ plist@^3.0.1: base64-js "^1.5.1" xmlbuilder "^9.0.7" +pngjs@^3.3.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" + integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== + pngjs@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" @@ -11975,6 +11985,15 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" +pureimage@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.3.6.tgz#3ab6070e2779193a8767fc9b657d41c6894b070f" + integrity sha512-Wtk+QdlB1X7wnfaXads+5i6tI95dNMyR7Hq9Q0qmEOXiG38JvPRcAISSzuSVGXPlgi54VFzl26ic8UpT6GLs6g== + dependencies: + jpeg-js "^0.4.1" + opentype.js "^0.4.3" + pngjs "^3.3.1" + q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"