a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

use uuidv7 for facts and entities instead of v4

Should probably use them everywhere eventually but for now this works!

+31 -19
+2 -1
app/page.tsx
··· 9 9 import postgres from "postgres"; 10 10 import { Doc } from "./[doc_id]/Doc"; 11 11 import { UpdateURL } from "components/UpdateURL"; 12 + import { v7 } from "uuid"; 12 13 const client = postgres(process.env.DB_URL as string); 13 14 const db = drizzle(client); 14 15 ··· 26 27 let [entity] = await tx 27 28 .insert(entities) 28 29 // And add it to that permission set 29 - .values({ set: entity_set.id }) 30 + .values({ set: entity_set.id, id: v7() }) 30 31 .returning(); 31 32 //Create a new permission token 32 33 let [permissionToken] = await tx
+4 -3
components/BlockOptions.tsx
··· 14 14 import { Separator } from "./Layout"; 15 15 import { addLinkBlock } from "src/utils/addLinkBlock"; 16 16 import { useEntitySetContext } from "./EntitySetProvider"; 17 + import { v7 } from "uuid"; 17 18 18 19 type Props = { 19 20 parent: string; ··· 53 54 await rep.mutate.retractFact({ factID: props.factID }); 54 55 let entity = props.entityID; 55 56 if (!entity) { 56 - entity = crypto.randomUUID(); 57 + entity = v7(); 57 58 await rep?.mutate.addBlock({ 58 59 parent: props.parent, 59 60 permission_set: entity_set.set, ··· 85 86 onMouseDown={(e) => e.preventDefault()} 86 87 onClick={async () => { 87 88 if (!props.entityID) { 88 - let entity = crypto.randomUUID(); 89 + let entity = v7(); 89 90 90 91 await rep?.mutate.addBlock({ 91 92 permission_set: entity_set.set, ··· 125 126 let submit = async () => { 126 127 let entity = props.entityID; 127 128 if (!entity) { 128 - entity = crypto.randomUUID(); 129 + entity = v7(); 129 130 130 131 await rep?.mutate.addBlock({ 131 132 permission_set: entity_set.set,
+6 -4
components/Blocks.tsx
··· 15 15 import { setEditorState, useEditorStates } from "src/state/useEditorState"; 16 16 import { useEntitySetContext } from "./EntitySetProvider"; 17 17 import { scanIndex } from "src/replicache/utils"; 18 + import { v7 } from "uuid"; 18 19 19 20 export type Block = { 20 21 parent: string; ··· 38 39 !lastBlock || 39 40 (lastBlock.type !== "text" && lastBlock.type !== "heading") 40 41 ) { 41 - let newEntityID = crypto.randomUUID(); 42 + let newEntityID = v7(); 42 43 await rep.rep?.mutate.addBlock({ 43 44 parent: props.entityID, 44 45 permission_set: entity_set.set, ··· 75 76 <div 76 77 className="shrink-0 h-[50vh]" 77 78 onClick={() => { 78 - let newEntityID = crypto.randomUUID(); 79 + let newEntityID = v7(); 79 80 80 81 if (lastBlock && lastBlock.type === "text") { 81 82 focusBlock({ ...lastBlock, type: "text" }, { type: "end" }); ··· 117 118 <div 118 119 className="h-6 hover:cursor-text italic text-tertiary" 119 120 onMouseDown={async () => { 120 - let newEntityID = crypto.randomUUID(); 121 + let newEntityID = v7(); 121 122 await rep?.mutate.addBlock({ 122 123 parent: props.entityID, 123 124 type: "text", ··· 204 205 if (block) focusBlock(block, { type: "end" }); 205 206 } 206 207 if (e.key === "Enter") { 207 - let newEntityID = crypto.randomUUID(); 208 + let newEntityID = v7(); 208 209 r.mutate.addBlock({ 209 210 permission_set: entity_set.set, 210 211 newEntityID, ··· 239 240 <div 240 241 data-entityid={props.entityID} 241 242 onMouseDown={(e) => { 243 + useSelectingMouse.setState({ start: props.entityID }); 242 244 if (e.shiftKey) { 243 245 e.preventDefault(); 244 246 useUIState.getState().addBlockToSelection(props);
+2 -1
components/TextBlock/keymap.ts
··· 11 11 import { useUIState } from "src/useUIState"; 12 12 import { setEditorState, useEditorStates } from "src/state/useEditorState"; 13 13 import { focusCard } from "components/Cards"; 14 + import { v7 } from "uuid"; 14 15 15 16 export const TextBlockKeymap = ( 16 17 propsRef: MutableRefObject<BlockProps & { entity_set: { set: string } }>, ··· 250 251 let newContent = tr.doc.slice(state.selection.anchor); 251 252 tr.delete(state.selection.anchor, state.doc.content.size); 252 253 dispatch?.(tr); 253 - let newEntityID = crypto.randomUUID(); 254 + let newEntityID = v7(); 254 255 let position = generateKeyBetween( 255 256 propsRef.current.position, 256 257 propsRef.current.nextPosition,
+4 -3
components/TextBlock/useHandlePaste.ts
··· 8 8 import { addImage } from "src/utils/addImage"; 9 9 import { BlockProps, focusBlock } from "components/Blocks"; 10 10 import { useEntitySetContext } from "components/EntitySetProvider"; 11 + import { v7 } from "uuid"; 11 12 12 13 export const useHandlePaste = ( 13 14 entityID: string, ··· 67 68 if (index === 0 && type === propsRef.current.type) 68 69 entityID = propsRef.current.entityID; 69 70 else { 70 - entityID = crypto.randomUUID(); 71 + entityID = v7(); 71 72 currentPosition = generateKeyBetween( 72 73 currentPosition, 73 74 propsRef.current.nextPosition, ··· 121 122 .filter((f) => !!f); 122 123 let currentPosition = propsRef.current.position; 123 124 for (let p of paragraphs) { 124 - let newEntityID = crypto.randomUUID(); 125 + let newEntityID = v7(); 125 126 currentPosition = generateKeyBetween( 126 127 currentPosition, 127 128 propsRef.current.nextPosition, ··· 161 162 }); 162 163 if (factID) rep.mutate.retractFact({ factID: factID }); 163 164 } else { 164 - entity = crypto.randomUUID(); 165 + entity = v7(); 165 166 rep.mutate.addBlock({ 166 167 permission_set: entity_set.set, 167 168 type: "image",
+2 -2
drizzle/schema.ts
··· 21 21 }); 22 22 23 23 export const entities = pgTable("entities", { 24 - id: uuid("id").defaultRandom().primaryKey().notNull(), 24 + id: uuid("id").primaryKey().notNull(), 25 25 created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 26 26 set: uuid("set").notNull().references(() => entity_sets.id, { onDelete: "cascade", onUpdate: "cascade" } ), 27 27 }); ··· 37 37 }); 38 38 39 39 export const facts = pgTable("facts", { 40 - id: uuid("id").defaultRandom().primaryKey().notNull(), 40 + id: uuid("id").primaryKey().notNull(), 41 41 entity: uuid("entity").notNull().references(() => entities.id, { onDelete: "cascade", onUpdate: "restrict" } ), 42 42 attribute: text("attribute").notNull(), 43 43 data: jsonb("data").notNull(),
+2 -1
src/replicache/clientMutationContext.ts
··· 6 6 import { Fact } from "."; 7 7 import { MutationContext } from "./mutations"; 8 8 import { supabaseBrowserClient } from "supabase/browserClient"; 9 + import { v7 } from "uuid"; 9 10 10 11 export function clientMutationContext(tx: WriteTransaction) { 11 12 let ctx: MutationContext = { ··· 32 33 async assertFact(f) { 33 34 let attribute = Attributes[f.attribute as keyof typeof Attributes]; 34 35 if (!attribute) return; 35 - let id = f.id || crypto.randomUUID(); 36 + let id = f.id || v7(); 36 37 let data = { ...f.data }; 37 38 if (attribute.cardinality === "one") { 38 39 let existingFact = await tx
+2 -1
src/replicache/serverMutationContext.ts
··· 9 9 import { DeepReadonly } from "replicache"; 10 10 import { createClient } from "@supabase/supabase-js"; 11 11 import { Database } from "supabase/database.types"; 12 + import { v7 } from "uuid"; 12 13 export function serverMutationContext( 13 14 tx: PgTransaction<any, any, any>, 14 15 token_rights: PermissionToken["permission_token_rights"], ··· 77 78 async assertFact(f) { 78 79 let attribute = Attributes[f.attribute as keyof typeof Attributes]; 79 80 if (!attribute) return; 80 - let id = f.id || crypto.randomUUID(); 81 + let id = f.id || v7(); 81 82 let data = { ...f.data }; 82 83 let [permission_set] = await tx 83 84 .select({ entity_set: entities.set })
+2 -1
src/utils/addImage.ts
··· 3 3 import { supabaseBrowserClient } from "supabase/browserClient"; 4 4 import { FilterAttributes } from "src/replicache/attributes"; 5 5 import { rgbaToDataURL, rgbaToThumbHash, thumbHashToDataURL } from "thumbhash"; 6 + import { v7 } from "uuid"; 6 7 7 8 export async function addImage( 8 9 file: File, ··· 14 15 ) { 15 16 let client = supabaseBrowserClient(); 16 17 let cache = await caches.open("minilink-user-assets"); 17 - let fileID = crypto.randomUUID(); 18 + let fileID = v7(); 18 19 let url = client.storage.from("minilink-user-assets").getPublicUrl(fileID) 19 20 .data.publicUrl; 20 21 let dimensions = await getImageDimensions(file);
+2 -2
supabase/database.types.ts
··· 42 42 } 43 43 Insert: { 44 44 created_at?: string 45 - id?: string 45 + id: string 46 46 set: string 47 47 } 48 48 Update: { ··· 90 90 created_at?: string 91 91 data: Json 92 92 entity: string 93 - id?: string 93 + id: string 94 94 updated_at?: string | null 95 95 version?: number 96 96 }
+3
supabase/migrations/20240707221949_make_ids_mandatory_for_entities_and_facts.sql
··· 1 + alter table "public"."entities" alter column "id" drop default; 2 + 3 + alter table "public"."facts" alter column "id" drop default;