unstable sharing refactor
This commit is contained in:
@@ -5,7 +5,7 @@ import { Button } from "./ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { store, currentTaskContext, setCurrentTaskContext } from "@/store";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { createResource, createSignal, createEffect } from "solid-js";
|
||||
import { createSignal, createEffect } from "solid-js";
|
||||
|
||||
interface NavItem {
|
||||
icon: any;
|
||||
@@ -121,7 +121,6 @@ export const ContextSwitcher: Component<{
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
isMobile?: boolean;
|
||||
}> = (props) => {
|
||||
const currentUserId = pb.authStore.model?.id;
|
||||
const [open, setOpen] = createSignal(false);
|
||||
|
||||
// Sync with parent if needed
|
||||
@@ -136,37 +135,37 @@ export const ContextSwitcher: Component<{
|
||||
}
|
||||
});
|
||||
|
||||
const [oversightUsers] = createResource(
|
||||
() => [...new Set(store.shareRules.filter(r => r.targetUserId === currentUserId && r.type === 'all').map(r => r.ownerId))],
|
||||
async (ownerIds) => {
|
||||
if (ownerIds.length === 0) return [];
|
||||
try {
|
||||
// Fetch user names for these IDs in a single query
|
||||
const filter = ownerIds.map(id => `id="${id}"`).join(' || ');
|
||||
const users = await pb.collection('users').getFullList({
|
||||
filter,
|
||||
fields: 'id,name,email',
|
||||
requestKey: null // Disable auto-cancellation
|
||||
});
|
||||
return users.map(u => ({ id: u.id, name: u.name || u.email }));
|
||||
} catch (e: any) {
|
||||
if (e.isAbort) return []; // Ignore explicit aborts
|
||||
console.error("Failed to load oversight users", e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const ctx = currentTaskContext; // Accessor
|
||||
const personalContext = () =>
|
||||
store.contexts.find(context =>
|
||||
!context.deletedAt &&
|
||||
context.kind === "user" &&
|
||||
context.targetUserId === pb.authStore.model?.id
|
||||
) || null;
|
||||
const isPersonalContext = () => {
|
||||
const current = ctx();
|
||||
const personal = personalContext();
|
||||
return current === 'mine' || (
|
||||
typeof current === "object" &&
|
||||
"bucketId" in current &&
|
||||
!!personal &&
|
||||
current.bucketId === personal.id
|
||||
);
|
||||
};
|
||||
|
||||
const label = () => {
|
||||
const c = ctx();
|
||||
return c === 'mine' ? "My Bucket" : c.name;
|
||||
return isPersonalContext() ? "My Bucket" : (c === 'mine' ? "My Bucket" : c.name);
|
||||
};
|
||||
|
||||
const handleSwitch = (id: string, name: string, type: 'mine' | 'user' | 'bucket' = 'mine') => {
|
||||
if (type === 'mine') {
|
||||
setCurrentTaskContext('mine');
|
||||
const personal = personalContext();
|
||||
if (personal) {
|
||||
setCurrentTaskContext({ bucketId: personal.id, name: "My Bucket", isPersonal: true });
|
||||
} else {
|
||||
setCurrentTaskContext('mine');
|
||||
}
|
||||
} else if (type === 'bucket') {
|
||||
setCurrentTaskContext({ bucketId: id, name });
|
||||
} else {
|
||||
@@ -182,7 +181,7 @@ export const ContextSwitcher: Component<{
|
||||
}
|
||||
};
|
||||
|
||||
const hasContexts = () => (oversightUsers() && oversightUsers()!.length > 0) || store.subscribedBuckets.length > 0;
|
||||
const hasContexts = () => !!personalContext() || store.subscribedBuckets.length > 0 || store.supervisedContexts.length > 0;
|
||||
|
||||
return (
|
||||
<Show when={hasContexts()}>
|
||||
@@ -197,9 +196,9 @@ export const ContextSwitcher: Component<{
|
||||
<Show when={props.isMobile} fallback={
|
||||
<>
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<div class={cn(
|
||||
"w-4 h-4 rounded-full flex items-center justify-center shrink-0",
|
||||
ctx() === 'mine' ? "bg-primary/20 text-primary" : "bg-orange-500/20 text-orange-500"
|
||||
<div class={cn(
|
||||
"w-4 h-4 rounded-full flex items-center justify-center shrink-0",
|
||||
isPersonalContext() ? "bg-primary/20 text-primary" : "bg-orange-500/20 text-orange-500"
|
||||
)}>
|
||||
<div class="w-2 h-2 rounded-full bg-current" />
|
||||
</div>
|
||||
@@ -209,8 +208,8 @@ export const ContextSwitcher: Component<{
|
||||
</>
|
||||
}>
|
||||
<div class="relative">
|
||||
<Users size={18} class={cn("transition-transform duration-300", open() && "scale-110", ctx() !== 'mine' && "text-orange-500")} />
|
||||
{ctx() !== 'mine' && (
|
||||
<Users size={18} class={cn("transition-transform duration-300", open() && "scale-110", !isPersonalContext() && "text-orange-500")} />
|
||||
{!isPersonalContext() && (
|
||||
<div class="absolute -top-1 -right-1 w-2 h-2 rounded-full bg-orange-500 border border-background" />
|
||||
)}
|
||||
</div>
|
||||
@@ -223,7 +222,7 @@ export const ContextSwitcher: Component<{
|
||||
onClick={() => handleSwitch('mine', '', 'mine')}
|
||||
class={cn(
|
||||
"w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors",
|
||||
ctx() === 'mine' ? "bg-primary/10 text-primary font-semibold" : "hover:bg-muted text-foreground"
|
||||
isPersonalContext() ? "bg-primary/10 text-primary font-semibold" : "hover:bg-muted text-foreground"
|
||||
)}
|
||||
>
|
||||
<div class="w-4 h-4 rounded-full bg-primary/20 flex items-center justify-center">
|
||||
@@ -260,31 +259,32 @@ export const ContextSwitcher: Component<{
|
||||
</For>
|
||||
</Show>
|
||||
|
||||
<Show when={oversightUsers() && oversightUsers()!.length > 0}>
|
||||
<Show when={store.supervisedContexts.length > 0}>
|
||||
<div class="py-1">
|
||||
<div class="h-px bg-border/50" />
|
||||
</div>
|
||||
<div class="px-2 py-1 text-[0.625rem] font-bold uppercase text-muted-foreground tracking-wider flex items-center gap-2">
|
||||
<span>Shared User Buckets</span>
|
||||
<span class="ml-auto bg-muted px-1.5 rounded text-[0.5625rem]">{oversightUsers()?.length}</span>
|
||||
<span>Supervised Contexts</span>
|
||||
<span class="ml-auto bg-muted px-1.5 rounded text-[0.5625rem]">{store.supervisedContexts.length}</span>
|
||||
</div>
|
||||
<For each={oversightUsers()}>
|
||||
{(user) => (
|
||||
<For each={store.supervisedContexts}>
|
||||
{(supervised) => (
|
||||
<button
|
||||
onClick={() => handleSwitch(user.id, user.name, 'user')}
|
||||
onClick={() => handleSwitch(supervised.ownerUserId, supervised.ownerName, 'user')}
|
||||
class={cn(
|
||||
"w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors",
|
||||
(ctx() as any).userId === user.id ? "bg-orange-500/10 text-orange-600 font-semibold" : "hover:bg-muted text-foreground"
|
||||
(ctx() as any).userId === supervised.ownerUserId ? "bg-orange-500/10 text-orange-600 font-semibold" : "hover:bg-muted text-foreground"
|
||||
)}
|
||||
>
|
||||
<div class="w-4 h-4 rounded-full bg-orange-500/20 flex items-center justify-center">
|
||||
<div class="w-2 h-2 rounded-full bg-orange-500" />
|
||||
</div>
|
||||
<span class="truncate">{user.name}</span>
|
||||
<span class="truncate">{supervised.ownerName}</span>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
Reference in New Issue
Block a user