mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Add Enum preference type support
This commit is contained in:
parent
ca824a2a1a
commit
dca7af4b0e
46
components/preferences/select.vue
Normal file
46
components/preferences/select.vue
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<Card class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
||||||
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
|
<CardTitle class="text-base">
|
||||||
|
{{ setting.title }}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{{ setting.description }}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardFooter class="p-0">
|
||||||
|
<Select :model-value="setting.value" @update:model-value="v => { setting.value = v }">
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select an option" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem v-for="option of setting.options" :value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "~/components/ui/card";
|
||||||
|
import type { EnumSetting } from "~/settings.ts";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "../ui/select";
|
||||||
|
|
||||||
|
defineModel<EnumSetting>("setting", {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Card class="grid grid-cols-[1fr,auto] items-center p-6">
|
<Card class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<CardTitle class="text-base">
|
<CardTitle class="text-base">
|
||||||
{{ setting.title }}
|
{{ setting.title }}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Avatar shape="square">
|
<Avatar :shape="(shape.value as 'circle' | 'square')">
|
||||||
<AvatarFallback v-if="name">
|
<AvatarFallback v-if="name">
|
||||||
{{ getInitials(name) }}
|
{{ getInitials(name) }}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { SettingIds } from "~/settings";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
|
||||||
|
|
||||||
const { name } = defineProps<{
|
const { name } = defineProps<{
|
||||||
|
|
@ -28,4 +29,6 @@ const getInitials = (name: string): string => {
|
||||||
|
|
||||||
return `${firstLetter}${secondLetter}`.toUpperCase();
|
return `${firstLetter}${secondLetter}`.toUpperCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shape = useSetting(SettingIds.AvatarShape);
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -13,8 +13,11 @@ import {
|
||||||
SidebarProvider,
|
SidebarProvider,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
} from "~/components/ui/sidebar";
|
} from "~/components/ui/sidebar";
|
||||||
|
import { SettingIds } from "~/settings";
|
||||||
import LeftSidebar from "./left-sidebar.vue";
|
import LeftSidebar from "./left-sidebar.vue";
|
||||||
import RightSidebar from "./right-sidebar.vue";
|
import RightSidebar from "./right-sidebar.vue";
|
||||||
|
|
||||||
|
const showRightSidebar = useSetting(SettingIds.NotificationsSidebar);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -45,6 +48,6 @@ import RightSidebar from "./right-sidebar.vue";
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
<RightSidebar v-if="identity" />
|
<RightSidebar v-if="identity" v-show="showRightSidebar.value" />
|
||||||
</SidebarProvider>
|
</SidebarProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
34
components/timelines/global.vue
Normal file
34
components/timelines/global.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<Timeline type="status" :items="(items as Status[])" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
||||||
|
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
||||||
|
:update-item="updateItem" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { Status } from "@versia/client/types";
|
||||||
|
import { useGlobalTimeline } from "~/composables/GlobalTimeline";
|
||||||
|
import Timeline from "./timeline.vue";
|
||||||
|
|
||||||
|
const {
|
||||||
|
error,
|
||||||
|
hasReachedEnd,
|
||||||
|
isLoading,
|
||||||
|
items,
|
||||||
|
loadNext,
|
||||||
|
loadPrev,
|
||||||
|
removeItem,
|
||||||
|
updateItem,
|
||||||
|
} = useGlobalTimeline(client.value);
|
||||||
|
|
||||||
|
useListen("note:delete", ({ id }) => {
|
||||||
|
removeItem(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
useListen("note:edit", (updatedNote) => {
|
||||||
|
updateItem(updatedNote);
|
||||||
|
});
|
||||||
|
|
||||||
|
useListen("composer:send", () => {
|
||||||
|
loadPrev();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="timeline rounded">
|
<div class="timeline rounded">
|
||||||
<TransitionGroup name="timeline-item" tag="div"
|
<TransitionGroup name="timeline-item" tag="div"
|
||||||
class="timeline-items *:rounded space-y-4 *:border *:border-border/50">
|
class="timeline-items *:rounded space-y-4 *:border">
|
||||||
<TimelineItem :type="type" v-for="item in items" :key="item.id" :item="item" @update="updateItem"
|
<TimelineItem :type="type" v-for="item in items" :key="item.id" :item="item" @update="updateItem"
|
||||||
@delete="removeItem" />
|
@delete="removeItem" />
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|
|
||||||
14
composables/GlobalTimeline.ts
Normal file
14
composables/GlobalTimeline.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import type { Client } from "@versia/client";
|
||||||
|
import type { Status } from "@versia/client/types";
|
||||||
|
import { type TimelineOptions, useTimeline } from "./Timeline";
|
||||||
|
|
||||||
|
export function useGlobalTimeline(
|
||||||
|
client: Client,
|
||||||
|
options: Partial<TimelineOptions<Status>> = {},
|
||||||
|
) {
|
||||||
|
return useTimeline(client, {
|
||||||
|
// TODO: Implement global timeline in client sdk
|
||||||
|
fetchFunction: (client, opts) => client.getPublicTimeline(opts),
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
16
pages/global.vue
Normal file
16
pages/global.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<template>
|
||||||
|
<div class="mx-auto max-w-2xl w-full">
|
||||||
|
<TimelineScroller>
|
||||||
|
<Global />
|
||||||
|
</TimelineScroller>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Global from "~/components/timelines/global.vue";
|
||||||
|
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
layout: "app",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -6,15 +6,18 @@
|
||||||
<div class="grid grid-cols-1 2xl:grid-cols-2 gap-4 mt-6">
|
<div class="grid grid-cols-1 2xl:grid-cols-2 gap-4 mt-6">
|
||||||
<template v-for="[id, setting] of settingEntries">
|
<template v-for="[id, setting] of settingEntries">
|
||||||
<SwitchPreference v-if="setting.type === SettingType.Boolean" :setting="(setting as BooleanSetting)" @update:setting="updateSetting(id, setting)" />
|
<SwitchPreference v-if="setting.type === SettingType.Boolean" :setting="(setting as BooleanSetting)" @update:setting="updateSetting(id, setting)" />
|
||||||
|
<SelectPreference v-else-if="setting.type === SettingType.Enum" :setting="(setting as EnumSetting)" @update:setting="updateSetting(id, setting)" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import SelectPreference from "~/components/preferences/select.vue";
|
||||||
import SwitchPreference from "~/components/preferences/switch.vue";
|
import SwitchPreference from "~/components/preferences/switch.vue";
|
||||||
import {
|
import {
|
||||||
type BooleanSetting,
|
type BooleanSetting,
|
||||||
|
type EnumSetting,
|
||||||
type Setting,
|
type Setting,
|
||||||
type SettingIds,
|
type SettingIds,
|
||||||
type SettingPages,
|
type SettingPages,
|
||||||
|
|
|
||||||
26
settings.ts
26
settings.ts
|
|
@ -80,6 +80,8 @@ export enum SettingIds {
|
||||||
CtrlEnterToSend = "ctrl-enter-to-send",
|
CtrlEnterToSend = "ctrl-enter-to-send",
|
||||||
EmojiTheme = "emoji-theme",
|
EmojiTheme = "emoji-theme",
|
||||||
BackgroundURL = "background-url",
|
BackgroundURL = "background-url",
|
||||||
|
NotificationsSidebar = "notifications-sidebar",
|
||||||
|
AvatarShape = "avatar-shape",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const settings: Record<SettingIds, Setting> = {
|
export const settings: Record<SettingIds, Setting> = {
|
||||||
|
|
@ -91,6 +93,23 @@ export const settings: Record<SettingIds, Setting> = {
|
||||||
page: SettingPages.Behaviour,
|
page: SettingPages.Behaviour,
|
||||||
notImplemented: true,
|
notImplemented: true,
|
||||||
} as BooleanSetting,
|
} as BooleanSetting,
|
||||||
|
[SettingIds.AvatarShape]: {
|
||||||
|
title: "Avatar Shape",
|
||||||
|
description: "Shape of all user avatars.",
|
||||||
|
type: SettingType.Enum,
|
||||||
|
value: "square",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "circle",
|
||||||
|
label: "Round",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "square",
|
||||||
|
label: "Square",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
page: SettingPages.Appearance,
|
||||||
|
} as EnumSetting,
|
||||||
[SettingIds.CustomCSS]: {
|
[SettingIds.CustomCSS]: {
|
||||||
title: "Custom CSS",
|
title: "Custom CSS",
|
||||||
description: "Custom CSS for the UI.",
|
description: "Custom CSS for the UI.",
|
||||||
|
|
@ -222,6 +241,13 @@ export const settings: Record<SettingIds, Setting> = {
|
||||||
value: "",
|
value: "",
|
||||||
page: SettingPages.Appearance,
|
page: SettingPages.Appearance,
|
||||||
} as StringSetting,
|
} as StringSetting,
|
||||||
|
[SettingIds.NotificationsSidebar]: {
|
||||||
|
title: "Notifications Sidebar",
|
||||||
|
description: "Display a sidebar with notifications on desktop.",
|
||||||
|
type: SettingType.Boolean,
|
||||||
|
value: true,
|
||||||
|
page: SettingPages.Appearance,
|
||||||
|
} as BooleanSetting,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSettingsForPage = (page: SettingPages): Partial<Settings> => {
|
export const getSettingsForPage = (page: SettingPages): Partial<Settings> => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue