Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 88 additions & 55 deletions src/routes/logs/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@

inputChannelName = channelName = q.get("c") || "";
inputUserName = userName = q.get("u") || "";
inputQuery = query = q.get("q") || "";
dateValue = q.get("d") || "";
searchValue = q.get("s") || "";
isJumpMode = (q.get("sm") || window.localStorage.getItem("logs-search-mode")) === "jump";
Expand All @@ -209,6 +210,11 @@
let channelStats = $state<StatsResponse | null>(null);
let statsError = $state<string | null>(null);

// Query mode
let inputQuery = $state("");
let query = $state("");
let isQueryMode = $derived(Boolean(query.trim()));

// Emotes
const channelEmotes = new SvelteMap<string, EmoteProps>();
const globalEmotes = new SvelteMap<string, EmoteProps>();
Expand All @@ -226,6 +232,7 @@
d: dateValue,
s: searchValue,
sm: searchValue && isJumpMode ? "jump" : null,
q: isQueryMode ? query : null,
};

untrack(() => {
Expand Down Expand Up @@ -433,11 +440,8 @@

$effect(() => {
// fetch available dates
if (!channelName) return;
if (isQueryMode || !channelName) return;
untrack(async () => {
availableDates = [];
chatLogs = [];
channelStats = null;
loading = true;

const res = await fetch(`https://logs.zonian.dev/list?${parseChannelUser(channelName, userName, true)}`);
Expand All @@ -459,7 +463,7 @@
$effect(() => {
// fetch logs
const date = dateContent;
if (!date) return;
if (!date && !query) return;

untrack(async () => {
error = null;
Expand All @@ -468,11 +472,21 @@
logsController?.abort();
logsController = new AbortController();

const res = await fetch(`https://logs.zonian.dev/${parseChannelUser(channelName, userName, false)}/${date.year}/${date.month}${date.day ? `/${date.day}` : ""}?jsonBasic=1`, {
signal: logsController.signal,
const queryParams = new URLSearchParams({
jsonBasic: "1",
q: isQueryMode ? query : "",
});
const res = await fetch(
`https://logs.zonian.dev/
${parseChannelUser(channelName, userName, false)}
${date ? `/${date.year}/${date.month}${date.day ? `/${date.day}` : ""}` : "/search"}
?${queryParams}`,
{
signal: logsController.signal,
}
);
if (!res.ok) {
if (res.status === 404) error = "No logs found for this date";
if (res.status === 404) error = "No results found";
else error = `Error from server: ${res.status} ${res.statusText}`;
loading = false;
throw error;
Expand Down Expand Up @@ -639,11 +653,22 @@
// force reload
channelName = "";
userName = "";
query = "";

// reset
availableDates = [];
dateValue = "";
chatLogs = [];
channelStats = null;

if (inputQuery.trim() && !inputUserName) {
error = "User is required for global search";
return;
}

channelName = inputChannelName;
userName = inputUserName;
query = inputQuery.trim();
};

const selectResult = (index: number) => {
Expand Down Expand Up @@ -838,7 +863,7 @@
<Label for="input-channel" class="text-base">
Channel<span class="text-red-500">*</span>
</Label>
<Input id="input-channel" maxlength={25} bind:value={inputChannelName} placeholder="channel or id:123" onkeydown={channelKeydown} autocomplete="off" autofocus />
<Input id="input-channel" maxlength={25} bind:value={inputChannelName} placeholder="Channel or id:123" onkeydown={channelKeydown} autocomplete="off" autofocus />

{#if foundChannels.length && foundChannels[0].target !== inputChannelName.toLowerCase()}
<div class="absolute left-0 right-0 top-full z-10 mt-1">
Expand All @@ -862,8 +887,15 @@
</div>

<div class="flex flex-col">
<Label for="input-user" class="text-base">User</Label>
<Input id="input-user" maxlength={25} bind:value={inputUserName} placeholder="username or id:123" />
<Label for="input-user" class="text-base">
User{#if inputQuery.trim()}<span class="text-red-500">*</span>{/if}
</Label>
<Input id="input-user" maxlength={25} bind:value={inputUserName} placeholder="Username or id:123" />
</div>

<div class="flex flex-col">
<Label for="input-query" class="text-base">Query</Label>
<Input id="input-query" maxlength={500} bind:value={inputQuery} placeholder="Global search" autocomplete="off" />
</div>

<div class="flex flex-row items-center gap-1 self-end">
Expand Down Expand Up @@ -1061,51 +1093,52 @@
</Select.Root>
</div>
{/if}
{#if chatLogs.length}
<div class="order-1 flex flex-1 basis-full gap-1 md:order-none md:basis-auto">
<form class="flex-1">
<div class="relative flex items-center">
<Input id="input-search" maxlength={500} placeholder="Search" class="h-8 pr-20" autocomplete="off" bind:ref={searchInput} bind:value={searchValue} />
<span class="pointer-events-none absolute right-2 select-none text-xs tabular-nums text-muted-foreground">
{displayMessageCount}
</span>
</div>
</form>
{#if isJumpSearching}
{@const width = searchResults.length.toString().length + 5}
<div class="flex items-center gap-1">
<Input type="number" class="h-8 w-16 tabular-nums" bind:value={jumpInputValue} min={1} max={searchResults.length} style={`width: ${width}ch;`} />
<span class="text-xs tabular-nums">/</span>
<Input type="number" class="h-8 w-16 tabular-nums" value={searchResults.length} disabled style={`width: ${width}ch;`} />
</div>
{/if}
{#if chatLogs.length}
<div class="order-1 flex flex-1 basis-full gap-1 md:order-none md:basis-auto">
<form class="flex-1">
<div class="relative flex items-center">
<Input id="input-search" maxlength={500} placeholder="Find..." class="h-8 pr-20" autocomplete="off" bind:ref={searchInput} bind:value={searchValue} />
<span class="pointer-events-none absolute right-2 select-none text-xs tabular-nums text-muted-foreground">
{displayMessageCount}
</span>
</div>
</form>
{#if isJumpSearching}
{@const width = searchResults.length.toString().length + 5}
<div class="flex items-center gap-1">
<Input type="number" class="h-8 w-16 tabular-nums" bind:value={jumpInputValue} min={1} max={searchResults.length} style={`width: ${width}ch;`} />
<span class="text-xs tabular-nums">/</span>
<Input type="number" class="h-8 w-16 tabular-nums" value={searchResults.length} disabled style={`width: ${width}ch;`} />
</div>
{/if}
</div>
<div class="ml-auto flex gap-1">
<Button variant="ghost" size="icon" class="size-8 border" onclick={searchModeToggle} title="Toggle Search Mode" aria-label="Toggle Search Mode" aria-pressed={isJumpMode}>
{#if !isJumpMode}
<FilterIcon />
{:else}
<SearchIcon />
{/if}
</div>
<div class="ml-auto flex gap-1">
<Button variant="ghost" size="icon" class="size-8 border" onclick={searchModeToggle} title="Toggle Search Mode" aria-label="Toggle Search Mode" aria-pressed={isJumpMode}>
{#if !isJumpMode}
<FilterIcon />
{:else}
<SearchIcon />
{/if}
</Button>
<Button variant="ghost" size="icon" class="size-8 border" onclick={scrollFromBottomToggle}>
{#if scrollFromBottom}
<ArrowUpNarrowWideIcon />
{:else}
<ArrowDownWideNarrowIcon />
{/if}
</Button>
<Button
variant="ghost"
size="icon"
class="size-8 border"
target="_blank"
href="https://logs.zonian.dev/{parseChannelUser(channelName, userName, false)}/{dateContent.year}/{dateContent.month}{dateContent.day ? `/${dateContent.day}` : ''}"
>
<FileTextIcon />
</Button>
</div>
{/if}
</Button>
<Button variant="ghost" size="icon" class="size-8 border" onclick={scrollFromBottomToggle}>
{#if scrollFromBottom}
<ArrowUpNarrowWideIcon />
{:else}
<ArrowDownWideNarrowIcon />
{/if}
</Button>
<Button
variant="ghost"
size="icon"
class="size-8 border"
target="_blank"
href="https://logs.zonian.dev/{parseChannelUser(channelName, userName, false)}/
{dateContent ? `${dateContent.year}/${dateContent.month}${dateContent.day ? `/${dateContent.day}` : ''}` : `search?q=${encodeURIComponent(query)}`}"
>
<FileTextIcon />
</Button>
</div>
{/if}
</div>
</div>
Expand Down
Loading