feat: Wire up new preferences and remove old settings
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s

This commit is contained in:
Jesse Wierzbinski 2025-04-30 18:03:14 +02:00
parent 412e49dfe2
commit 3ce71dd4df
No known key found for this signature in database
32 changed files with 213 additions and 340 deletions

View file

@ -159,7 +159,6 @@ import {
SelectTrigger,
} from "~/components/ui/select";
import * as m from "~/paraglide/messages.js";
import { SettingIds } from "~/settings";
import EditorContent from "../editor/content.vue";
import { Button } from "../ui/button";
import { DialogFooter } from "../ui/dialog";
@ -169,13 +168,11 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
import Files from "./files.vue";
const { Control_Enter, Command_Enter } = useMagicKeys();
const ctrlEnterSend = useSetting(SettingIds.CtrlEnterToSend);
const defaultVisibility = useSetting(SettingIds.DefaultVisibility);
const { play } = useAudio();
const fileInput = ref<HTMLInputElement | null>(null);
watch([Control_Enter, Command_Enter], () => {
if (sending.value || !ctrlEnterSend.value.value) {
if (sending.value || !preferences.ctrl_enter_send.value) {
return;
}
@ -220,9 +217,10 @@ const state = reactive({
sensitive: relation?.type === "edit" ? relation.note.sensitive : false,
contentWarning: relation?.type === "edit" ? relation.note.spoiler_text : "",
contentType: "text/html" as "text/html" | "text/plain",
visibility: (relation?.type === "edit"
? relation.note.visibility
: (defaultVisibility.value.value ?? "public")) as Status["visibility"],
visibility:
relation?.type === "edit"
? relation.note.visibility
: preferences.default_visibility.value,
files: (relation?.type === "edit"
? relation.note.media_attachments.map((a) => ({
apiId: a.id,

View file

@ -19,9 +19,9 @@ defineProps<{
modalOptions: ConfirmModalOptions;
}>();
defineEmits<{
confirm: (result: ConfirmModalResult) => void;
cancel: () => void;
const emit = defineEmits<{
confirm: [result: ConfirmModalResult];
cancel: [];
}>();
const inputValue = ref<string>("");
@ -55,10 +55,10 @@ const inputValue = ref<string>("");
</div>
<DialogFooter>
<Button variant="outline" @click="() => $emit('cancel')">
<Button variant="outline" @click="() => emit('cancel')">
{{ modalOptions.cancelText }}
</Button>
<Button @click="() => $emit('confirm', {
<Button @click="() => emit('confirm', {
confirmed: true,
value: inputValue,
})">
@ -67,4 +67,4 @@ const inputValue = ref<string>("");
</DialogFooter>
</DialogContent>
</Dialog>
</template>
</template>

View file

@ -21,7 +21,6 @@ import { Ellipsis, Heart, Quote, Repeat, Reply } from "lucide-vue-next";
import { toast } from "vue-sonner";
import * as m from "~/paraglide/messages.js";
import { getLocale } from "~/paraglide/runtime";
import { SettingIds } from "~/settings";
import { confirmModalService } from "../modals/composable";
import ActionButton from "./action-button.vue";
import Menu from "./menu.vue";
@ -48,11 +47,8 @@ const emit = defineEmits<{
}>();
const { play } = useAudio();
const confirmLikes = useSetting(SettingIds.ConfirmLike);
const confirmReblogs = useSetting(SettingIds.ConfirmReblog);
const like = async () => {
if (confirmLikes.value.value) {
if (preferences.confirm_actions.value.includes("like")) {
const confirmation = await confirmModalService.confirm({
title: m.slimy_least_ray_aid(),
message: m.stale_new_ray_jolt(),
@ -74,7 +70,7 @@ const like = async () => {
};
const unlike = async () => {
if (confirmLikes.value.value) {
if (preferences.confirm_actions.value.includes("like")) {
const confirmation = await confirmModalService.confirm({
title: m.odd_strong_halibut_prosper(),
message: m.slow_blue_parrot_savor(),
@ -95,7 +91,7 @@ const unlike = async () => {
};
const reblog = async () => {
if (confirmReblogs.value.value) {
if (preferences.confirm_actions.value.includes("reblog")) {
const confirmation = await confirmModalService.confirm({
title: m.best_mellow_llama_surge(),
message: m.salty_plain_mallard_gaze(),
@ -116,7 +112,7 @@ const reblog = async () => {
};
const unreblog = async () => {
if (confirmReblogs.value.value) {
if (preferences.confirm_actions.value.includes("reblog")) {
const confirmation = await confirmModalService.confirm({
title: m.main_fancy_octopus_loop(),
message: m.odd_alive_swan_express(),

View file

@ -1,11 +1,11 @@
<template>
<ContentWarning v-if="(sensitive || contentWarning) && showCw.value" :content-warning="contentWarning" v-model="blurred" />
<ContentWarning v-if="(sensitive || contentWarning) && preferences.show_content_warning" :content-warning="contentWarning" v-model="blurred" />
<OverflowGuard :character-count="characterCount" :class="(blurred && showCw.value) && 'blur-md'">
<OverflowGuard :character-count="characterCount" :class="(blurred && preferences.show_content_warning) && 'blur-md'">
<Prose v-html="content" v-render-emojis="emojis"></Prose>
</OverflowGuard>
<Attachments v-if="attachments.length > 0" :attachments="attachments" :class="(blurred && showCw.value) && 'blur-xl'" />
<Attachments v-if="attachments.length > 0" :attachments="attachments" :class="(blurred && preferences.show_content_warning) && 'blur-xl'" />
<div v-if="quote" class="mt-4 rounded border overflow-hidden">
<Note :note="quote" :hide-actions="true" :small-layout="true" />
@ -14,7 +14,6 @@
<script lang="ts" setup>
import type { Attachment, Emoji, Status } from "@versia/client/types";
import { type BooleanSetting, SettingIds } from "~/settings";
import Attachments from "./attachments.vue";
import ContentWarning from "./content-warning.vue";
import Note from "./note.vue";
@ -32,7 +31,6 @@ const { content, plainContent, sensitive, contentWarning } = defineProps<{
}>();
const blurred = ref(sensitive || !!contentWarning);
const showCw = useSetting(SettingIds.ShowContentWarning) as Ref<BooleanSetting>;
const characterCount = plainContent?.length;
</script>

View file

@ -1,7 +1,7 @@
<template>
<div class="rounded flex flex-row items-center gap-3">
<HoverCard v-model:open="popupOpen" @update:open="() => {
if (!enableHoverCard.value) {
if (!preferences.popup_avatar_hover) {
popupOpen = false;
}
}" :open-delay="2000">
@ -51,7 +51,6 @@ import type {
} from "@vueuse/core";
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
import { getLocale } from "~/paraglide/runtime";
import { SettingIds } from "~/settings";
import Avatar from "../profiles/avatar.vue";
import SmallCard from "../profiles/small-card.vue";
import {
@ -59,7 +58,6 @@ import {
HoverCardContent,
HoverCardTrigger,
} from "../ui/hover-card";
import CopyableText from "./copyable-text.vue";
const { createdAt, noteUrl, author, authorUrl } = defineProps<{
cornerAvatar?: string;
@ -94,7 +92,6 @@ const fullTime = new Intl.DateTimeFormat(getLocale(), {
dateStyle: "medium",
timeStyle: "short",
}).format(createdAt);
const enableHoverCard = useSetting(SettingIds.PopupAvatarHover);
const popupOpen = ref(false);
const visibilities = {

View file

@ -21,7 +21,6 @@ import {
import { toast } from "vue-sonner";
import { confirmModalService } from "~/components/modals/composable.ts";
import * as m from "~/paraglide/messages.js";
import { SettingIds } from "~/settings";
const { authorId, noteId } = defineProps<{
apiNoteString: string;
@ -41,8 +40,6 @@ const { copy } = useClipboard();
const loggedIn = !!identity.value;
const authorIsMe = loggedIn && authorId === identity.value?.account.id;
const confirmDeletes = useSetting(SettingIds.ConfirmDelete);
const copyText = (text: string) => {
copy(text);
toast.success(m.flat_nice_worm_dream());
@ -57,7 +54,7 @@ const blockUser = async (userId: string) => {
};
const _delete = async () => {
if (confirmDeletes.value.value) {
if (preferences.confirm_actions.value.includes("delete")) {
const confirmation = await confirmModalService.confirm({
title: m.calm_icy_weasel_twirl(),
message: m.gray_fun_toucan_slide(),

View file

@ -5,13 +5,13 @@
</CardTitle>
<Card class="p-0 gap-0">
<div v-for="preference of preferences" :key="preference">
<TextPreferenceVue v-if="(prefs[preference] instanceof TextPreference)" :pref="(prefs[preference] as TextPreference)" />
<BooleanPreferenceVue v-else-if="(prefs[preference] instanceof BooleanPreference)" :pref="(prefs[preference] as BooleanPreference)" />
<SelectPreferenceVue v-else-if="(prefs[preference] instanceof SelectPreference)" :pref="(prefs[preference] as SelectPreference<string>)" />
<NumberPreferenceVue v-else-if="(prefs[preference] instanceof NumberPreference)" :pref="(prefs[preference] as NumberPreference)" />
<MultiSelectPreferenceVue v-else-if="(prefs[preference] instanceof MultiSelectPreference)" :pref="(prefs[preference] as MultiSelectPreference<string>)" />
<CodePreferenceVue v-else-if="(prefs[preference] instanceof CodePreference)" :pref="(prefs[preference] as CodePreference)" />
<UrlPreferenceVue v-else-if="(prefs[preference] instanceof UrlPreference)" :pref="(prefs[preference] as UrlPreference)" />
<TextPreferenceVue v-if="(prefs[preference] instanceof TextPreference)" :pref="(prefs[preference] as TextPreference)" :name="preference" />
<BooleanPreferenceVue v-else-if="(prefs[preference] instanceof BooleanPreference)" :pref="(prefs[preference] as BooleanPreference)" :name="preference" />
<SelectPreferenceVue v-else-if="(prefs[preference] instanceof SelectPreference)" :pref="(prefs[preference] as SelectPreference<string>)" :name="preference" />
<NumberPreferenceVue v-else-if="(prefs[preference] instanceof NumberPreference)" :pref="(prefs[preference] as NumberPreference)" :name="preference" />
<MultiSelectPreferenceVue v-else-if="(prefs[preference] instanceof MultiSelectPreference)" :pref="(prefs[preference] as MultiSelectPreference<string>)" :name="preference" />
<CodePreferenceVue v-else-if="(prefs[preference] instanceof CodePreference)" :pref="(prefs[preference] as CodePreference)" :name="preference" />
<UrlPreferenceVue v-else-if="(prefs[preference] instanceof UrlPreference)" :pref="(prefs[preference] as UrlPreference)" :name="preference" />
</div>
</Card>
</section>

View file

@ -0,0 +1,60 @@
<template>
<Card class="grid gap-3 text-sm p-4">
<dl class="grid gap-3">
<div v-for="[key, value] of data" :key="key" class="flex flex-row items-baseline justify-between gap-4 truncate">
<dt class="text-muted-foreground">
{{ key }}
</dt>
<dd class="font-mono" v-if="typeof value === 'string'">{{ value }}</dd>
<dd class="font-mono" v-else>
<component :is="value" />
</dd>
</div>
</dl>
</Card>
</template>
<script lang="tsx" setup>
import type { VNode } from "vue";
import { toast } from "vue-sonner";
import { Button } from "../ui/button";
import { Card } from "../ui/card";
const copy = (data: string) => {
navigator.clipboard.writeText(data);
toast.success("Copied to clipboard");
};
const appData = useAppData();
const data: [string, string | VNode][] = [
["User ID", identity.value?.account.id ?? ""],
["Instance domain", identity.value?.instance.domain ?? ""],
["Instance version", identity.value?.instance.versia_version ?? ""],
["Client ID", appData.value?.client_id ?? ""],
[
"Client secret",
<Button
variant="outline"
class="font-sans"
size="sm"
// @ts-expect-error missing onClick types
onClick={() => copy(appData.value?.client_secret ?? "")}
>
Click to copy
</Button>,
],
[
"Access token",
<Button
variant="outline"
class="font-sans"
size="sm"
// @ts-expect-error missing onClick types
onClick={() => copy(identity.value?.tokens.access_token ?? "")}
>
Click to copy
</Button>,
],
];
</script>

View file

@ -21,6 +21,7 @@ import TinyCard from "../profiles/tiny-card.vue";
import { Separator } from "../ui/separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import Category from "./category.vue";
import Developer from "./developer.vue";
import Emojis from "./emojis/index.vue";
import Page from "./page.vue";
import { preferences } from "./preferences";
@ -110,6 +111,11 @@ const { account: author3 } = useAccountFromAcct(
<Emojis />
</Page>
</TabsContent>
<TabsContent value="Developer" as-child>
<Page title="Developer">
<Developer />
</Page>
</TabsContent>
<TabsContent value="About" as-child>
<Page title="About">
<section class="space-y-4">

View file

@ -25,7 +25,6 @@ const data: [string, string | VNode][] = [
["Author", pkg.author.name],
[
"Repository",
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
<a
href={pkg.repository.url.replace("git+", "")}
target="_blank"

View file

@ -13,17 +13,23 @@
</template>
<script lang="ts" setup>
import type { preferences as prefs } from "../preferences";
import type { Preference } from "../types";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: Preference<any>;
name: keyof typeof prefs;
}>();
const value = ref<any>(pref.options.defaultValue);
const value = ref<any>(preferences[name].value);
const setValue = (newValue: MaybeRef<any>) => {
value.value = toValue(newValue);
};
watch(value, (newVal) => {
preferences[name].value = newVal;
});
defineSlots<{
default(props: {
value: any;

View file

@ -1,15 +1,17 @@
<template>
<Base :pref="pref" v-slot="{ setValue, value }">
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
<Switch @update:model-value="setValue" :model-value="value" />
</Base>
</template>
<script lang="ts" setup>
import { Switch } from "~/components/ui/switch";
import type { preferences as prefs } from "../preferences";
import type { BooleanPreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: BooleanPreference;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,6 +1,6 @@
<template>
<Collapsible as-child>
<Base :pref="pref">
<Base :name="name" :pref="pref">
<template #default>
<CollapsibleTrigger as-child>
<Button variant="outline">
@ -25,10 +25,12 @@ import {
CollapsibleTrigger,
} from "~/components/ui/collapsible";
import { Textarea } from "~/components/ui/textarea";
import type { preferences as prefs } from "../preferences";
import type { CodePreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: CodePreference;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,5 +1,5 @@
<template>
<Base :pref="pref" v-slot="{ setValue, value }">
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline">
@ -30,10 +30,12 @@ import {
DropdownMenuContent,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import type { preferences as prefs } from "../preferences";
import type { MultiSelectPreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: MultiSelectPreference<string>;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,5 +1,5 @@
<template>
<Base :pref="pref" v-slot="{ setValue, value }">
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
<NumberField :model-value="value" @update:model-value="setValue" :min="pref.options.min" :max="pref.options.max" :step="pref.options.integer ? 1 : pref.options.step">
<NumberFieldContent>
<NumberFieldDecrement />
@ -18,10 +18,12 @@ import {
NumberFieldIncrement,
NumberFieldInput,
} from "~/components/ui/number-field";
import type { preferences as prefs } from "../preferences";
import type { NumberPreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: NumberPreference;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,5 +1,5 @@
<template>
<Base :pref="pref" v-slot="{ setValue, value }">
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
<Select :model-value="value" @update:model-value="setValue">
<SelectTrigger>
<SelectValue placeholder="Select an option" />
@ -24,10 +24,12 @@ import {
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
import type { preferences as prefs } from "../preferences";
import type { SelectPreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: SelectPreference<string>;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,15 +1,17 @@
<template>
<Base :pref="pref" v-slot="{ setValue, value }">
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
<Input placeholder="Content here..." :model-value="value" @update:model-value="setValue" />
</Base>
</template>
<script lang="ts" setup>
import { Input } from "~/components/ui/input";
import type { preferences as prefs } from "../preferences";
import type { TextPreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: TextPreference;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,6 +1,6 @@
<template>
<Collapsible as-child>
<Base :pref="pref">
<Base :pref="pref" :name="name">
<template #default>
<CollapsibleTrigger as-child>
<Button variant="outline">
@ -25,10 +25,12 @@ import {
CollapsibleTrigger,
} from "~/components/ui/collapsible";
import { Input, UrlInput } from "~/components/ui/input";
import type { preferences as prefs } from "../preferences";
import type { TextPreference } from "../types";
import Base from "./base.vue";
const { pref } = defineProps<{
const { pref, name } = defineProps<{
pref: TextPreference;
name: keyof typeof prefs;
}>();
</script>

View file

@ -1,5 +1,5 @@
<template>
<Avatar :class="[shape.value === 'square' && 'rounded-md', 'bg-secondary']">
<Avatar :class="['rounded-md bg-secondary']">
<AvatarFallback v-if="name">
{{ getInitials(name) }}
</AvatarFallback>
@ -8,7 +8,6 @@
</template>
<script lang="ts" setup>
import { SettingIds } from "~/settings";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
const { name } = defineProps<{
@ -29,6 +28,4 @@ const getInitials = (name: string): string => {
return `${firstLetter}${secondLetter}`.toUpperCase();
};
const shape = useSetting(SettingIds.AvatarShape);
</script>

View file

@ -74,7 +74,6 @@ import { Button } from "~/components/ui/button";
import { Card, CardContent, CardFooter, CardTitle } from "~/components/ui/card";
import { Separator } from "~/components/ui/separator";
import * as m from "~/paraglide/messages.js";
import { SettingIds } from "~/settings";
import { confirmModalService } from "../modals/composable";
import ProfileActions from "./profile-actions.vue";
import ProfileBadges from "./profile-badges.vue";
@ -91,10 +90,8 @@ const { relationship, isLoading } = useRelationship(client, account.id);
const isMe = identity.value?.account.id === account.id;
const [username, instance] = account.acct.split("@");
const confirmFollows = useSetting(SettingIds.ConfirmFollow);
const follow = async () => {
if (confirmFollows.value.value) {
if (preferences.confirm_actions.value.includes("follow")) {
const confirmation = await confirmModalService.confirm({
title: m.many_fair_capybara_imagine(),
message: m.mellow_yummy_jannes_cuddle({
@ -118,7 +115,7 @@ const follow = async () => {
};
const unfollow = async () => {
if (confirmFollows.value.value) {
if (preferences.confirm_actions.value.includes("follow")) {
const confirmation = await confirmModalService.confirm({
title: m.funny_aloof_swan_loop(),
message: m.white_best_dolphin_catch({

View file

@ -1,13 +1,8 @@
<script setup lang="ts">
import { cn } from "@/lib/utils";
import Timelines from "~/components/navigation/timelines.vue";
import { SidebarInset } from "~/components/ui/sidebar";
import { SettingIds } from "~/settings";
import LeftSidebar from "./left-sidebar.vue";
import RightSidebar from "./right-sidebar.vue";
const showRightSidebar = useSetting(SettingIds.NotificationsSidebar);
const route = useRoute();
const isMd = useMediaQuery("(max-width: 768px)");
const showTimelines = computed(
@ -28,5 +23,5 @@ const showTimelines = computed(
</header>
<slot />
</main>
<RightSidebar v-if="identity" v-show="showRightSidebar.value" />
<RightSidebar v-if="identity" v-show="preferences.display_notifications_sidebar" />
</template>

View file

@ -24,7 +24,7 @@
<!-- If there are no posts at all -->
<NoPosts v-else-if="hasReachedEnd && items.length === 0" />
<div v-else-if="!infiniteScroll.value" class="py-10 px-4">
<div v-else-if="!preferences.infinite_scroll" class="py-10 px-4">
<Button
variant="secondary"
@click="loadNext"
@ -43,7 +43,6 @@
import type { Notification, Status } from "@versia/client/types";
import { useIntersectionObserver } from "@vueuse/core";
import * as m from "~/paraglide/messages.js";
import { SettingIds } from "~/settings";
import NoPosts from "../errors/NoPosts.vue";
import ReachedEnd from "../errors/ReachedEnd.vue";
import Spinner from "../graphics/spinner.vue";
@ -66,14 +65,13 @@ const emit = defineEmits<(e: "update") => void>();
const loadMoreTrigger = ref<HTMLElement | null>(null);
// @ts-expect-error Too complex?
useIntersectionObserver(loadMoreTrigger, ([observer]) => {
if (observer?.isIntersecting && !props.isLoading && !props.hasReachedEnd) {
props.loadNext();
}
});
const infiniteScroll = useSetting(SettingIds.InfiniteScroll);
watch(
() => props.items,
() => {