From 5018245587f6b15ef477e4d8892c431fbf1667cf Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 17:48:30 -0700 Subject: [PATCH 01/94] redux-undo add --- mirror-2/package.json | 1 + mirror-2/yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/mirror-2/package.json b/mirror-2/package.json index 69f0e233..da9dd093 100644 --- a/mirror-2/package.json +++ b/mirror-2/package.json @@ -39,6 +39,7 @@ "react-hook-form": "^7.53.0", "react-redux": "^9.1.2", "react-resizable-panels": "^2.1.4", + "redux-undo": "^1.1.0", "server-only": "^0.0.1", "unique-names-generator": "^4.7.1", "use-sound": "^4.0.3", diff --git a/mirror-2/yarn.lock b/mirror-2/yarn.lock index 4a103548..655e15b0 100644 --- a/mirror-2/yarn.lock +++ b/mirror-2/yarn.lock @@ -1558,6 +1558,11 @@ redux-thunk@^3.1.0: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== +redux-undo@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/redux-undo/-/redux-undo-1.1.0.tgz#ae28e517797b1aa5521d55da561f7214d310f8a5" + integrity sha512-zzLFh2qeF0MTIlzDhDLm9NtkfBqCllQJ3OCuIl5RKlG/ayHw6GUdIFdMhzMS9NnrnWdBX5u//ExMOHpfudGGOg== + redux@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" From 575f355555c9f3f8e68f3eaae8ad2af3e5f71500 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:22:40 -0700 Subject: [PATCH 02/94] fix build layout --- mirror-2/app/layout.client.tsx | 2 +- .../[spaceId]/build/(controlBar)/assets.tsx | 2 +- mirror-2/app/space/[spaceId]/build/layout.tsx | 20 +++++-------------- .../space/[spaceId]/build/space-viewport.tsx | 20 ++++++++++--------- mirror-2/app/space/new/page.tsx | 2 +- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/mirror-2/app/layout.client.tsx b/mirror-2/app/layout.client.tsx index b1a948ca..51435a2b 100644 --- a/mirror-2/app/layout.client.tsx +++ b/mirror-2/app/layout.client.tsx @@ -23,7 +23,7 @@ export default function ClientLayout({ children }) { export function AuthLayout({ children }) { useSetupAuthEvents() return ( -
+
{children}
diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index 34bdcf14..d765ebb2 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -44,7 +44,7 @@ export default function Assets() { }, [isSuccess, form]); // Only run this effect when space or isLoading changes return ( -
+
{/* Search bar */} {/* */}
diff --git a/mirror-2/app/space/[spaceId]/build/layout.tsx b/mirror-2/app/space/[spaceId]/build/layout.tsx index bc17b606..055f511d 100644 --- a/mirror-2/app/space/[spaceId]/build/layout.tsx +++ b/mirror-2/app/space/[spaceId]/build/layout.tsx @@ -1,11 +1,7 @@ -import Link from "next/link" - -import { TopNavbar } from "@/app/space/[spaceId]/build/top-navbar" -import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable" -import { AppLogoImageSmall } from "@/lib/theme-service" +import InnerControlBar from "@/app/space/[spaceId]/build/(controlBar)/inner-control-bar" import { Sidebar } from "@/app/space/[spaceId]/build/sidebar" import SpaceViewport from "@/app/space/[spaceId]/build/space-viewport" -import InnerControlBar from "@/app/space/[spaceId]/build/(controlBar)/inner-control-bar" +import { TopNavbar } from "@/app/space/[spaceId]/build/top-navbar" export default async function Layout({ children, params }: { children: React.ReactNode, @@ -14,17 +10,11 @@ export default async function Layout({ children, params }: { }) { return ( -
-
- -
-
+
+ +
-
-
-
-
diff --git a/mirror-2/app/space/[spaceId]/build/space-viewport.tsx b/mirror-2/app/space/[spaceId]/build/space-viewport.tsx index 8c972193..b86b4e30 100644 --- a/mirror-2/app/space/[spaceId]/build/space-viewport.tsx +++ b/mirror-2/app/space/[spaceId]/build/space-viewport.tsx @@ -1,11 +1,13 @@ export default function SpaceViewport() { - return
-
-

- 3D Viewport -

-
-
+ return ( +
+
+

+ 3D Viewport +

+
+
+ ); } diff --git a/mirror-2/app/space/new/page.tsx b/mirror-2/app/space/new/page.tsx index 8ec94c1a..d58ac5e8 100644 --- a/mirror-2/app/space/new/page.tsx +++ b/mirror-2/app/space/new/page.tsx @@ -23,7 +23,7 @@ export default function NewSpacePage() { create() }, [router]) return ( -
+
{/* Top Menu Bar */}
From c9abaa1cc868a7f87bfe131108729c2ddcf5cb53 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:23:40 -0700 Subject: [PATCH 03/94] add engine --- mirror-2/package.json | 1 + mirror-2/yarn.lock | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/mirror-2/package.json b/mirror-2/package.json index da9dd093..4e8d886a 100644 --- a/mirror-2/package.json +++ b/mirror-2/package.json @@ -33,6 +33,7 @@ "lucide-react": "^0.446.0", "next": "latest", "next-themes": "^0.3.0", + "playcanvas": "^2.0.0", "prettier": "^3.3.3", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/mirror-2/yarn.lock b/mirror-2/yarn.lock index 655e15b0..92a5644a 100644 --- a/mirror-2/yarn.lock +++ b/mirror-2/yarn.lock @@ -752,6 +752,11 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/webxr@^0.5.16": + version "0.5.20" + resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.20.tgz#b16b681af314ec011b2e8221b0a072d691c04953" + integrity sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg== + "@types/ws@^8.5.10": version "8.5.12" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" @@ -759,6 +764,11 @@ dependencies: "@types/node" "*" +"@webgpu/types@^0.1.40": + version "0.1.48" + resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.48.tgz#8ab741852283118bd633345c20e218faa7211e9c" + integrity sha512-e3zmDEPih4Rle+JrP5cT8nCCtDizoUpEaN72OuD1clbhXGERtn0wwuMdxOrBymu3kMLWKDd8hd+ERhSheLuLTg== + acorn-walk@^8.0.0: version "8.3.4" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" @@ -1386,6 +1396,14 @@ pirates@^4.0.1: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +playcanvas@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/playcanvas/-/playcanvas-2.0.0.tgz#ef298a2609c0e1c551709ed4e00d64c13932bd2b" + integrity sha512-hEbIgL3ZkB8GyawN3gHpxYl+ZDO+lE3vEFs2TKe8jnT6b9CcF6yzITLorTkRbkd7dyMz1J0+633qH4HeD7EZpQ== + dependencies: + "@types/webxr" "^0.5.16" + "@webgpu/types" "^0.1.40" + postcss-import@^15.1.0: version "15.1.0" resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" From 0d903f955b6884e56f9c9c0c8e141cc44bdd25aa Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:26:36 -0700 Subject: [PATCH 04/94] layout clean --- mirror-2/app/space/[spaceId]/build/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirror-2/app/space/[spaceId]/build/layout.tsx b/mirror-2/app/space/[spaceId]/build/layout.tsx index 055f511d..ed8fb7e3 100644 --- a/mirror-2/app/space/[spaceId]/build/layout.tsx +++ b/mirror-2/app/space/[spaceId]/build/layout.tsx @@ -12,7 +12,7 @@ export default async function Layout({ children, params }: { return (
-
+
From a591e51a733904a3731bd2fd58e2501811fd60b0 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:30:45 -0700 Subject: [PATCH 05/94] search asset fix --- mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx | 2 -- mirror-2/state/supabase.tsx | 1 - 2 files changed, 3 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index d765ebb2..906759c8 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -46,7 +46,6 @@ export default function Assets() { return (
{/* Search bar */} - {/* */} - {/* */} {/* Scrollable area that takes up remaining space */} diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index 84024f38..e16ecba7 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -129,7 +129,6 @@ export const supabaseApi = createApi({ .from("assets") .select("*") .eq("name", text) - .single() if (error) { return { error: error.message }; From c0ec83939be0832b4d0742d4527277acad8406b9 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:34:34 -0700 Subject: [PATCH 06/94] break out assets migration into separate file --- ...s_assets.sql => 20240929043655_spaces.sql} | 26 ---------- .../migrations/20240929043656_assets.sql | 50 +++++++++++++++++++ 2 files changed, 50 insertions(+), 26 deletions(-) rename mirror-2/supabase/migrations/{20240929043655_spaces_assets.sql => 20240929043655_spaces.sql} (59%) create mode 100644 mirror-2/supabase/migrations/20240929043656_assets.sql diff --git a/mirror-2/supabase/migrations/20240929043655_spaces_assets.sql b/mirror-2/supabase/migrations/20240929043655_spaces.sql similarity index 59% rename from mirror-2/supabase/migrations/20240929043655_spaces_assets.sql rename to mirror-2/supabase/migrations/20240929043655_spaces.sql index da2ceab6..cc2f1754 100644 --- a/mirror-2/supabase/migrations/20240929043655_spaces_assets.sql +++ b/mirror-2/supabase/migrations/20240929043655_spaces.sql @@ -12,11 +12,6 @@ create table spaces ( alter table spaces enable row level security; --- Policy for space owners -create policy "Owners can view their own spaces" -on spaces -for select -using (owner_user_id = auth.uid()); -- Policy for creating spaces create policy "Users can create their own spaces" @@ -46,24 +41,3 @@ create policy "Users can delete their own spaces" on spaces for delete using (owner_user_id = auth.uid()); - --- assets -create table assets ( - id uuid not null primary key default uuid_generate_v4(), - owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Assets can be transferred and we want to retain the creator - creator_user_id uuid references auth.users(id) not null, - name text not null, - asset_url text not null, - created_at timestamp with time zone not null default now(), - updated_at timestamp with time zone not null default now() - ); - --- add RLS -alter table assets - enable row level security; - --- set up storage for assets -insert into - storage.buckets (id, name, public) -values - ('assets', 'assets', true); diff --git a/mirror-2/supabase/migrations/20240929043656_assets.sql b/mirror-2/supabase/migrations/20240929043656_assets.sql new file mode 100644 index 00000000..aed9d98f --- /dev/null +++ b/mirror-2/supabase/migrations/20240929043656_assets.sql @@ -0,0 +1,50 @@ +-- assets +create table assets ( + id uuid not null primary key default uuid_generate_v4(), + owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Assets can be transferred and we want to retain the creator + creator_user_id uuid references auth.users(id) not null, + name text not null, + asset_url text not null, + created_at timestamp with time zone not null default now(), + updated_at timestamp with time zone not null default now() + ); + +-- add RLS +alter table assets + enable row level security; + + -- Policy for creating assets +create policy "Users can create their own assets" +on assets +for insert +with check ( + owner_user_id = auth.uid() + and creator_user_id = auth.uid() +); + +-- Policy for selecting assets +create policy "Users can view their own assets" +on assets +for select +using ( + owner_user_id = auth.uid() +); + +-- Policy for updating assets +create policy "Users can update their own assets" +on assets +for update +using (owner_user_id = auth.uid()); + +-- Policy for deleting assets +create policy "Users can delete their own assets" +on assets +for delete +using (owner_user_id = auth.uid()); + + +-- set up storage for assets +insert into + storage.buckets (id, name, public) +values + ('assets', 'assets', true); From 65c99726ed7e45c264e385a8a276215b35ae1901 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:35:44 -0700 Subject: [PATCH 07/94] home fix --- mirror-2/app/space/[spaceId]/build/top-navbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirror-2/app/space/[spaceId]/build/top-navbar.tsx b/mirror-2/app/space/[spaceId]/build/top-navbar.tsx index 6cb05e00..8288dc78 100644 --- a/mirror-2/app/space/[spaceId]/build/top-navbar.tsx +++ b/mirror-2/app/space/[spaceId]/build/top-navbar.tsx @@ -21,7 +21,7 @@ export function TopNavbar() { }, []); return (
- +
From c0b0da9bef4956512dbc48d3816bb37c9b259e66 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:43:58 -0700 Subject: [PATCH 08/94] text search assets --- mirror-2/state/supabase.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index e16ecba7..569245b3 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -128,7 +128,7 @@ export const supabaseApi = createApi({ const { data, error } = await supabase .from("assets") .select("*") - .eq("name", text) + .textSearch("name", text) if (error) { return { error: error.message }; From 3aa15443b64dfb990c17b330b7f215fe5fffe642 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 19:59:25 -0700 Subject: [PATCH 09/94] add description, computed columns --- mirror-2/package.json | 1 + mirror-2/state/supabase.tsx | 2 +- .../migrations/20240929043655_spaces.sql | 1 + .../migrations/20240929043656_assets.sql | 1 + .../20241005025417_searches_assets_spaces.sql | 16 ++++++++++++++++ mirror-2/supabase/seed.sql | 8 ++++---- mirror-2/utils/database.types.ts | 13 +++++++++++++ 7 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql diff --git a/mirror-2/package.json b/mirror-2/package.json index 4e8d886a..4c8a5823 100644 --- a/mirror-2/package.json +++ b/mirror-2/package.json @@ -5,6 +5,7 @@ "types": "npx supabase gen types --lang=typescript --local > ./utils/database.types.ts", "reset": "supabase db reset && yarn types", "build": "next build", + "m": "supabase migration new", "start": "next start" }, "dependencies": { diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index 569245b3..f54bbcb8 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -128,7 +128,7 @@ export const supabaseApi = createApi({ const { data, error } = await supabase .from("assets") .select("*") - .textSearch("name", text) + .textSearch("name_description", text) if (error) { return { error: error.message }; diff --git a/mirror-2/supabase/migrations/20240929043655_spaces.sql b/mirror-2/supabase/migrations/20240929043655_spaces.sql index cc2f1754..697c92e7 100644 --- a/mirror-2/supabase/migrations/20240929043655_spaces.sql +++ b/mirror-2/supabase/migrations/20240929043655_spaces.sql @@ -1,6 +1,7 @@ create table spaces ( id uuid not null primary key default uuid_generate_v4(), name text not null, + description text not null, owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Spaces can be transferred and we want to retain the creator creator_user_id uuid references auth.users(id) not null, created_at timestamp with time zone not null default now(), diff --git a/mirror-2/supabase/migrations/20240929043656_assets.sql b/mirror-2/supabase/migrations/20240929043656_assets.sql index aed9d98f..d71bd024 100644 --- a/mirror-2/supabase/migrations/20240929043656_assets.sql +++ b/mirror-2/supabase/migrations/20240929043656_assets.sql @@ -4,6 +4,7 @@ create table assets ( owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Assets can be transferred and we want to retain the creator creator_user_id uuid references auth.users(id) not null, name text not null, + description text not null, asset_url text not null, created_at timestamp with time zone not null default now(), updated_at timestamp with time zone not null default now() diff --git a/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql b/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql new file mode 100644 index 00000000..c0549c54 --- /dev/null +++ b/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql @@ -0,0 +1,16 @@ +-- Computed name description column +create function name_description(assets) returns text as $$ + select $1.name || ' ' || $1.description; +$$ language sql immutable; + +create function name_description(spaces) returns text as $$ + select $1.name || ' ' || $1.description; +$$ language sql immutable; + +-- create or replace function search_assets_by_title_description(prefix text) +-- returns setof assets AS $$ +-- begin +-- return query +-- select * from assets where to_tsvector('english', title) @@ to_tsquery(prefix || ':*'); +-- end; +-- $$ language plpgsql; diff --git a/mirror-2/supabase/seed.sql b/mirror-2/supabase/seed.sql index 6bfc0c20..90667073 100644 --- a/mirror-2/supabase/seed.sql +++ b/mirror-2/supabase/seed.sql @@ -64,9 +64,9 @@ BEGIN asset_url := format('https://picsum.photos/seed/picsum/800/600', i, j); -- Use %s for numbers INSERT INTO public.assets - (id, name, asset_url, creator_user_id, owner_user_id, created_at, updated_at) + (id, name, description, asset_url, creator_user_id, owner_user_id, created_at, updated_at) VALUES - (gen_random_uuid(), format('Asset %s', j), asset_url, user_id, user_id, now(), now()); -- Initially, owner_user_id is the same as creator_user_id + (gen_random_uuid(), format('Asset %s', j), 'This is a placeholder description for the asset.', asset_url, user_id, user_id, now(), now()); -- Added placeholder description END LOOP; END LOOP; @@ -75,9 +75,9 @@ BEGIN FOR i IN 1..45 LOOP -- Create a new space INSERT INTO public.spaces - (id, name, creator_user_id, owner_user_id, created_at, updated_at) + (id, name, description, creator_user_id, owner_user_id, created_at, updated_at) VALUES - (gen_random_uuid(), format('Space %s', i), user_ids[((i - 1) % 15) + 1], user_ids[((i - 1) % 15) + 1], now(), now()) + (gen_random_uuid(), format('Space %s', i), 'This is a placeholder description for the space.', user_ids[((i - 1) % 15) + 1], user_ids[((i - 1) % 15) + 1], now(), now()) -- Added placeholder description RETURNING id INTO space_id; -- Capture the newly created space ID -- Insert 3 space_versions for each space diff --git a/mirror-2/utils/database.types.ts b/mirror-2/utils/database.types.ts index bdb4f97e..7523e99d 100644 --- a/mirror-2/utils/database.types.ts +++ b/mirror-2/utils/database.types.ts @@ -39,15 +39,18 @@ export type Database = { asset_url: string created_at: string creator_user_id: string + description: string id: string name: string owner_user_id: string updated_at: string + name_description: string | null } Insert: { asset_url: string created_at?: string creator_user_id: string + description: string id?: string name: string owner_user_id: string @@ -57,6 +60,7 @@ export type Database = { asset_url?: string created_at?: string creator_user_id?: string + description?: string id?: string name?: string owner_user_id?: string @@ -247,6 +251,7 @@ export type Database = { Row: { created_at: string creator_user_id: string + description: string id: string name: string owner_user_id: string @@ -255,6 +260,7 @@ export type Database = { Insert: { created_at?: string creator_user_id: string + description: string id?: string name: string owner_user_id: string @@ -263,6 +269,7 @@ export type Database = { Update: { created_at?: string creator_user_id?: string + description?: string id?: string name?: string owner_user_id?: string @@ -335,6 +342,12 @@ export type Database = { } Returns: string } + name_description: { + Args: { + "": unknown + } + Returns: string + } } Enums: { avatar_type: "ready_player_me" From 7646e71cbcc06f8a921230e21a5d5d1251aac523 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 20:15:59 -0700 Subject: [PATCH 10/94] name prefix search assets spaces --- mirror-2/state/supabase.tsx | 4 +-- .../migrations/20240929043655_spaces.sql | 2 +- .../20241005025417_searches_assets_spaces.sql | 29 ++++++++------- mirror-2/utils/database.types.ts | 36 +++++++++++++++---- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index f54bbcb8..bc07db9f 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -126,9 +126,7 @@ export const supabaseApi = createApi({ const supabase = createSupabaseBrowserClient(); const { data, error } = await supabase - .from("assets") - .select("*") - .textSearch("name_description", text) + .rpc("search_assets_by_name_prefix", { 'prefix': text }) if (error) { return { error: error.message }; diff --git a/mirror-2/supabase/migrations/20240929043655_spaces.sql b/mirror-2/supabase/migrations/20240929043655_spaces.sql index 697c92e7..9193a0c1 100644 --- a/mirror-2/supabase/migrations/20240929043655_spaces.sql +++ b/mirror-2/supabase/migrations/20240929043655_spaces.sql @@ -1,7 +1,7 @@ create table spaces ( id uuid not null primary key default uuid_generate_v4(), name text not null, - description text not null, + description text, owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Spaces can be transferred and we want to retain the creator creator_user_id uuid references auth.users(id) not null, created_at timestamp with time zone not null default now(), diff --git a/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql b/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql index c0549c54..6de4c564 100644 --- a/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql +++ b/mirror-2/supabase/migrations/20241005025417_searches_assets_spaces.sql @@ -1,16 +1,15 @@ --- Computed name description column -create function name_description(assets) returns text as $$ - select $1.name || ' ' || $1.description; -$$ language sql immutable; +create or replace function search_assets_by_name_prefix(prefix text) +returns setof assets AS $$ +begin + return query + select * from assets where to_tsvector('english', name) @@ to_tsquery(prefix || ':*'); +end; +$$ language plpgsql; -create function name_description(spaces) returns text as $$ - select $1.name || ' ' || $1.description; -$$ language sql immutable; - --- create or replace function search_assets_by_title_description(prefix text) --- returns setof assets AS $$ --- begin --- return query --- select * from assets where to_tsvector('english', title) @@ to_tsquery(prefix || ':*'); --- end; --- $$ language plpgsql; +create or replace function search_spaces_by_name_prefix(prefix text) +returns setof spaces AS $$ +begin + return query + select * from spaces where to_tsvector('english', name) @@ to_tsquery(prefix || ':*'); +end; +$$ language plpgsql; diff --git a/mirror-2/utils/database.types.ts b/mirror-2/utils/database.types.ts index 7523e99d..055df187 100644 --- a/mirror-2/utils/database.types.ts +++ b/mirror-2/utils/database.types.ts @@ -44,7 +44,6 @@ export type Database = { name: string owner_user_id: string updated_at: string - name_description: string | null } Insert: { asset_url: string @@ -251,7 +250,7 @@ export type Database = { Row: { created_at: string creator_user_id: string - description: string + description: string | null id: string name: string owner_user_id: string @@ -260,7 +259,7 @@ export type Database = { Insert: { created_at?: string creator_user_id: string - description: string + description?: string | null id?: string name: string owner_user_id: string @@ -269,7 +268,7 @@ export type Database = { Update: { created_at?: string creator_user_id?: string - description?: string + description?: string | null id?: string name?: string owner_user_id?: string @@ -342,11 +341,34 @@ export type Database = { } Returns: string } - name_description: { + search_assets_by_name_prefix: { Args: { - "": unknown + prefix: string } - Returns: string + Returns: { + asset_url: string + created_at: string + creator_user_id: string + description: string + id: string + name: string + owner_user_id: string + updated_at: string + }[] + } + search_spaces_by_name_prefix: { + Args: { + prefix: string + } + Returns: { + created_at: string + creator_user_id: string + description: string | null + id: string + name: string + owner_user_id: string + updated_at: string + }[] } } Enums: { From d039d2541a73b5148c8ef4c8523db3a226fc6615 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 20:26:14 -0700 Subject: [PATCH 11/94] fix display --- .../build/(controlBar)/inner-control-bar.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx index 8e51d1ab..f686b73d 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx @@ -17,13 +17,13 @@ export default function InnerControlBar() { return (
- {currentView === "scenes" && } - {currentView === "hierarchy" && } - {currentView === "assets" && } - {currentView === "code" && } - {currentView === "database" && } - {currentView === "versions" && } - {currentView === "settings" && } + + + + + + +
From abc791d5331aed598120fa6442e7b759823c9fb4 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 20:27:00 -0700 Subject: [PATCH 12/94] same --- .../build/(controlBar)/inner-control-bar.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx index f686b73d..6bf9aa24 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/inner-control-bar.tsx @@ -17,13 +17,13 @@ export default function InnerControlBar() { return (
- - - - - - - + + + + + + +
From e70bf12dd40507e327b0d2e49992604637a3094b Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 20:50:34 -0700 Subject: [PATCH 13/94] asset search fixes --- .../[spaceId]/build/(controlBar)/assets.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index 906759c8..28a38650 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -18,7 +18,7 @@ export default function Assets() { // define the form const form = useForm>({ resolver: zodResolver(formSchema), - mode: "onChange", + mode: "onSubmit", defaultValues: { text: "", }, @@ -34,20 +34,19 @@ export default function Assets() { throttledSubmit() } - // Reset the form values when the space data is fetched - useEffect(() => { - if (assets && isSuccess) { - form.reset({ - text: "", // Set the form value once space.name is available - }); - } - }, [isSuccess, form]); // Only run this effect when space or isLoading changes + // Watch the text input value + const watchedText = form.watch('text'); return (
{/* Search bar */}
- + { + const text = form.getValues("text") + if (text.length >= 3) { + form.handleSubmit(onSubmit) + } + }}> Date: Fri, 4 Oct 2024 21:10:31 -0700 Subject: [PATCH 14/94] real time text search --- .../[spaceId]/build/(controlBar)/assets.tsx | 21 +++++++++++-------- mirror-2/components/ui/form.tsx | 20 ++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index 28a38650..0c977f69 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -6,7 +6,7 @@ import { XIcon } from 'lucide-react'; import { z } from "zod"; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Form, FormField, FormItem, FormControl, FormMessage } from '@/components/ui/form'; +import { Form, FormField, FormItem, FormControl, FormMessage, FormSuccessMessage } from '@/components/ui/form'; import { useLazySearchAssetsQuery } from '@/state/supabase'; import { useThrottleCallback } from '@react-hook/throttle' @@ -18,7 +18,7 @@ export default function Assets() { // define the form const form = useForm>({ resolver: zodResolver(formSchema), - mode: "onSubmit", + mode: "onChange", defaultValues: { text: "", }, @@ -35,18 +35,20 @@ export default function Assets() { } // Watch the text input value - const watchedText = form.watch('text'); + useEffect(() => { + const subscription = form.watch(({ text }, { name, type }) => { + if (text && text?.length >= 3) { + form.handleSubmit(onSubmit)() + } + }) + return () => subscription.unsubscribe() + }, []) return (
{/* Search bar */} - { - const text = form.getValues("text") - if (text.length >= 3) { - form.handleSubmit(onSubmit) - } - }}> + {/* TODO add better styling for this so it doesn't shift the input field */} + {assets?.length} Results )} /> diff --git a/mirror-2/components/ui/form.tsx b/mirror-2/components/ui/form.tsx index e7ef2d8e..4808c7ec 100644 --- a/mirror-2/components/ui/form.tsx +++ b/mirror-2/components/ui/form.tsx @@ -10,6 +10,7 @@ import { FieldValues, FormProvider, useFormContext, + useFormState, } from "react-hook-form" import { cn } from "@/lib/utils" @@ -166,6 +167,24 @@ const FormMessage = React.forwardRef< }) FormMessage.displayName = "FormMessage" +const FormSuccessMessage = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, children, ...props }, ref) => { + const { isSubmitSuccessful } = useFormState() + + return ( + (isSubmitSuccessful &&

+ {children} +

) + ) +}) +FormSuccessMessage.displayName = "FormSuccessMessage" + export { useFormField, Form, @@ -174,5 +193,6 @@ export { FormControl, FormDescription, FormMessage, + FormSuccessMessage, FormField, } From 6af9f717c16b0474252009d60ac5e7d26a9b0bd3 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 21:22:09 -0700 Subject: [PATCH 15/94] asset search --- .../space/[spaceId]/build/(controlBar)/assets.tsx | 14 +++++++------- mirror-2/state/supabase.tsx | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index 0c977f69..65415e0c 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -27,7 +27,7 @@ export default function Assets() { const [triggerSearch, { data: assets, isLoading, isSuccess, error }] = useLazySearchAssetsQuery() const throttledSubmit = useThrottleCallback(() => { - triggerSearch({ text: form.getValues("text") }) + triggerSearch({ text: form.getValues("text")?.trim() }) }, 3, true) // the 4 if 4 FPS // 2. Define a submit handler. async function onSubmit(values: z.infer) { @@ -90,14 +90,14 @@ export default function Assets() { {/* Scrollable area that takes up remaining space */}
- {assets?.map((image, index) => ( + {assets?.map((asset, index) => (
- {image.text} -

{image.text}

+ /> */} +

{asset.name}

))}
diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index bc07db9f..8d62c15c 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -125,8 +125,10 @@ export const supabaseApi = createApi({ queryFn: async ({ text }) => { const supabase = createSupabaseBrowserClient(); + // replace spaces with + + const friendlyText = text?.trim().replaceAll(" ", "&") const { data, error } = await supabase - .rpc("search_assets_by_name_prefix", { 'prefix': text }) + .rpc("search_assets_by_name_prefix", { 'prefix': friendlyText }) if (error) { return { error: error.message }; From dc9635b724b91c8cac955c1c97cc02c2eb674549 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 21:39:28 -0700 Subject: [PATCH 16/94] add recent assets --- .../[spaceId]/build/(controlBar)/assets.tsx | 21 +++++++++++++--- mirror-2/state/supabase.tsx | 24 ++++++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index 65415e0c..bd8c24b4 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -7,8 +7,9 @@ import { z } from "zod"; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { Form, FormField, FormItem, FormControl, FormMessage, FormSuccessMessage } from '@/components/ui/form'; -import { useLazySearchAssetsQuery } from '@/state/supabase'; +import { useLazyGetUserMostRecentlyUpdatedAssetsQuery, useLazySearchAssetsQuery } from '@/state/supabase'; import { useThrottleCallback } from '@react-hook/throttle' +import { Tables } from '@/utils/database.types'; const formSchema = z.object({ text: z.string().min(3) @@ -25,6 +26,7 @@ export default function Assets() { // errors: error TODO add error handling here }) const [triggerSearch, { data: assets, isLoading, isSuccess, error }] = useLazySearchAssetsQuery() + const [triggerGetUserMostRecentlyUpdatedAssets, { data: recentAssets }] = useLazyGetUserMostRecentlyUpdatedAssetsQuery({}) const throttledSubmit = useThrottleCallback(() => { triggerSearch({ text: form.getValues("text")?.trim() }) @@ -44,6 +46,10 @@ export default function Assets() { return () => subscription.unsubscribe() }, []) + useEffect(() => { + triggerGetUserMostRecentlyUpdatedAssets({}) + }, [triggerGetUserMostRecentlyUpdatedAssets]) + return (
{/* Search bar */} @@ -90,14 +96,23 @@ export default function Assets() { {/* Scrollable area that takes up remaining space */}
- {assets?.map((asset, index) => ( + {form.formState.isSubmitted ? assets?.map((asset, index) => ( +
+ {/* {name.text} */} +

{asset?.name}

+
+ )) : recentAssets?.map((asset, index) => (
{/* {name.text} */} -

{asset.name}

+

{asset?.name}

))}
diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index 8d62c15c..3dea0132 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -121,6 +121,28 @@ export const supabaseApi = createApi({ } }), + + getUserMostRecentlyUpdatedAssets: builder.query({ + queryFn: async () => { + const supabase = createSupabaseBrowserClient(); + const { data: { user } } = await supabase.auth.getUser() + if (!user) { + throw new Error('User not found') + } + const { data, error } = await supabase + .from("assets") + .select("*") + .eq("owner_user_id", user.id) + .order("updated_at", { ascending: false }) + + if (error) { + return { error: error.message }; + } + return { data }; + } + }), + + searchAssets: builder.query({ queryFn: async ({ text }) => { const supabase = createSupabaseBrowserClient(); @@ -169,6 +191,6 @@ export const { /** * Assets */ - useSearchAssetsQuery, useLazySearchAssetsQuery, useGetSingleAssetQuery, useUpdateAssetMutation + useSearchAssetsQuery, useLazySearchAssetsQuery, useGetSingleAssetQuery, useLazyGetUserMostRecentlyUpdatedAssetsQuery, useUpdateAssetMutation } = supabaseApi From cf7d726776946969a96c6fa12f65d0543f0c5e17 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 21:59:01 -0700 Subject: [PATCH 17/94] storage thumb --- .../[spaceId]/build/(controlBar)/assets.tsx | 21 +++-------- mirror-2/components/ui/asset-thumbnail.tsx | 33 ++++++++++++++++++ mirror-2/public/dev/150.jpg | Bin 0 -> 1058 bytes 3 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 mirror-2/components/ui/asset-thumbnail.tsx create mode 100644 mirror-2/public/dev/150.jpg diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index bd8c24b4..de1e4507 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -10,6 +10,7 @@ import { Form, FormField, FormItem, FormControl, FormMessage, FormSuccessMessage import { useLazyGetUserMostRecentlyUpdatedAssetsQuery, useLazySearchAssetsQuery } from '@/state/supabase'; import { useThrottleCallback } from '@react-hook/throttle' import { Tables } from '@/utils/database.types'; +import AssetThumbnail from '@/components/ui/asset-thumbnail'; const formSchema = z.object({ text: z.string().min(3) @@ -97,23 +98,9 @@ export default function Assets() {
{form.formState.isSubmitted ? assets?.map((asset, index) => ( -
- {/* {name.text} */} -

{asset?.name}

-
- )) : recentAssets?.map((asset, index) => ( -
- {/* {name.text} */} -

{asset?.name}

-
+ + )) : recentAssets?.map((asset) => ( + ))}
diff --git a/mirror-2/components/ui/asset-thumbnail.tsx b/mirror-2/components/ui/asset-thumbnail.tsx new file mode 100644 index 00000000..def449a0 --- /dev/null +++ b/mirror-2/components/ui/asset-thumbnail.tsx @@ -0,0 +1,33 @@ + +import React, { useEffect, useState } from 'react'; +import Image from 'next/image'; + +interface AssetThumbnailProps { + imageUrl: string; + name: string; + size?: number; +} + +const AssetThumbnail: React.FC = ({ imageUrl, name, size = 64 }) => { + // primarily used for dev since haven't up storage on seed data 2024-10-04 21:58:50 + const [devImgSrc, setDevImgSrc] = useState(imageUrl); + useEffect(() => { + if (process.env.NODE_ENV === 'development') { + setDevImgSrc("/dev/150.jpg") + } + }, []) + return ( +
+ {name} +

{name}

+
+ ) +}; + +export default AssetThumbnail; diff --git a/mirror-2/public/dev/150.jpg b/mirror-2/public/dev/150.jpg new file mode 100644 index 0000000000000000000000000000000000000000..745c7d0d5cd187445e862060df7f8e4f785f55b5 GIT binary patch literal 1058 zcmex=jKe5@ci+Wc+`GL7IV)k%5^JNWcLrBNGcV8#_=^TY!OyiIJIwk(rqlD$U5m%)%sx(OD|nUb_Kckd)a&11H< zLEbChY_E%axZ+et!d;ymUjr8ZTDr6oUC=ho4gFV?rwjmizh-`c6UviRjMoe6a-CT+9IEj4+wS;?sF-rj$K6_AK$>^$lx)cxVw znG;MgCNdmRYkG8E$J^elSNplkj4$=w>s7VKkIYGVeD&WqjRwod?fo+TTSE@N{dRkC z9$$^FJU7?7x1za8(dwF(CF^e&H83zR3wZ?=)=Ax%F3ZkjrY!<7Of`Vz&s}M5qwl3x z?H)&4y7}39f4}jfP0q&9?wZNL))bBdO<}mJUs6O>r| literal 0 HcmV?d00001 From 64c191fa801e7d03e95be12f1a8554cb9a8dea16 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Fri, 4 Oct 2024 22:06:17 -0700 Subject: [PATCH 18/94] sidebar optimize --- .../[spaceId]/build/(controlBar)/assets.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index de1e4507..d4b4b342 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -11,6 +11,7 @@ import { useLazyGetUserMostRecentlyUpdatedAssetsQuery, useLazySearchAssetsQuery import { useThrottleCallback } from '@react-hook/throttle' import { Tables } from '@/utils/database.types'; import AssetThumbnail from '@/components/ui/asset-thumbnail'; +import { ScrollArea } from '@radix-ui/react-scroll-area'; const formSchema = z.object({ text: z.string().min(3) @@ -96,13 +97,15 @@ export default function Assets() { {/* Scrollable area that takes up remaining space */}
-
- {form.formState.isSubmitted ? assets?.map((asset, index) => ( - - )) : recentAssets?.map((asset) => ( - - ))} -
+ +
+ {form.formState.isSubmitted ? assets?.map((asset, index) => ( + + )) : recentAssets?.map((asset) => ( + + ))} +
+
); From 9ed491842481ea02cf0e5d09d1e1ae2223a6d9fe Mon Sep 17 00:00:00 2001 From: Kluskey Date: Sat, 5 Oct 2024 00:15:12 -0700 Subject: [PATCH 19/94] basic file upload! --- mirror-2/app/home/page.tsx | 4 + .../[spaceId]/build/(controlBar)/assets.tsx | 26 ++++- .../app/space/[spaceId]/build/top-navbar.tsx | 19 +--- .../components/ui/account-dropdown-menu.tsx | 30 +++++ .../ui/custom-buttons/asset-upload.button.tsx | 20 ++++ mirror-2/hooks/file-upload.tsx | 31 ++++++ mirror-2/next.config.js | 2 +- mirror-2/package.json | 2 + mirror-2/state/supabase.tsx | 71 +++++++++++- .../migrations/20240929043655_spaces.sql | 1 + .../migrations/20240929043656_assets.sql | 41 ++++++- mirror-2/supabase/seed.sql | 24 ++-- mirror-2/utils/database.types.ts | 24 ++-- mirror-2/yarn.lock | 103 +++++++++++++++++- 14 files changed, 354 insertions(+), 44 deletions(-) create mode 100644 mirror-2/components/ui/account-dropdown-menu.tsx create mode 100644 mirror-2/components/ui/custom-buttons/asset-upload.button.tsx create mode 100644 mirror-2/hooks/file-upload.tsx diff --git a/mirror-2/app/home/page.tsx b/mirror-2/app/home/page.tsx index 2accc2d3..5f12a254 100644 --- a/mirror-2/app/home/page.tsx +++ b/mirror-2/app/home/page.tsx @@ -14,9 +14,11 @@ import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area" import Link from "next/link" import { appDescription, appName } from "@/lib/theme-service" import { Metadata } from "next" +import AccountDropdownMenu from "@/components/ui/account-dropdown-menu" export default function MusicPage() { + return ( <>
@@ -63,7 +65,9 @@ export default function MusicPage() { +
+
diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index d4b4b342..c1d1e24a 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; -import { XIcon } from 'lucide-react'; +import { Box, FileUp, PlusCircleIcon, XIcon } from 'lucide-react'; import { z } from "zod"; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -12,6 +12,9 @@ import { useThrottleCallback } from '@react-hook/throttle' import { Tables } from '@/utils/database.types'; import AssetThumbnail from '@/components/ui/asset-thumbnail'; import { ScrollArea } from '@radix-ui/react-scroll-area'; +import { useDropzone } from 'react-dropzone'; +import AssetUploadButton from '@/components/ui/custom-buttons/asset-upload.button'; +import { useGetFileUpload } from '@/hooks/file-upload'; const formSchema = z.object({ text: z.string().min(3) @@ -38,6 +41,16 @@ export default function Assets() { throttledSubmit() } + // file dropzone + const onDrop = useGetFileUpload() + + const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({ + // Disable click and keydown behavior + noClick: true, + noKeyboard: true, + onDrop + }); + // Watch the text input value useEffect(() => { const subscription = form.watch(({ text }, { name, type }) => { @@ -53,7 +66,7 @@ export default function Assets() { }, [triggerGetUserMostRecentlyUpdatedAssets]) return ( -
+
{/* Search bar */} @@ -94,15 +107,18 @@ export default function Assets() { + {/* Asset Upload Button */} + + {/* Scrollable area that takes up remaining space */}
-
+
{form.formState.isSubmitted ? assets?.map((asset, index) => ( - + )) : recentAssets?.map((asset) => ( - + ))}
diff --git a/mirror-2/app/space/[spaceId]/build/top-navbar.tsx b/mirror-2/app/space/[spaceId]/build/top-navbar.tsx index 8288dc78..a68e5e01 100644 --- a/mirror-2/app/space/[spaceId]/build/top-navbar.tsx +++ b/mirror-2/app/space/[spaceId]/build/top-navbar.tsx @@ -1,6 +1,7 @@ "use client" import { EditableSpaceName } from "@/components/editable-space-name"; import { ThemeSwitcher } from "@/components/theme-switcher"; +import AccountDropdownMenu from "@/components/ui/account-dropdown-menu"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { signOut } from "@/hooks/auth"; @@ -38,23 +39,7 @@ export function TopNavbar() { > Create Account } - - - - - - {localUserState?.email || "Welcome"} - - {process.env.NEXT_PUBLIC_DISCORD_INVITE_URL && Chat on Discord} - - signOut()} className="cursor-pointer"> - Logout - - - +
); } diff --git a/mirror-2/components/ui/account-dropdown-menu.tsx b/mirror-2/components/ui/account-dropdown-menu.tsx new file mode 100644 index 00000000..e6625034 --- /dev/null +++ b/mirror-2/components/ui/account-dropdown-menu.tsx @@ -0,0 +1,30 @@ +"use client" +import { Button } from "@/components/ui/button"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import { signOut } from "@/hooks/auth"; +import { useAppSelector } from "@/hooks/hooks"; +import { selectLocalUserState } from "@/state/local"; +import { CircleUser } from "lucide-react"; +import Link from "next/link"; + +export default function AccountDropdownMenu() { + const localUserState = useAppSelector(selectLocalUserState) + + return ( + + + + + {localUserState?.email || "Welcome"} + + {process.env.NEXT_PUBLIC_DISCORD_INVITE_URL && Chat on Discord} + + signOut()} className="cursor-pointer"> + Logout + + + ) +} diff --git a/mirror-2/components/ui/custom-buttons/asset-upload.button.tsx b/mirror-2/components/ui/custom-buttons/asset-upload.button.tsx new file mode 100644 index 00000000..2ca99ae0 --- /dev/null +++ b/mirror-2/components/ui/custom-buttons/asset-upload.button.tsx @@ -0,0 +1,20 @@ +import { Button } from "@/components/ui/button"; +import { useGetFileUpload } from "@/hooks/file-upload"; +import { FileUp } from "lucide-react"; +import { useDropzone } from "react-dropzone"; + +export default function AssetUploadButton() { + const onDrop = useGetFileUpload() + // file dropzone + const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({ onDrop }); + + return ( + <> + + + + ) +} diff --git a/mirror-2/hooks/file-upload.tsx b/mirror-2/hooks/file-upload.tsx new file mode 100644 index 00000000..eb353b3b --- /dev/null +++ b/mirror-2/hooks/file-upload.tsx @@ -0,0 +1,31 @@ +import { useCallback } from "react"; +import { useCreateAssetMutation } from "@/state/supabase"; +import { Database } from "@/utils/database.types"; + + +export function useGetFileUpload() { + // Get the mutation hook + const [createAsset] = useCreateAssetMutation(); + + return useCallback((acceptedFiles: File[]) => { + acceptedFiles.forEach((file) => { + // Create the asset record for each file uploaded + const assetData = { + name: file.name, // Use the file name as the asset name + // description: 'Auto-generated description', // TODO add description support + }; + + createAsset({ + assetData, // Asset data with name and description + file, // File to upload + }) + .unwrap() // Unwraps the result to handle promise resolution + .then((response) => { + console.log('File uploaded and asset created successfully:', response); + }) + .catch((error) => { + console.error('Error uploading file or creating asset:', error); + }); + }); + }, [createAsset]); +} diff --git a/mirror-2/next.config.js b/mirror-2/next.config.js index 4b10fa4d..c61491f3 100644 --- a/mirror-2/next.config.js +++ b/mirror-2/next.config.js @@ -13,7 +13,7 @@ const nextConfig = { } ], images: { - domains: ['images.unsplash.com'], + domains: ['images.unsplash.com', '127.0.0.1', 'localhost', 'picsum.photos'], }, }; diff --git a/mirror-2/package.json b/mirror-2/package.json index 4c8a5823..aab6ec5c 100644 --- a/mirror-2/package.json +++ b/mirror-2/package.json @@ -27,6 +27,7 @@ "@reduxjs/toolkit": "^2.2.7", "@supabase/ssr": "latest", "@supabase/supabase-js": "latest", + "ahooks": "^3.8.1", "autoprefixer": "10.4.17", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -38,6 +39,7 @@ "prettier": "^3.3.3", "react": "18.2.0", "react-dom": "18.2.0", + "react-dropzone": "^14.2.9", "react-hook-form": "^7.53.0", "react-redux": "^9.1.2", "react-resizable-panels": "^2.1.4", diff --git a/mirror-2/state/supabase.tsx b/mirror-2/state/supabase.tsx index 3dea0132..ab889362 100644 --- a/mirror-2/state/supabase.tsx +++ b/mirror-2/state/supabase.tsx @@ -1,8 +1,15 @@ "use client"; // we want to use client-side only because supabase tracks auth clientside import { generateSpaceName } from "@/actions/name-generator"; +import { Database } from "@/utils/database.types"; import { createSupabaseBrowserClient } from "@/utils/supabase/client"; import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query/react'; +export const ASSETS_BUCKET_USERS_FOLDER = 'users' // used for the assets bucket +export const ASSETS_BUCKET_VERSIONED_ASSETS_FOLDER = 'versioned' // generally immutable, used for space_versions (published Spaces/games) +export interface CreateAssetMutation { + name: string +} + export const supabaseApi = createApi({ reducerPath: 'supabaseApi', baseQuery: fakeBaseQuery(), @@ -103,6 +110,68 @@ export const supabaseApi = createApi({ // return { data }; // } // }), + createAsset: builder.mutation({ + queryFn: async ({ assetData, file }) => { + const supabase = createSupabaseBrowserClient(); + + + // Get the authenticated user + const { data: { user }, error: authError } = await supabase.auth.getUser(); + if (!user) { + return { error: 'User not found' }; + } + + + // Variable to store the file path (if file exists) + let filePath = ''; + + // Check if a file is passed for upload + if (file) { + // Generate a unique file name for Supabase Storage + filePath = `${ASSETS_BUCKET_USERS_FOLDER}/${file.name}`; + + // Upload the file to Supabase Storage + const { error: uploadError } = await supabase.storage + .from('assets') // Replace with your bucket name + .upload(filePath, file); + + // Handle file upload error + if (uploadError) { + return { error: uploadError.message }; + } + } + + // Create a thumbnail URL using Supabase transform + const { data: thumbnailUrl } = supabase.storage.from('assets').getPublicUrl(filePath, { + transform: { + width: 150, + height: 150 + } + }); + + + // Add the creator and owner user_id inline + const assetUploadData: Database['public']['Tables']['assets']['Insert'] = { + ...assetData, + creator_user_id: user.id, + owner_user_id: user.id, + file_url: `${supabase.storage.from('assets').getPublicUrl(filePath).data.publicUrl}`, + thumbnail_url: thumbnailUrl.publicUrl + } + + // Insert the asset in the "assets" table + const { data, error } = await supabase + .from("assets") + .insert([assetUploadData]) // Insert a new row with updateData + .single(); // Ensure only a single record is returned + + if (error) { + return { error: error.message }; + } + + return { data }; + } + }), getSingleAsset: builder.query({ queryFn: async (assetId) => { @@ -191,6 +260,6 @@ export const { /** * Assets */ - useSearchAssetsQuery, useLazySearchAssetsQuery, useGetSingleAssetQuery, useLazyGetUserMostRecentlyUpdatedAssetsQuery, useUpdateAssetMutation + useCreateAssetMutation, useSearchAssetsQuery, useLazySearchAssetsQuery, useGetSingleAssetQuery, useLazyGetUserMostRecentlyUpdatedAssetsQuery, useUpdateAssetMutation } = supabaseApi diff --git a/mirror-2/supabase/migrations/20240929043655_spaces.sql b/mirror-2/supabase/migrations/20240929043655_spaces.sql index 9193a0c1..333fd256 100644 --- a/mirror-2/supabase/migrations/20240929043655_spaces.sql +++ b/mirror-2/supabase/migrations/20240929043655_spaces.sql @@ -2,6 +2,7 @@ create table spaces ( id uuid not null primary key default uuid_generate_v4(), name text not null, description text, + public_page_image_urls text[] default '{}', owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Spaces can be transferred and we want to retain the creator creator_user_id uuid references auth.users(id) not null, created_at timestamp with time zone not null default now(), diff --git a/mirror-2/supabase/migrations/20240929043656_assets.sql b/mirror-2/supabase/migrations/20240929043656_assets.sql index d71bd024..83dbe447 100644 --- a/mirror-2/supabase/migrations/20240929043656_assets.sql +++ b/mirror-2/supabase/migrations/20240929043656_assets.sql @@ -4,8 +4,9 @@ create table assets ( owner_user_id uuid references auth.users(id) not null, -- owner is different from creator. Assets can be transferred and we want to retain the creator creator_user_id uuid references auth.users(id) not null, name text not null, - description text not null, - asset_url text not null, + description text, + file_url text not null, + thumbnail_url text not null, created_at timestamp with time zone not null default now(), updated_at timestamp with time zone not null default now() ); @@ -49,3 +50,39 @@ insert into storage.buckets (id, name, public) values ('assets', 'assets', true); + + +create policy "User can insert their own objects" +on storage.objects +for insert +to authenticated +with check ( + bucket_id = 'assets' and + owner_id = (select auth.uid()::text) -- Ensure the owner is the current authenticated user +); + +create policy "Anyone can read assets" +on storage.objects +for select +to authenticated +using ( + bucket_id = 'assets' +); + +create policy "User can update their own objects" +on storage.objects +for update +to authenticated +using ( + bucket_id = 'assets' and + owner_id = (select auth.uid()::text) -- Ensure the user owns the object they want to update +); + +create policy "User can delete their own objects" +on storage.objects +for delete +to authenticated +using ( + bucket_id = 'assets' and + owner_id = (select auth.uid()::text) -- Ensure the user owns the object they want to delete +); diff --git a/mirror-2/supabase/seed.sql b/mirror-2/supabase/seed.sql index 90667073..ed9942fc 100644 --- a/mirror-2/supabase/seed.sql +++ b/mirror-2/supabase/seed.sql @@ -34,12 +34,14 @@ DECLARE user_ids uuid[] := ARRAY[]::uuid[]; -- Array to store user IDs user_id uuid; profile_url TEXT; - asset_url TEXT; + file_url TEXT; + thumbnail_url TEXT; space_id uuid; scene_id uuid; entity_name TEXT; entity_id uuid; component_name TEXT; + public_page_image_urls text[]; BEGIN -- Loop to insert 15 users FOR i IN 1..15 LOOP @@ -59,25 +61,33 @@ BEGIN VALUES (user_id, format('User %s', i), format('This is the bio of user %s.', i), format('https://picsum.photos/seed/picsum/300/300', i)); - -- Insert 30 assets for each user, now including creator_user_id and owner_user_id + -- Insert 30 assets for each user, now including creator_user_id, owner_user_id, and thumbnail_url FOR j IN 1..30 LOOP - asset_url := format('https://picsum.photos/seed/picsum/800/600', i, j); -- Use %s for numbers + file_url := format('https://picsum.photos/seed/picsum/800/600', i, j); -- Use %s for numbers + thumbnail_url := format('https://picsum.photos/seed/picsum/200/150', i, j); -- Use %s for thumbnails INSERT INTO public.assets - (id, name, description, asset_url, creator_user_id, owner_user_id, created_at, updated_at) + (id, name, description, file_url, thumbnail_url, creator_user_id, owner_user_id, created_at, updated_at) VALUES - (gen_random_uuid(), format('Asset %s', j), 'This is a placeholder description for the asset.', asset_url, user_id, user_id, now(), now()); -- Added placeholder description + (gen_random_uuid(), format('Asset %s', j), 'This is a placeholder description for the asset.', file_url, thumbnail_url, user_id, user_id, now(), now()); -- Added thumbnail_url and description END LOOP; END LOOP; -- Insert spaces, using user_ids from the array and setting both owner_user_id and creator_user_id FOR i IN 1..45 LOOP + -- Create an array of image URLs for the space's public_page_image_urls + public_page_image_urls := ARRAY[ + format('https://picsum.photos/seed/picsum/%s/800', i), + format('https://picsum.photos/seed/picsum/%s/801', i), + format('https://picsum.photos/seed/picsum/%s/802', i) + ]; + -- Create a new space INSERT INTO public.spaces - (id, name, description, creator_user_id, owner_user_id, created_at, updated_at) + (id, name, description, public_page_image_urls, creator_user_id, owner_user_id, created_at, updated_at) VALUES - (gen_random_uuid(), format('Space %s', i), 'This is a placeholder description for the space.', user_ids[((i - 1) % 15) + 1], user_ids[((i - 1) % 15) + 1], now(), now()) -- Added placeholder description + (gen_random_uuid(), format('Space %s', i), 'This is a placeholder description for the space.', public_page_image_urls, user_ids[((i - 1) % 15) + 1], user_ids[((i - 1) % 15) + 1], now(), now()) -- Added public_page_image_urls and description RETURNING id INTO space_id; -- Capture the newly created space ID -- Insert 3 space_versions for each space diff --git a/mirror-2/utils/database.types.ts b/mirror-2/utils/database.types.ts index 055df187..c2f024c1 100644 --- a/mirror-2/utils/database.types.ts +++ b/mirror-2/utils/database.types.ts @@ -36,33 +36,36 @@ export type Database = { Tables: { assets: { Row: { - asset_url: string created_at: string creator_user_id: string - description: string + description: string | null + file_url: string id: string name: string owner_user_id: string + thumbnail_url: string updated_at: string } Insert: { - asset_url: string created_at?: string creator_user_id: string - description: string + description?: string | null + file_url: string id?: string name: string owner_user_id: string + thumbnail_url: string updated_at?: string } Update: { - asset_url?: string created_at?: string creator_user_id?: string - description?: string + description?: string | null + file_url?: string id?: string name?: string owner_user_id?: string + thumbnail_url?: string updated_at?: string } Relationships: [ @@ -254,6 +257,7 @@ export type Database = { id: string name: string owner_user_id: string + public_page_image_urls: string[] | null updated_at: string } Insert: { @@ -263,6 +267,7 @@ export type Database = { id?: string name: string owner_user_id: string + public_page_image_urls?: string[] | null updated_at?: string } Update: { @@ -272,6 +277,7 @@ export type Database = { id?: string name?: string owner_user_id?: string + public_page_image_urls?: string[] | null updated_at?: string } Relationships: [ @@ -346,13 +352,14 @@ export type Database = { prefix: string } Returns: { - asset_url: string created_at: string creator_user_id: string - description: string + description: string | null + file_url: string id: string name: string owner_user_id: string + thumbnail_url: string updated_at: string }[] } @@ -367,6 +374,7 @@ export type Database = { id: string name: string owner_user_id: string + public_page_image_urls: string[] | null updated_at: string }[] } diff --git a/mirror-2/yarn.lock b/mirror-2/yarn.lock index 92a5644a..d19b7bb9 100644 --- a/mirror-2/yarn.lock +++ b/mirror-2/yarn.lock @@ -7,6 +7,13 @@ resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== +"@babel/runtime@^7.21.0": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" + integrity sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w== + dependencies: + regenerator-runtime "^0.14.0" + "@discoveryjs/json-ext@0.5.7": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -781,6 +788,21 @@ acorn@^8.0.4, acorn@^8.11.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +ahooks@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/ahooks/-/ahooks-3.8.1.tgz#3a19d0e4085618a7a38a22a34b568c8d3fd974c0" + integrity sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA== + dependencies: + "@babel/runtime" "^7.21.0" + dayjs "^1.9.1" + intersection-observer "^0.12.0" + js-cookie "^3.0.5" + lodash "^4.17.21" + react-fast-compare "^3.2.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + tslib "^2.4.1" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -828,6 +850,11 @@ aria-hidden@^1.1.1: dependencies: tslib "^2.0.0" +attr-accept@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + autoprefixer@10.4.17: version "10.4.17" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be" @@ -974,6 +1001,11 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +dayjs@^1.9.1: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debounce@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" @@ -1047,6 +1079,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +file-selector@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc" + integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw== + dependencies: + tslib "^2.4.0" + fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -1147,6 +1186,11 @@ immer@^10.0.3: resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== +intersection-observer@^0.12.0: + version "0.12.2" + resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375" + integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== + invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -1214,6 +1258,11 @@ jiti@^1.19.1: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1234,7 +1283,12 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -loose-envify@^1.0.0, loose-envify@^1.1.0: +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -1338,7 +1392,7 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -object-assign@^4.0.1: +object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -1480,6 +1534,15 @@ prettier@^3.3.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -1493,11 +1556,30 @@ react-dom@18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-dropzone@^14.2.9: + version "14.2.9" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.9.tgz#193a33f9035e29fc91abf24e50de5d66cfa7c8c0" + integrity sha512-jRZsMC7h48WONsOLHcmhyn3cRWJoIPQjPApvt/sJVfnYaB3Qltn025AoRTTJaj4WdmmgmLl6tUQg1s0wOhpodQ== + dependencies: + attr-accept "^2.2.2" + file-selector "^0.6.0" + prop-types "^15.8.1" + +react-fast-compare@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + react-hook-form@^7.53.0: version "7.53.0" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.0.tgz#3cf70951bf41fa95207b34486203ebefbd3a05ab" integrity sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ== +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-redux@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.2.tgz#deba38c64c3403e9abd0c3fbeab69ffd9d8a7e4b" @@ -1586,11 +1668,21 @@ redux@^5.0.1: resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + reselect@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve@^1.1.7, resolve@^1.22.2: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" @@ -1619,6 +1711,11 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" +screenfull@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + server-only@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/server-only/-/server-only-0.0.1.tgz#0f366bb6afb618c37c9255a314535dc412cd1c9e" @@ -1793,7 +1890,7 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== -tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0: +tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.4.1: version "2.7.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== From 019b1eafec2c9a0e67d4c2bc3926cfbfdd9982b9 Mon Sep 17 00:00:00 2001 From: Kluskey Date: Sat, 5 Oct 2024 00:17:41 -0700 Subject: [PATCH 20/94] lift dropzone --- .../[spaceId]/build/(controlBar)/assets.tsx | 33 +++++++------------ .../build/(controlBar)/control-bar.tsx | 14 +++++++- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx index c1d1e24a..8c8f2fc5 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/assets.tsx @@ -1,20 +1,17 @@ 'use client'; -import { useEffect, useState } from 'react'; -import { Input } from '@/components/ui/input'; +import AssetThumbnail from '@/components/ui/asset-thumbnail'; import { Button } from '@/components/ui/button'; -import { Box, FileUp, PlusCircleIcon, XIcon } from 'lucide-react'; -import { z } from "zod"; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { Form, FormField, FormItem, FormControl, FormMessage, FormSuccessMessage } from '@/components/ui/form'; +import AssetUploadButton from '@/components/ui/custom-buttons/asset-upload.button'; +import { Form, FormControl, FormField, FormItem, FormMessage, FormSuccessMessage } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; import { useLazyGetUserMostRecentlyUpdatedAssetsQuery, useLazySearchAssetsQuery } from '@/state/supabase'; -import { useThrottleCallback } from '@react-hook/throttle' -import { Tables } from '@/utils/database.types'; -import AssetThumbnail from '@/components/ui/asset-thumbnail'; +import { zodResolver } from '@hookform/resolvers/zod'; import { ScrollArea } from '@radix-ui/react-scroll-area'; -import { useDropzone } from 'react-dropzone'; -import AssetUploadButton from '@/components/ui/custom-buttons/asset-upload.button'; -import { useGetFileUpload } from '@/hooks/file-upload'; +import { useThrottleCallback } from '@react-hook/throttle'; +import { XIcon } from 'lucide-react'; +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from "zod"; const formSchema = z.object({ text: z.string().min(3) @@ -41,15 +38,7 @@ export default function Assets() { throttledSubmit() } - // file dropzone - const onDrop = useGetFileUpload() - const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({ - // Disable click and keydown behavior - noClick: true, - noKeyboard: true, - onDrop - }); // Watch the text input value useEffect(() => { @@ -66,7 +55,7 @@ export default function Assets() { }, [triggerGetUserMostRecentlyUpdatedAssets]) return ( -
+
{/* Search bar */}
diff --git a/mirror-2/app/space/[spaceId]/build/(controlBar)/control-bar.tsx b/mirror-2/app/space/[spaceId]/build/(controlBar)/control-bar.tsx index e2edae6a..7f879968 100644 --- a/mirror-2/app/space/[spaceId]/build/(controlBar)/control-bar.tsx +++ b/mirror-2/app/space/[spaceId]/build/(controlBar)/control-bar.tsx @@ -3,9 +3,11 @@ import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { useUiHoverSoundEffect } from "@/components/ui/ui-sounds"; +import { useGetFileUpload } from "@/hooks/file-upload"; import { useAppDispatch, useAppSelector } from "@/hooks/hooks"; import { ControlBarView, selectControlBarCurrentView, selectUiSoundsCanPlay, setControlBarCurrentView, turnOffUiSounds, turnOnUiSounds } from "@/state/local"; import { Box, Clapperboard, Code2, Database, GitBranch, ListTree, Settings, Volume2, VolumeOff } from "lucide-react"; +import { useDropzone } from "react-dropzone"; export default function ControlBar() { const currentView = useAppSelector(selectControlBarCurrentView); @@ -25,8 +27,18 @@ export default function ControlBar() { return currentView === view ? "default" : "ghost"; }; + // file dropzone + const onDrop = useGetFileUpload() + + const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({ + // Disable click and keydown behavior + noClick: true, + noKeyboard: true, + onDrop + }); + return ( - +