mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-13 07:03:45 +00:00
fix dialog to add spaces
This commit is contained in:
parent
a8c344778d
commit
cd71d6b4f9
10 changed files with 456 additions and 148 deletions
|
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"cmdk": "^1.0.0",
|
||||
|
|
|
|||
135
apps/extension/pnpm-lock.yaml
generated
135
apps/extension/pnpm-lock.yaml
generated
|
|
@ -8,6 +8,9 @@ dependencies:
|
|||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.0.5
|
||||
version: 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-dropdown-menu':
|
||||
specifier: ^2.0.6
|
||||
version: 2.0.6(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-popover':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
|
|
@ -668,6 +671,30 @@ packages:
|
|||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.4
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.75)(react@18.2.0)
|
||||
'@types/react': 18.2.75
|
||||
'@types/react-dom': 18.2.24
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.75)(react@18.2.0):
|
||||
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
|
||||
peerDependencies:
|
||||
|
|
@ -730,6 +757,20 @@ packages:
|
|||
react-remove-scroll: 2.5.5(@types/react@18.2.75)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-direction@1.0.1(@types/react@18.2.75)(react@18.2.0):
|
||||
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.4
|
||||
'@types/react': 18.2.75
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==}
|
||||
peerDependencies:
|
||||
|
|
@ -755,6 +796,33 @@ packages:
|
|||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.4
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@types/react': 18.2.75
|
||||
'@types/react-dom': 18.2.24
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.75)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
|
||||
peerDependencies:
|
||||
|
|
@ -807,6 +875,44 @@ packages:
|
|||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.4
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@types/react': 18.2.75
|
||||
'@types/react-dom': 18.2.24
|
||||
aria-hidden: 1.2.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-remove-scroll: 2.5.5(@types/react@18.2.75)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -936,6 +1042,35 @@ packages:
|
|||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.4
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.75)(react@18.2.0)
|
||||
'@types/react': 18.2.75
|
||||
'@types/react-dom': 18.2.24
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-slot@1.0.2(@types/react@18.2.75)(react@18.2.0):
|
||||
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
|
||||
peerDependencies:
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import {
|
|||
DialogFooter,
|
||||
DialogClose,
|
||||
} from "./components/ui/dialog";
|
||||
import { Space } from "./types/memory";
|
||||
|
||||
function sendUrlToAPI() {
|
||||
function sendUrlToAPI(spaces: number[]) {
|
||||
// get the current URL
|
||||
const url = window.location.href;
|
||||
|
||||
|
|
@ -46,11 +47,14 @@ function SideBar() {
|
|||
// }
|
||||
// });
|
||||
|
||||
|
||||
const [savedWebsites, setSavedWebsites] = useState<string[]>([]);
|
||||
|
||||
const [isSendingData, setIsSendingData] = useState(false);
|
||||
|
||||
const [selectedSpaces, setSelectedSpaces] = useState<number[]>([0, 1]);
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [spaces, setSpaces] = useState<Space[]>();
|
||||
const [selectedSpaces, setSelectedSpaces] = useState<number[]>([]);
|
||||
|
||||
interface TweetData {
|
||||
tweetText: string;
|
||||
|
|
@ -60,6 +64,15 @@ function SideBar() {
|
|||
time: string;
|
||||
}
|
||||
|
||||
const fetchSpaces = async () => {
|
||||
setLoading(true)
|
||||
chrome.runtime.sendMessage({ type: "fetchSpaces" }, (resp) => {
|
||||
console.log(resp)
|
||||
setSpaces(resp)
|
||||
setLoading(false)
|
||||
});
|
||||
}
|
||||
|
||||
const fetchBookmarks = () => {
|
||||
const tweets: TweetData[] = []; // Initialize an empty array to hold all tweet elements
|
||||
|
||||
|
|
@ -205,7 +218,7 @@ function SideBar() {
|
|||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Dialog>
|
||||
<Dialog onOpenChange={open => open === true && fetchSpaces()}>
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger
|
||||
className="anycontext-bg-transparent
|
||||
|
|
@ -215,15 +228,7 @@ function SideBar() {
|
|||
<DialogTrigger asChild>
|
||||
<button
|
||||
onClick={() => {
|
||||
sendUrlToAPI();
|
||||
setIsSendingData(true);
|
||||
setTimeout(() => {
|
||||
setIsSendingData(false);
|
||||
setSavedWebsites([
|
||||
...savedWebsites,
|
||||
window.location.href,
|
||||
]);
|
||||
}, 1000);
|
||||
return;
|
||||
}}
|
||||
disabled={savedWebsites.includes(window.location.href)}
|
||||
className="anycontext-open-button disabled:anycontext-opacity-30 anycontext-bg-transparent
|
||||
|
|
@ -276,23 +281,27 @@ function SideBar() {
|
|||
</DialogHeader>
|
||||
|
||||
<FilterSpaces
|
||||
loading={loading}
|
||||
className="anycontext-mr-auto"
|
||||
selectedSpaces={selectedSpaces}
|
||||
setSelectedSpaces={setSelectedSpaces}
|
||||
name={"Add to Spaces"}
|
||||
spaces={[
|
||||
{
|
||||
name: "cool tech",
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: "cool libs",
|
||||
id: 1,
|
||||
},
|
||||
]}
|
||||
spaces={spaces ?? []}
|
||||
/>
|
||||
<DialogFooter className="anycontext-w-full anycontext-text-sm">
|
||||
<DialogClose>Add</DialogClose>
|
||||
<DialogClose
|
||||
onClick={() => {
|
||||
sendUrlToAPI(selectedSpaces);
|
||||
setIsSendingData(true);
|
||||
setTimeout(() => {
|
||||
setIsSendingData(false);
|
||||
setSavedWebsites([
|
||||
...savedWebsites,
|
||||
window.location.href,
|
||||
]);
|
||||
}, 1000);
|
||||
}}
|
||||
>Add</DialogClose>
|
||||
<DialogClose>Cancel</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { getEnv } from "./util";
|
||||
import { Space } from "./types/memory"
|
||||
|
||||
const backendUrl =
|
||||
getEnv() === "development"
|
||||
|
|
@ -37,8 +38,10 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|||
|
||||
return true;
|
||||
} else if (request.type === "urlChange") {
|
||||
|
||||
const content = request.content;
|
||||
const url = request.url;
|
||||
const spaces = request.spaces
|
||||
|
||||
(async () => {
|
||||
chrome.storage.local.get(["jwt"], ({ jwt }) => {
|
||||
|
|
@ -51,10 +54,40 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
body: JSON.stringify({ pageContent: content, url }),
|
||||
body: JSON.stringify({ pageContent: content, url, spaces }),
|
||||
}).then((ers) => console.log(ers.status));
|
||||
});
|
||||
})();
|
||||
} else if (request.type === "fetchSpaces") {
|
||||
|
||||
const run = () => chrome.storage.local.get(["jwt"], async ({ jwt }) => {
|
||||
if (!jwt) {
|
||||
console.error("No JWT found");
|
||||
return;
|
||||
}
|
||||
const resp = await fetch(`${backendUrl}/api/spaces`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
})
|
||||
|
||||
const data: {
|
||||
message: "OK" | string;
|
||||
data: Space[] | undefined;
|
||||
} = await resp.json();
|
||||
|
||||
if (data.message === "OK" && data.data) {
|
||||
sendResponse(data.data)
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
run()
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
} else if (request.type === "queryApi") {
|
||||
const input = request.input;
|
||||
const jwt = request.jwt;
|
||||
|
|
|
|||
|
|
@ -1,152 +1,67 @@
|
|||
import * as React from "react";
|
||||
import { Check, ChevronsUpDown, X } from "lucide-react";
|
||||
|
||||
import { cn } from "../lib/utils";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "../components/ui/command";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "../components/ui/popover";
|
||||
import { PlusCircleIcon, X } from "lucide-react";
|
||||
import { Space } from "../types/memory";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from "./ui/dropdown-menu";
|
||||
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
|
||||
|
||||
export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
side?: "top" | "bottom";
|
||||
align?: "end" | "start" | "center";
|
||||
onClose?: () => void;
|
||||
selectedSpaces: number[];
|
||||
setSelectedSpaces: (
|
||||
spaces: number[] | ((prev: number[]) => number[]),
|
||||
) => void;
|
||||
name: string;
|
||||
spaces: Space[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export function FilterSpaces({
|
||||
className,
|
||||
side = "bottom",
|
||||
align = "center",
|
||||
onClose,
|
||||
loading,
|
||||
selectedSpaces,
|
||||
setSelectedSpaces,
|
||||
name,
|
||||
spaces,
|
||||
...props
|
||||
}: Props) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
console.log(selectedSpaces, spaces);
|
||||
|
||||
const sortedSpaces = spaces.sort(({ id: a }, { id: b }) =>
|
||||
selectedSpaces.includes(a) && !selectedSpaces.includes(b)
|
||||
? -1
|
||||
: selectedSpaces.includes(b) && !selectedSpaces.includes(a)
|
||||
? 1
|
||||
: 0,
|
||||
);
|
||||
const filteredSpaces = spaces.filter(space => selectedSpaces.includes(space.id))
|
||||
const leftSpaces = spaces.filter(space => !selectedSpaces.includes(space.id))
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) {
|
||||
onClose?.();
|
||||
}
|
||||
}, [open]);
|
||||
if (loading) {
|
||||
return "Loading..."
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="anycontext-flex anycontext-flex-wrap anycontext-gap-1 anycontext-text-sm anycontext-">
|
||||
{selectedSpaces.map((spaceid) => {
|
||||
const space = spaces.find((s) => s.id === spaceid)!;
|
||||
return <SpaceItem {...space} key={spaceid} onRemove={() => {}} />;
|
||||
})}
|
||||
{filteredSpaces.length < 1 && "Add to a space"}
|
||||
{filteredSpaces.map((space) => (
|
||||
<SpaceItem {...space} key={space.id} onRemove={() => setSelectedSpaces(prev => prev.filter(s => s !== space.id))} />
|
||||
))}
|
||||
{leftSpaces.length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="anycontext-rounded-full">
|
||||
<PlusCircleIcon className="anycontext-w-5 anycontext-h-5 [--anycontext-icon-stroke:white] dark:[--anycontext-icon-stroke:black]" stroke='var(--anycontext-icon-stroke)' fill='currentColor' />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{leftSpaces.map(space => (
|
||||
<>
|
||||
{loading && "Loading..."}
|
||||
<DropdownMenuItem onClick={() => setSelectedSpaces(prev => [...prev, space.id])}>
|
||||
{space.name}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
type={undefined}
|
||||
data-state-on={open}
|
||||
className={cn(
|
||||
"anycontext-combobox-button anycontext-w-fit",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{name}
|
||||
<ChevronsUpDown className="anycontext-h-4 anycontext-w-4" />
|
||||
<div
|
||||
data-state-on={selectedSpaces.length > 0}
|
||||
className="on:anycontext-flex anycontext-text-rgray-11 anycontext-border-rgray-6 anycontext-bg-rgray-2 anycontext-absolute anycontext-left-0 anycontext-top-0 anycontext-hidden anycontext-aspect-[1] anycontext-h-4 anycontext-w-4 anycontext--translate-x-1/3 anycontext--translate-y-1/3 anycontext-items-center anycontext-justify-center anycontext-rounded-full anycontext-border anycontext-text-center anycontext-text-[9px]"
|
||||
>
|
||||
{selectedSpaces.length}
|
||||
</div>
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
onCloseAutoFocus={(e) => e.preventDefault()}
|
||||
align={align}
|
||||
side={side}
|
||||
className="anycontext-w-[200px] anycontext-p-0"
|
||||
>
|
||||
<Command
|
||||
filter={(val, search) =>
|
||||
spaces
|
||||
.find((s) => s.id.toString() === val)
|
||||
?.name.toLowerCase()
|
||||
.includes(search.toLowerCase().trim())
|
||||
? 1
|
||||
: 0
|
||||
}
|
||||
>
|
||||
<CommandInput placeholder="Filter spaces..." />
|
||||
<CommandList asChild>
|
||||
<div>
|
||||
<CommandEmpty>Nothing found</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{sortedSpaces.map((space) => (
|
||||
<CommandItem
|
||||
key={space.id}
|
||||
value={space.id.toString()}
|
||||
onSelect={(val) => {
|
||||
setSelectedSpaces((prev: number[]) =>
|
||||
prev.includes(parseInt(val))
|
||||
? prev.filter((v) => v !== parseInt(val))
|
||||
: [...prev, parseInt(val)],
|
||||
);
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
<div className="anycontext-text-black/90 dark:anycontext-text-white/90">
|
||||
{space.name}
|
||||
<Check
|
||||
data-state-on={selectedSpaces.includes(space.id)}
|
||||
className={cn(
|
||||
"on:anycontext-opacity-100 anycontext-ml-auto anycontext-h-4 anycontext-w-4 anycontext-opacity-0",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</div>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function SpaceItem({ name, onRemove }: { onRemove: () => void } & Space) {
|
||||
return (
|
||||
<div className="anycontext-flex anycontext-justify-center anycontext-items-center anycontext-gap-2 anycontext-p-1 anycontext-pl-2 anycontext-pr-3 anycontext-rounded-full anycontext-bg-black/5 dark:anycontext-bg-white/5 anycontext-border-white/20 dark:anycontext-border-black/20 border">
|
||||
<button className="anycontext-flex hover:anycontext-bg-transparent anycontext-justify-center anycontext-scale-110 anycontext-items-center focus-visible:anycontext-outline-none anycontext-rounded-full anycontext-w-3 anycontext-bg-black/5 dark:anycontext-bg-white/5 anycontext-h-3 anycontext-text-transparent hover:anycontext-text-black dark:hover:anycontext-text-white">
|
||||
<button onClick={onRemove} className="anycontext-flex hover:anycontext-bg-transparent anycontext-justify-center anycontext-scale-110 anycontext-items-center focus-visible:anycontext-outline-none anycontext-rounded-full anycontext-w-3 anycontext-bg-black/5 dark:anycontext-bg-white/5 anycontext-h-3 anycontext-text-transparent hover:anycontext-text-black dark:hover:anycontext-text-white">
|
||||
<X className="anycontext-w-3 anycontext-h-3" />
|
||||
</button>
|
||||
{name}
|
||||
|
|
|
|||
198
apps/extension/src/components/ui/dropdown-menu.tsx
Normal file
198
apps/extension/src/components/ui/dropdown-menu.tsx
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
import * as React from "react"
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||
|
||||
import { cn } from "../../lib/utils"
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"anycontext-flex anycontext-cursor-default anycontext-select-none anycontext-items-center anycontext-rounded-sm anycontext-px-2 anycontext-py-1.5 anycontext-text-sm anycontext-outline-none focus:anycontext-bg-stone-100 data-[state=open]:anycontext-bg-stone-100 dark:focus:anycontext-bg-stone-800 dark:data-[state=open]:anycontext-bg-stone-800",
|
||||
inset && "anycontext-pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="anycontext-ml-auto anycontext-h-4 anycontext-w-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
DropdownMenuPrimitive.SubTrigger.displayName
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"anycontext-z-50 anycontext-min-w-[8rem] anycontext-overflow-hidden anycontext-rounded-md anycontext-border anycontext-border-stone-200 anycontext-bg-white anycontext-p-1 anycontext-text-stone-950 anycontext-shadow-lg data-[state=open]:anycontext-animate-in data-[state=closed]:anycontext-animate-out data-[state=closed]:anycontext-fade-out-0 data-[state=open]:anycontext-fade-in-0 data-[state=closed]:anycontext-zoom-out-95 data-[state=open]:anycontext-zoom-in-95 data-[side=bottom]:anycontext-slide-in-from-top-2 data-[side=left]:anycontext-slide-in-from-right-2 data-[side=right]:anycontext-slide-in-from-left-2 data-[side=top]:anycontext-slide-in-from-bottom-2 dark:anycontext-border-stone-800 dark:anycontext-bg-stone-950 dark:anycontext-text-stone-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSubContent.displayName =
|
||||
DropdownMenuPrimitive.SubContent.displayName
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"anycontext-z-50 anycontext-min-w-[8rem] anycontext-overflow-hidden anycontext-rounded-md anycontext-border anycontext-border-stone-200 anycontext-bg-white anycontext-p-1 anycontext-text-stone-950 anycontext-shadow-md data-[state=open]:anycontext-animate-in data-[state=closed]:anycontext-animate-out data-[state=closed]:anycontext-fade-out-0 data-[state=open]:anycontext-fade-in-0 data-[state=closed]:anycontext-zoom-out-95 data-[state=open]:anycontext-zoom-in-95 data-[side=bottom]:anycontext-slide-in-from-top-2 data-[side=left]:anycontext-slide-in-from-right-2 data-[side=right]:anycontext-slide-in-from-left-2 data-[side=top]:anycontext-slide-in-from-bottom-2 dark:anycontext-border-stone-800 dark:anycontext-bg-stone-950 dark:anycontext-text-stone-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"anycontext-relative anycontext-flex anycontext-cursor-default anycontext-select-none anycontext-items-center anycontext-rounded-sm anycontext-px-2 anycontext-py-1.5 anycontext-text-sm anycontext-outline-none anycontext-transition-colors focus:anycontext-bg-stone-100 focus:anycontext-text-stone-900 data-[disabled]:anycontext-pointer-events-none data-[disabled]:anycontext-opacity-50 dark:focus:anycontext-bg-stone-800 dark:focus:anycontext-text-stone-50",
|
||||
inset && "anycontext-pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"anycontext-relative anycontext-flex anycontext-cursor-default anycontext-select-none anycontext-items-center anycontext-rounded-sm anycontext-py-1.5 anycontext-pl-8 anycontext-pr-2 anycontext-text-sm anycontext-outline-none anycontext-transition-colors focus:anycontext-bg-stone-100 focus:anycontext-text-stone-900 data-[disabled]:anycontext-pointer-events-none data-[disabled]:anycontext-opacity-50 dark:focus:anycontext-bg-stone-800 dark:focus:anycontext-text-stone-50",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="anycontext-absolute anycontext-left-2 anycontext-flex anycontext-h-3.5 anycontext-w-3.5 anycontext-items-center anycontext-justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="anycontext-h-4 anycontext-w-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"anycontext-relative anycontext-flex anycontext-cursor-default anycontext-select-none anycontext-items-center anycontext-rounded-sm anycontext-py-1.5 anycontext-pl-8 anycontext-pr-2 anycontext-text-sm anycontext-outline-none anycontext-transition-colors focus:anycontext-bg-stone-100 focus:anycontext-text-stone-900 data-[disabled]:anycontext-pointer-events-none data-[disabled]:anycontext-opacity-50 dark:focus:anycontext-bg-stone-800 dark:focus:anycontext-text-stone-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="anycontext-absolute anycontext-left-2 anycontext-flex anycontext-h-3.5 anycontext-w-3.5 anycontext-items-center anycontext-justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="anycontext-h-2 anycontext-w-2 anycontext-fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
))
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"anycontext-px-2 anycontext-py-1.5 anycontext-text-sm anycontext-font-semibold",
|
||||
inset && "anycontext-pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("anycontext--mx-1 anycontext-my-1 anycontext-h-px anycontext-bg-stone-100 dark:anycontext-bg-stone-800", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
||||
|
||||
const DropdownMenuShortcut = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn("anycontext-ml-auto anycontext-text-xs anycontext-tracking-widest anycontext-opacity-60", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ import { sessions, space, users } from "@/server/db/schema";
|
|||
import { eq } from "drizzle-orm";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export const runtime = "edge"
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
|
||||
const token =
|
||||
|
|
@ -33,6 +35,7 @@ export async function GET(req: NextRequest) {
|
|||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, token!));
|
||||
|
||||
|
||||
if (!sessionData || sessionData.length === 0) {
|
||||
return new Response(
|
||||
JSON.stringify({ message: "Invalid Key, session not found." }),
|
||||
|
|
@ -55,12 +58,14 @@ export async function GET(req: NextRequest) {
|
|||
|
||||
const user = userData[0]
|
||||
|
||||
|
||||
const spaces = await db
|
||||
.select()
|
||||
.from(space)
|
||||
.where(eq(space.user, user.id))
|
||||
.all()
|
||||
.all();
|
||||
|
||||
|
||||
console.log('data', spaces)
|
||||
|
||||
return NextResponse.json({
|
||||
message: "OK",
|
||||
|
|
|
|||
|
|
@ -56,7 +56,10 @@ export default async function Home() {
|
|||
const collectedSpaces = await db
|
||||
.select()
|
||||
.from(space)
|
||||
.where(and(eq(space.user, userData.id), not(eq(space.name, "none"))));
|
||||
.where(eq(space.user, userData.id))
|
||||
.all();
|
||||
|
||||
console.log(collectedSpaces)
|
||||
|
||||
// Fetch only first 3 content of each spaces
|
||||
let contents: (typeof storedContent.$inferSelect)[] = [];
|
||||
|
|
|
|||
10
apps/web/src/server/db/test.ts
Normal file
10
apps/web/src/server/db/test.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { db } from "."
|
||||
import { space, user } from "./schema"
|
||||
|
||||
const user = await db.select(user).all()
|
||||
|
||||
await db.insert(space).values([
|
||||
{
|
||||
|
||||
}
|
||||
])
|
||||
|
|
@ -5,7 +5,7 @@ import {
|
|||
storedContent,
|
||||
StoredContent,
|
||||
} from "@/server/db/schema";
|
||||
import { asc, and, eq, inArray, notExists } from "drizzle-orm";
|
||||
import { asc, and, eq, inArray, notExists, sql, exists } from "drizzle-orm";
|
||||
|
||||
export async function fetchContentForSpace(
|
||||
spaceId: number,
|
||||
|
|
@ -19,9 +19,8 @@ export async function fetchContentForSpace(
|
|||
.select()
|
||||
.from(storedContent)
|
||||
.where(
|
||||
inArray(
|
||||
storedContent.id,
|
||||
db.select().from(space).where(eq(space.id, spaceId)),
|
||||
exists(
|
||||
db.select().from(contentToSpace).where(and(eq(contentToSpace.spaceId, spaceId), eq(contentToSpace.contentId, storedContent.id))),
|
||||
),
|
||||
).orderBy(asc(storedContent.title))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue