Skip to content

Commit

Permalink
Refactor message notifications handling, compress image, update depen…
Browse files Browse the repository at this point in the history
…dencies, and improve error handling (#124)

This pull request includes several changes:

- Refactored the message notifications handling to improve reliability.

- Compressed an image.

- Updated dependencies.

- Removed unnecessary dependencies from the optimization process in the Vite configuration file.

- Improved error handling.

- Implemented a better search system.

- Enabled streaming.

- Added Pickaxe and Users icons to the SearchSelect dropdown menu.

Please review and merge these changes.
  • Loading branch information
DarthGigi authored May 22, 2024
2 parents d49deab + c56aaf9 commit 6d56f58
Show file tree
Hide file tree
Showing 18 changed files with 710 additions and 381 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@jill64/universal-sanitizer": "^1.2.11",
"@lucia-auth/adapter-prisma": "4.0.1",
"@sveltejs/adapter-vercel": "^5.3.0",
"@sveltejs/kit": "^2.5.9",
"@sveltejs/kit": "^2.5.10",
"@sveltejs/vite-plugin-svelte": "^3.1.0",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.7",
Expand All @@ -38,7 +38,7 @@
"prettier": "^3.2.5",
"prettier-plugin-svelte": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.5.14",
"shiki": "^1.5.2",
"shiki": "^1.6.0",
"svelte": "^4.2.17",
"svelte-check": "^3.7.1",
"svelte-headless-table": "^0.18.2",
Expand All @@ -63,19 +63,19 @@
"@tiptap/pm": "^2.4.0",
"@tiptap/starter-kit": "^2.4.0",
"@vercel/speed-insights": "^1.0.10",
"bits-ui": "^0.21.8",
"bits-ui": "^0.21.9",
"cloudinary": "^2.2.0",
"clsx": "^2.1.1",
"cmdk-sv": "^0.0.17",
"css-tree": "^2.3.1",
"date-fns": "^3.6.0",
"fast-average-color-node": "^3.0.0",
"firebase": "^10.12.0",
"firebase-admin": "^12.1.0",
"firebase": "^10.12.1",
"firebase-admin": "^12.1.1",
"formsnap": "^1.0.0",
"headview3d": "^3.0.2",
"lucia": "^3.2.0",
"lucide-svelte": "^0.378.0",
"lucide-svelte": "^0.379.0",
"mode-watcher": "^0.3.0",
"numerable": "^0.3.15",
"oslo": "^1.2.0",
Expand All @@ -85,7 +85,7 @@
"skinview3d": "^3.0.1",
"super-sitemap": "^0.14.14",
"svelte-interactions": "^0.2.0",
"svelte-persisted-store": "^0.9.2",
"svelte-persisted-store": "^0.9.4",
"svelte-sonner": "^0.3.24",
"sveltekit-rate-limiter": "^0.5.1",
"sveltekit-superforms": "^2.13.1",
Expand Down
343 changes: 176 additions & 167 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

82 changes: 55 additions & 27 deletions src/lib/components/MinionsListBox.svelte
Original file line number Diff line number Diff line change
@@ -1,50 +1,75 @@
<script lang="ts">
import * as Avatar from "$lib/components/ui/avatar";
import { Button } from "$lib/components/ui/button";
import * as Command from "$lib/components/ui/command";
import * as Popover from "$lib/components/ui/popover";
import { ScrollArea } from "$lib/components/ui/scroll-area";
import { cn } from "$lib/utils";
import Check from "lucide-svelte/icons/check";
import ChevronsUpDown from "lucide-svelte/icons/chevrons-up-down";
import SearchX from "lucide-svelte/icons/search-x";
import { createEventDispatcher, tick } from "svelte";
import { ScrollArea } from "$lib/components/ui/scroll-area";
import { writable } from "svelte/store";
export let minionType: { id: string; generator: string; texture: string; maxTier: number }[];
export let search: string = "";
export let showReset: boolean;
export let variant: "rounded" | "half-rounded" = "rounded";
const dispatch = createEventDispatcher<{ onSelect: { id: string; generator: string; texture: string; maxTier: number } }>();
const dispatch = createEventDispatcher<{ onSelect: { id: string; generator: string; texture: string; maxTier: number | undefined } }>();
let open = false;
let value = "";
const open = writable<boolean>(false);
const value = writable<string>(search);
// We want to refocus the trigger button when the user selects
// an item from the list so users can continue navigating the
// rest of the form with the keyboard.
function closeAndFocusTrigger(triggerId: string) {
open = false;
open.set(false);
tick().then(() => {
document.getElementById(triggerId)?.focus();
});
}
</script>

<Popover.Root bind:open let:ids>
<Popover.Root bind:open={$open} let:ids>
<Popover.Trigger asChild let:builder>
<Button builders={[builder]} variant="outline" role="combobox" type="button" class={cn("relative w-40 cursor-default justify-between rounded-md border border-input bg-background py-1.5 pl-3 text-left shadow-sm focus:outline-none focus:ring-2 focus:ring-ring sm:text-sm sm:leading-6 md:w-44", !value && "text-muted-foreground")} aria-label="Select a minion" aria-haspopup="listbox" aria-expanded={open}>
<div class="flex">
{#if minionType.find((f) => f.generator === value)}
<img loading="lazy" src={`https://res.cloudinary.com/minionah/image/upload/v1/minions/head/${minionType.find((f) => f.generator === value)?.id}`} class="mr-2 h-6 w-6" alt={value} />
<span class="capitalize">
{minionType
.find((f) => f.generator === value)
?.generator.replace(/_/g, " ")
.toLowerCase()
.charAt(0) + value.slice(1).toLowerCase().replace(/_/g, " ")}
</span>
{:else}
<span>Select a minion</span>
{/if}
</div>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
<div class="flex items-center">
<Button builders={[builder]} variant="outline" role="combobox" type="button" class={cn("relative w-40 cursor-default justify-between rounded-md border border-input bg-background py-1.5 pl-3 text-left shadow-sm focus:z-10 focus:outline-none focus:ring-2 focus:ring-ring sm:text-sm sm:leading-6 md:w-44", !$value && "text-muted-foreground", $value && showReset && "rounded-r-none border-r-0", variant === "half-rounded" && "rounded-r-none border-r-0")} aria-label="Select a minion" aria-haspopup="listbox" aria-expanded={$open}>
<div class="flex">
{#if minionType.find((f) => f.generator === $value)}
<Avatar.Root class="mr-2 size-6 flex-shrink-0 overflow-visible rounded-full">
<Avatar.Image src={`https://res.cloudinary.com/minionah/image/upload/v1/minions/head/${minionType.find((f) => f.generator === $value)?.id}`} alt={$value} class="pointer-events-none h-full w-full overflow-visible" />
<Avatar.Fallback class="border-2 border-accent bg-accent text-xs">{$value.slice(0, 2).toUpperCase()}</Avatar.Fallback>
</Avatar.Root>
<span class="capitalize">
{minionType
.find((f) => f.generator === $value)
?.generator.replace(/_/g, " ")
.toLowerCase()
.charAt(0) + $value.slice(1).toLowerCase().replace(/_/g, " ")}
</span>
{:else}
<span>Select a minion</span>
{/if}
</div>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>

{#if $value && showReset}
<Button
variant="outline"
class={cn("relative rounded-md rounded-l-none border-r border-input bg-background py-1.5 pl-3 text-left shadow-sm focus:z-10 focus:outline-none focus:ring-2 focus:ring-ring sm:text-sm sm:leading-6")}
on:click={() => {
value.set("");
dispatch("onSelect", { id: "", generator: "", texture: "", maxTier: undefined });
}}
aria-label="Reset search">
<SearchX class="h-4 w-4 shrink-0 opacity-50" />
</Button>
{/if}
</div>
</Popover.Trigger>
<Popover.Content class="mt-1 w-[200] border border-border bg-popover p-0">
<Command.Root class="max-h-56 overflow-hidden border-none bg-popover text-base sm:text-sm">
Expand All @@ -56,18 +81,21 @@
<Command.Item
value={minionType.generator}
onSelect={() => {
value = minionType.generator;
value.set(minionType.generator);
dispatch("onSelect", minionType);
closeAndFocusTrigger(ids.trigger);
}}
class="justify-between text-popover-foreground aria-selected:bg-background">
<div class="inline-flex items-center">
<img src={`https://res.cloudinary.com/minionah/image/upload/v1/minions/head/${minionType.id}`} class="mr-2 h-6 w-6" alt={minionType.generator} />
<Avatar.Root class="mr-2 size-6 flex-shrink-0 overflow-visible rounded-full">
<Avatar.Image src={`https://res.cloudinary.com/minionah/image/upload/v1/minions/head/${minionType.id}`} alt={minionType.generator} class="pointer-events-none h-full w-full overflow-visible" />
<Avatar.Fallback class="border-2 border-accent bg-accent text-xs">{minionType.generator.slice(0, 2).toUpperCase()}</Avatar.Fallback>
</Avatar.Root>
<span class="capitalize">
{minionType.generator.replace(/_/g, " ").toLowerCase().charAt(0).toUpperCase() + minionType.generator.slice(1).toLowerCase().replace(/_/g, " ")}
{minionType.generator.replace(/_/g, " ").toLowerCase()}
</span>
</div>
<Check class={cn("mr-2 h-4 w-4", value !== minionType.generator && "text-transparent")} />
<Check class={cn("mr-2 h-4 w-4", $value !== minionType.generator && "text-transparent")} />
</Command.Item>
{/each}
</ScrollArea>
Expand Down
52 changes: 52 additions & 0 deletions src/lib/components/SearchSelect.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script context="module" lang="ts">
export enum SearchType {
Minion = "Minion",
User = "User"
}
</script>

<script lang="ts">
import { Button } from "$lib/components/ui/button";
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
import { cn } from "$lib/utils";
import ChevronDown from "lucide-svelte/icons/chevron-down";
import { createEventDispatcher } from "svelte";
import Pickaxe from "lucide-svelte/icons/pickaxe";
import Users from "lucide-svelte/icons/users";
export let searchType: SearchType = SearchType.Minion;
const dispatch = createEventDispatcher<{ onSelect: SearchType }>();
</script>

<DropdownMenu.Root>
<DropdownMenu.Trigger asChild let:builder>
<Button builders={[builder]} variant="outline" class={cn("relative rounded-md rounded-l-none border-r border-input bg-background py-1.5 pl-3 text-left shadow-sm focus:z-10 focus:outline-none focus:ring-2 focus:ring-ring sm:text-sm sm:leading-6")} aria-label="Reset search">
<ChevronDown class="h-4 w-4 shrink-0 opacity-50" />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content class="rounded-md border-input bg-background">
<DropdownMenu.Group class="text-muted-foreground">
<DropdownMenu.Label>Search for</DropdownMenu.Label>
{#if searchType !== SearchType.Minion}
<DropdownMenu.Item
class="transition-colors hover:bg-accent hover:text-accent-foreground"
on:click={() => {
searchType = SearchType.Minion;
dispatch("onSelect", SearchType.Minion);
}}>
<Pickaxe class="mr-1 size-4" /> Minions
</DropdownMenu.Item>
{:else}
<DropdownMenu.Item
class="transition-colors hover:bg-accent hover:text-accent-foreground"
on:click={() => {
searchType = SearchType.User;
dispatch("onSelect", SearchType.User);
}}>
<Users class="mr-1 size-4" /> Users
</DropdownMenu.Item>
{/if}
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
106 changes: 106 additions & 0 deletions src/lib/components/UsersListBox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<script lang="ts">
import * as Avatar from "$lib/components/ui/avatar";
import { Button } from "$lib/components/ui/button";
import * as Command from "$lib/components/ui/command";
import * as Popover from "$lib/components/ui/popover";
import { ScrollArea } from "$lib/components/ui/scroll-area";
import { cn } from "$lib/utils";
import Check from "lucide-svelte/icons/check";
import ChevronsUpDown from "lucide-svelte/icons/chevrons-up-down";
import SearchX from "lucide-svelte/icons/search-x";
import { createEventDispatcher, tick } from "svelte";
import { writable } from "svelte/store";
type User = {
id: string;
username: string;
};
export let users: User[];
export let search: string;
export let showReset: boolean;
export let variant: "rounded" | "half-rounded" = "rounded";
const dispatch = createEventDispatcher<{ onSelect: User }>();
const open = writable<boolean>(false);
const value = writable<string>(search);
// We want to refocus the trigger button when the user selects
// an item from the list so users can continue navigating the
// rest of the form with the keyboard.
function closeAndFocusTrigger(triggerId: string) {
open.set(false);
tick().then(() => {
document.getElementById(triggerId)?.focus();
});
}
</script>

<Popover.Root bind:open={$open} let:ids>
<Popover.Trigger asChild let:builder>
<div class="flex items-center">
<Button builders={[builder]} variant="outline" role="combobox" type="button" class={cn("relative w-40 cursor-default justify-between rounded-md border border-input bg-background py-1.5 pl-3 text-left shadow-sm focus:z-10 focus:outline-none focus:ring-2 focus:ring-ring sm:text-sm sm:leading-6 md:w-44", !$value && "text-muted-foreground", $value && showReset && "rounded-r-none border-r-0", variant === "half-rounded" && "rounded-r-none border-r-0")} aria-label="Select a user" aria-haspopup="listbox" aria-expanded={$open}>
<div class="flex">
{#if users.find((f) => f.id === $value)}
<Avatar.Root class="mr-2 size-6 flex-shrink-0 overflow-visible rounded-full">
<Avatar.Image src={`https://res.cloudinary.com/minionah/image/upload/v1/users/avatars/${users.find((f) => f.id === $value)?.id}`} alt={$value} class="pointer-events-none h-full w-full overflow-visible" />
<Avatar.Fallback class="border-2 border-accent bg-accent text-xs uppercase">{users.find((f) => f.id === $value)?.username.slice(0, 2)}</Avatar.Fallback>
</Avatar.Root>
<span>
{users.find((f) => f.id === $value)?.username}
</span>
{:else}
<span>Select a user</span>
{/if}
</div>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>

{#if $value && showReset}
<Button
variant="outline"
class={cn("relative rounded-md rounded-l-none border-r border-input bg-background py-1.5 pl-3 text-left shadow-sm focus:z-10 focus:outline-none focus:ring-2 focus:ring-ring sm:text-sm sm:leading-6")}
on:click={() => {
value.set("");
dispatch("onSelect", { id: "", username: "" });
}}
aria-label="Reset search">
<SearchX class="h-4 w-4 shrink-0 opacity-50" />
</Button>
{/if}
</div>
</Popover.Trigger>
<Popover.Content class="mt-1 w-[200] border border-border bg-popover p-0">
<Command.Root class="max-h-56 overflow-hidden border-none bg-popover text-base sm:text-sm">
<Command.Input autofocus placeholder="Search for a user" class="border-0 text-popover-foreground focus:shadow-none focus:outline-0 focus:ring-0" />
<Command.Empty>No users found</Command.Empty>
<Command.Group>
<ScrollArea class="h-40 rounded-md">
{#each users as user}
<Command.Item
value={user.username}
onSelect={() => {
value.set(user.id);
dispatch("onSelect", user);
closeAndFocusTrigger(ids.trigger);
}}
class="justify-between text-popover-foreground aria-selected:bg-background">
<div class="inline-flex items-center">
<Avatar.Root class="mr-2 size-6 flex-shrink-0 overflow-visible rounded-full">
<Avatar.Image src={`https://res.cloudinary.com/minionah/image/upload/v1/users/avatars/${user.id}`} alt={user.username} class="pointer-events-none h-full w-full overflow-visible" />
<Avatar.Fallback class="border-2 border-accent bg-accent text-xs uppercase">{user.username.slice(0, 2)}</Avatar.Fallback>
</Avatar.Root>
<span>
{user.username}
</span>
</div>
<Check class={cn("mr-2 h-4 w-4", $value !== user.id && "text-transparent")} />
</Command.Item>
{/each}
</ScrollArea>
</Command.Group>
</Command.Root>
</Popover.Content>
</Popover.Root>
2 changes: 1 addition & 1 deletion src/lib/components/card/card-item-minion.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
class="group rounded bg-accent p-1 text-sm text-muted-foreground focus:outline-none focus:ring-4 focus:ring-transparent"
use:pressAction
on:press={() => {
handleSearchSignal(minion.minion.generator.replace(/_/g, " ").toLowerCase().charAt(0).toUpperCase() + minion.minion.generator.slice(1).toLowerCase().replace(/_/g, " "));
handleSearchSignal(minion.minion.generator);
}}>
<Search class="h-5 w-5 transition-colors duration-300 group-hover:text-white" />
</button>
Expand Down
11 changes: 6 additions & 5 deletions src/routes/(main)/(protected)/profile/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
let canvasIsLoading = true;
let maxtier: number = 12;
let maxtier: number | undefined = 12;
let tierListDisabled = true;
Expand Down Expand Up @@ -167,14 +167,15 @@
<Form.Control let:attrs>
<Form.Label>Minion</Form.Label>
<MinionsListBox
showReset={false}
on:onSelect={({ detail }) => {
maxtier = detail.maxTier;
$formDataCreate.type = detail.generator;
if ($constraintsCreate.tier?.min && $constraintsCreate.tier?.max) {
const minTier = Number($constraintsCreate.tier.min);
const maxTier = Number($constraintsCreate.tier.max);
if ($constraintsCreate.tier?.min && $constraintsCreate.tier?.max && maxtier) {
const minTierConstraint = Number($constraintsCreate.tier.min);
const maxTierConstraint = Number($constraintsCreate.tier.max);

if (maxtier > minTier && maxtier <= maxTier) {
if (maxtier > minTierConstraint && maxtier <= maxTierConstraint) {
tierListDisabled = false;
}
}
Expand Down
Loading

0 comments on commit 6d56f58

Please sign in to comment.