Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function apiClient(fetch: SvelteFetch, baseUrl?: string): ApiClie
const { options, resolve, reject } = scheduled.shift() as (typeof scheduled)[0]
try {
const resp = await fetch(
`${baseUrl ?? env.PUBLIC_BASE_API_URL ?? "https://api.pluralkit.me"}/v2/${options.path}`,
`${baseUrl ?? env.PUBLIC_BASE_API_URL ?? "https://api.pluralkit.me"}${options.path.startsWith("private") ? "" : "/v2"}/${options.path}`,
{
method: (options && options.method) || "GET",
headers: {
Expand Down
7 changes: 7 additions & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,19 @@ export interface System {
pronouns?: string
}

export interface DashView {
id: string
name: string
value: string
}

export interface Config {
timezone: string
pings_enabled: boolean
member_default_private?: boolean
group_default_private?: boolean
show_private_info?: boolean
dash_views?: DashView[]
member_limit: number
group_limit: number
description_templates: string[]
Expand Down
87 changes: 75 additions & 12 deletions src/components/dash/CopyPermaLink.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts">
import { dash, PrivacyMode } from "$lib/dash/dash.svelte"
import { Base64 } from "js-base64"
import CopyField from "./CopyField.svelte"
import { page } from "$app/state"
import { IconInfoCircle } from "@tabler/icons-svelte"
import { IconDeviceFloppy, IconInfoCircle } from "@tabler/icons-svelte"
import { fade } from "svelte/transition"
import type { DashView } from "$api/types"

let {
tab,
Expand All @@ -22,26 +22,89 @@
})
)
)
let name = $state("")
let id: string|undefined = $state("")
let err = $state("")
let success = $state(false)
let saved = $state(false)

async function save() {
success = false
err = ""

if (!name) {
err = "A view name is required."
return
}

const token = localStorage.getItem("pk-token")
if (!token) {
err = "No token found in browser storage"
return
}

try {
const resp = await window.api<DashView>("private/dash_views", {
method: "POST",
token,
body: {
action: "add",
name: name,
value: viewUrl
}
})

id = resp?.id
name = resp?.name ?? name
success = true
err = ""
} catch (e) {
console.error(e)
err = (e as Error).message
}
saved = true
await new Promise((resolve) => setTimeout(resolve, 3000))
saved = false
}
</script>

<div class="box bg-base-100 h-min mt-4">
<div class="text-sm">
{#if err}
<div transition:fade={{ duration: 400 }} role="alert" class="my-2 alert bg-error/20">
{err}
</div>
{/if}
{#if success}
<div transition:fade={{ duration: 400 }} role="alert" class="my-2 alert bg-success/20 flex-col">
<span>View successfully saved! You can use the following link to access it:
<a class="link-primary" href={`/view/${id}`}>{`${window.location.host}/view/${id}`}</a></span>
</div>
{/if}
<div class="flex flex-row gap-2 items-center w-full">
<input
id={`${dash.user?.uuid}-view-url`}
id={`${dash.system.uuid}-view-name`}
type="text"
disabled
class="input input-bordered input-sm flex-1"
value={viewUrl}
/>
<CopyField
value={`${page.url.protocol}//${page.url.host}/view?uri=${viewUrl}`}
field="permanent link"
visible
bind:value={name}
placeholder="View name..."
/>
<button
title="Save dashboard view"
onclick={() => save()}
disabled={saved}
class={`transition-all hover:scale-110 hover:bg-base-200 active:scale-110 active:bg-base-300 rounded-md`}
>
{#if saved}
<IconDeviceFloppy size={22} class="text-success" />
{:else}
<IconDeviceFloppy size={22} class="text-muted" />
{/if}
</button>
</div>
<span class="text-muted text-sm block mt-2"
><IconInfoCircle class="inline" /> This will copy a link to your current dashboard view, including
><IconInfoCircle class="inline" /> Save your current dashboard view to your
<a class="link-primary" href={`/dash/${dash.system.id}/config`}>system settings</a>, including
filters.</span
>
</div>
Expand Down
95 changes: 95 additions & 0 deletions src/components/dash/edit/EditDashView.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<script lang="ts">
import type { Config, DashView } from "$api/types"
import { createConfigState } from "$lib/dash/dash.svelte"
import {
IconShare2,
IconTrash,
} from "@tabler/icons-svelte"
import { fade } from "svelte/transition"

let {
config = $bindable(),
editedState = $bindable(),
view,
}: {
view: DashView
editedState: any
config: Config
} = $props()

let element: HTMLDialogElement
let saving = $state(false)
let err = $state()

async function submitDelete() {
const token = localStorage.getItem("pk-token")

if (!token) {
err = "No token found in browser storage."
return
}

saving = true

try {
await window.api<DashView>("private/dash_views", {
method: "POST",
token,
body: {
action: "remove",
id: view.id
}
})

saving = false
element.close()

if (config.dash_views) {
config.dash_views.splice(config.dash_views.indexOf(view), 1)
}
editedState = createConfigState(JSON.parse(JSON.stringify(config)))

} catch (e) {
err = (e as Error).message
}
}
</script>

<div class="flex flex-row gap-3 items-center p-2 bg-base-200 rounded-xl">
<a
class="link-info"
href={`/view/${view.id}`}
target="_blank"
title={`Go to view (${view.name})`}
>
<IconShare2 size={26} />
</a>
<div class="text-lg flex-1">{view.name}</div>
<button onclick={() => element.showModal()} class="btn btn-sm btn-ghost" title="Delete View">
<IconTrash class="inline link-error" size={26} />
</button>
</div>

<dialog bind:this={element} class="modal">
<div class="max-w-xl modal-box">
<form method="dialog">
<button class="absolute text-lg btn btn-circle btn-ghost right-2 top-2">✕</button>
</form>
<h3 class="text-xl">Delete Dashboard View (<b>{view.name}</b>)</h3>
<p class="my-2 mb-3">Are you sure you want to delete this view?</p>
{#if err}
<div transition:fade={{ duration: 400 }} role="alert" class="my-2 alert bg-error/20">
{err}
</div>
{/if}
<div class="flex flex-col md:flex-row mt-3">
<button
class="btn btn-sm btn-error mt-1 md:mt-0 md:ml-auto"
disabled={saving}
onclick={() => submitDelete()}
>
<IconTrash /> Yes, delete
</button>
</div>
</div>
</dialog>
2 changes: 1 addition & 1 deletion src/components/dash/groups/GroupHome.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<div class="box bg-base-100 h-min">
<GroupControls privacyMode={dash.privacyMode} bind:list={dash.groups} memberList={dash.members} wide={dash.settings.display?.forceControlsAtTop === true} />
</div>
{#if dash.groups.settings.filterMode === "advanced"}
{#if dash.groups.settings.filterMode === "advanced" && dash.user?.uuid === dash.system.uuid}
<CopyPermaLink tab="groups" />
{/if}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/dash/members/MemberHome.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<div class="box bg-base-100 h-min">
<MemberControls privacyMode={dash.privacyMode} groupList={dash.groups} bind:list={dash.members} wide={dash.settings.display?.forceControlsAtTop === true} />
</div>
{#if dash.members.settings.filterMode === "advanced"}
{#if dash.members.settings.filterMode === "advanced" && dash.user?.uuid === dash.system.uuid}
<CopyPermaLink tab="members" />
{/if}
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/lib/dash/dash.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,9 @@ export function createConfigState(config?: Config) {
let privateMember = $state(config?.member_default_private)
let privateGroup = $state(config?.group_default_private)
let templates = $state(config?.description_templates ?? [])
let dash_views = $state(config?.dash_views)
return {
dash_views,
get member_default_private() {
return privateMember
},
Expand Down
4 changes: 2 additions & 2 deletions src/routes/dash/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { loadDash } from "$lib/dash/load.js"

export async function load({ cookies, fetch, url, parent }) {
export async function load({ cookies, url, parent, locals }) {
const { apiBaseUrl } = await parent()

return await loadDash(fetch, cookies, url, apiBaseUrl)
return await loadDash(locals.api, cookies, url, apiBaseUrl)
}
2 changes: 1 addition & 1 deletion src/routes/dash/[sid]/(dashboard)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { initDash } from "$lib/dash/dash.svelte.js"
let { children, data } = $props()

initDash(data)
initDash({...data, user: dash.user})
</script>

<div class="container px-4 mx-auto">
Expand Down
25 changes: 22 additions & 3 deletions src/routes/dash/[sid]/(dashboard)/config/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { Config } from "$api/types"
import EditBoolean from "$components/dash/edit/EditBoolean.svelte"
import EditDashView from "$components/dash/edit/EditDashView.svelte"
import EditDescription from "$components/dash/edit/EditDescription.svelte"
import SubmitEditButton from "$components/dash/edit/SubmitEditButton.svelte"
import Spinny from "$components/Spinny.svelte"
Expand All @@ -15,12 +15,12 @@
if (typeof $state.snapshot(value) === "object") {
return (
JSON.stringify(value) !==
JSON.stringify(dash.config ? dash.config[key as unknown as keyof Config] : undefined)
JSON.stringify(dash.config ? dash.config[key as unknown as keyof typeof dash.config] : undefined)
)
} else {
return (
$state.snapshot(value) !==
$state.snapshot(dash.config ? dash.config[key as unknown as keyof Config] : undefined)
$state.snapshot(dash.config ? dash.config[key as unknown as keyof typeof dash.config] : undefined)
)
}
})
Expand All @@ -38,6 +38,9 @@
if (body.description_templates) {
body.description_templates = (body.description_templates as string[]).filter((t) => t)
}
if (body.dash_views) {
delete body.dash_views
}

if (err.length > 0) return

Expand Down Expand Up @@ -128,6 +131,22 @@
</EditBoolean>
</div>
</div>
{#if dash.config.dash_views}
<div class="box bg-base-100 flex flex-col p-5 w-full">
<h3 class="font-semibold text-lg mb-2">Saved Views</h3>
<p class="my-2">You can create views from your member/group list, when advanced mode is enabled.
Any saved views will show up here.</p>
{#if dash.config.dash_views.length === 0}
<div class="alert bg-info/20 flex flex-col text-center">No views saved.</div>
{:else}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
{#each dash.config.dash_views as view (view.id)}
<EditDashView {view} bind:config={dash.config} bind:editedState />
{/each}
</div>
{/if}
</div>
{/if}
<div class="box bg-base-100 w-full flex flex-col p-5">
<h3 class="font-semibold text-lg mb-2">Description templates</h3>
<div class="flex flex-col gap-3 -mx-4">
Expand Down
2 changes: 1 addition & 1 deletion src/routes/dash/[sid]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
const p = page.url.searchParams
p.delete("uri")

initDash(data)
initDash({...data, user: dash.user})
</script>

<div class="container px-4 mx-auto">
Expand Down
2 changes: 1 addition & 1 deletion src/routes/dash/g/[gid]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

let { data } = $props()

initDash(data)
initDash({...data, user: dash.user})
</script>

<div class="flex flex-col gap-8 mx-auto w-full max-w-4xl">
Expand Down
2 changes: 1 addition & 1 deletion src/routes/dash/m/[mid]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { dash, initDash } from "$lib/dash/dash.svelte"

let { data } = $props()
initDash(data)
initDash({...data, user: dash.user})
</script>

<div class="flex flex-col gap-8 mx-auto w-full max-w-4xl">
Expand Down
2 changes: 1 addition & 1 deletion src/routes/view/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function load({ url }) {

if (system)
redirect(
302,
308,
`/dash/${system}?${isPublic ? `public=true&` : ""}uri=${uri}&tab=${tab}&${url.searchParams.toString()}`
)
}
Expand Down
Loading