mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Polish UI, add new functionality to composer
This commit is contained in:
parent
ddebe77e5b
commit
2cc3d2ea7a
18
components/composer/button.vue
Normal file
18
components/composer/button.vue
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<template>
|
||||||
|
<button v-bind="$props"
|
||||||
|
:class="['rounded text-gray-300 hover:bg-dark-900/70 p-2 flex items-center justify-center duration-200', toggled && 'bg-pink-500/70 hover:bg-pink-900/70']">
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { ButtonHTMLAttributes } from "vue";
|
||||||
|
|
||||||
|
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes { }
|
||||||
|
|
||||||
|
defineProps<
|
||||||
|
Props & {
|
||||||
|
toggled?: boolean;
|
||||||
|
}
|
||||||
|
>();
|
||||||
|
</script>
|
||||||
|
|
@ -14,14 +14,37 @@
|
||||||
<SocialElementsNotesNote :note="respondingTo" :small="true" :disabled="true" />
|
<SocialElementsNotesNote :note="respondingTo" :small="true" :disabled="true" />
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
</div>
|
</div>
|
||||||
<textarea :disabled="submitting" ref="textarea" v-model="content" placeholder="You can use Markdown here!"
|
<textarea :disabled="submitting" ref="textarea" v-model="content" :placeholder="chosenSplash"
|
||||||
class="resize-none min-h-48 prose prose-invert max-h-[70dvh] w-full p-0 focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 bg-transparent appearance-none focus:!border-none focus:!outline-none disabled:cursor-not-allowed"></textarea>
|
class="resize-none min-h-48 prose prose-invert max-h-[70dvh] w-full p-0 focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 bg-transparent appearance-none focus:!border-none focus:!outline-none disabled:cursor-not-allowed"></textarea>
|
||||||
<div
|
<div
|
||||||
:class="['absolute bottom-0 right-0 p-2 text-gray-400 font-semibold text-xs', remainingCharacters < 0 && 'text-red-500']">
|
:class="['absolute bottom-0 right-0 p-2 text-gray-400 font-semibold text-xs', remainingCharacters < 0 && 'text-red-500']">
|
||||||
{{ remainingCharacters }}
|
{{ remainingCharacters }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Content warning textbox -->
|
||||||
|
<div v-if="cw !== null" class="mb-4">
|
||||||
|
<input type="text" placeholder="Add a content warning"
|
||||||
|
class="w-full p-2 mt-1 text-sm prose prose-invert bg-dark-900 rounded focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 appearance-none focus:!border-none focus:!outline-none" />
|
||||||
|
</div>
|
||||||
<div class="flex flex-row gap-1 border-white/20">
|
<div class="flex flex-row gap-1 border-white/20">
|
||||||
|
<ComposerButton title="Mention someone">
|
||||||
|
<Icon name="tabler:at" class="size-6" aria-hidden="true" />
|
||||||
|
</ComposerButton>
|
||||||
|
<ComposerButton title="Toggle Markdown" @click="markdown = !markdown" :toggled="markdown">
|
||||||
|
<Icon :name="markdown ? 'tabler:markdown' : 'tabler:markdown-off'" class="size-6" aria-hidden="true" />
|
||||||
|
</ComposerButton>
|
||||||
|
<ComposerButton title="Use a custom emoji">
|
||||||
|
<Icon name="tabler:mood-smile" class="size-6" aria-hidden="true" />
|
||||||
|
</ComposerButton>
|
||||||
|
<ComposerButton title="Add media">
|
||||||
|
<Icon name="tabler:photo-up" class="size-6" aria-hidden="true" />
|
||||||
|
</ComposerButton>
|
||||||
|
<ComposerButton title="Add a file">
|
||||||
|
<Icon name="tabler:file-upload" class="size-6" aria-hidden="true" />
|
||||||
|
</ComposerButton>
|
||||||
|
<ComposerButton title="Add content warning" @click="cw = cw === null ? '' : null" :toggled="cw !== null">
|
||||||
|
<Icon name="tabler:rating-18-plus" class="size-6" aria-hidden="true" />
|
||||||
|
</ComposerButton>
|
||||||
<ButtonsPrimary :loading="submitting" @click="send" class="ml-auto rounded-full">
|
<ButtonsPrimary :loading="submitting" @click="send" class="ml-auto rounded-full">
|
||||||
<span>Send!</span>
|
<span>Send!</span>
|
||||||
</ButtonsPrimary>
|
</ButtonsPrimary>
|
||||||
|
|
@ -38,10 +61,18 @@ const textarea = ref<HTMLTextAreaElement | undefined>(undefined);
|
||||||
const { input: content } = useTextareaAutosize({
|
const { input: content } = useTextareaAutosize({
|
||||||
element: textarea,
|
element: textarea,
|
||||||
});
|
});
|
||||||
const { Control_Enter, Command_Enter } = useMagicKeys();
|
const { Control_Enter, Command_Enter, Control_Alt } = useMagicKeys();
|
||||||
const respondingTo = ref<Status | null>(null);
|
const respondingTo = ref<Status | null>(null);
|
||||||
const respondingType = ref<"reply" | "quote" | null>(null);
|
const respondingType = ref<"reply" | "quote" | null>(null);
|
||||||
const me = useMe();
|
const me = useMe();
|
||||||
|
const cw = ref(null as string | null);
|
||||||
|
const markdown = ref(true);
|
||||||
|
|
||||||
|
const splashes = useConfig().COMPOSER_SPLASHES;
|
||||||
|
const chosenSplash = ref(splashes[Math.floor(Math.random() * splashes.length)]);
|
||||||
|
watch(Control_Alt, () => {
|
||||||
|
chosenSplash.value = splashes[Math.floor(Math.random() * splashes.length)];
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useListen("composer:reply", (note: Status) => {
|
useListen("composer:reply", (note: Status) => {
|
||||||
|
|
@ -86,7 +117,7 @@ const send = async () => {
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
status: content.value.trim(),
|
status: content.value.trim(),
|
||||||
content_type: "text/markdown",
|
content_type: markdown.value ? "text/markdown" : "text/plain",
|
||||||
in_reply_to_id:
|
in_reply_to_id:
|
||||||
respondingType.value === "reply"
|
respondingType.value === "reply"
|
||||||
? respondingTo.value?.id
|
? respondingTo.value?.id
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,14 @@
|
||||||
<h3 v-if="tokenData"
|
<h3 v-if="tokenData"
|
||||||
class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
|
class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
|
||||||
Posts</h3>
|
Posts</h3>
|
||||||
<ButtonsBase v-if="tokenData" @click="compose"
|
<ButtonsBase v-if="tokenData" @click="compose" title="Open composer (shortcut: n)"
|
||||||
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 bg-gradient-to-tr from-pink-300 via-purple-300 to-indigo-400 overflow-hidden h-12 w-full duration-200">
|
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 bg-gradient-to-tr from-pink-300 via-purple-300 to-indigo-400 overflow-hidden h-12 w-full duration-200">
|
||||||
<Icon name="tabler:writing" class="shrink-0 text-2xl" />
|
<Icon name="tabler:writing" class="shrink-0 text-2xl" />
|
||||||
<span class="pr-28 line-clamp-1">Compose</span>
|
<span class="pr-28 line-clamp-1">Compose</span>
|
||||||
|
<kbd class="text-xs font-semibold rounded bg-dark-500 font-mono px-1 flex flex-row">
|
||||||
|
<Icon name="tabler:keyboard" class="size-4 inline" aria-hidden="true" />
|
||||||
|
<Icon name="tabler:letter-n-small" class="size-4 inline -mr-1" aria-hidden="true" />
|
||||||
|
</kbd>
|
||||||
</ButtonsBase>
|
</ButtonsBase>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -210,7 +214,7 @@ const signOut = async () => {
|
||||||
tokenData.value.access_token,
|
tokenData.value.access_token,
|
||||||
tokenData.value.access_token,
|
tokenData.value.access_token,
|
||||||
)
|
)
|
||||||
.catch(() => {});
|
.catch(() => { });
|
||||||
|
|
||||||
tokenData.value = null;
|
tokenData.value = null;
|
||||||
me.value = null;
|
me.value = null;
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,20 @@ export const useConfig = () => {
|
||||||
link: "https://sk22.github.io/megalodon/",
|
link: "https://sk22.github.io/megalodon/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
COMPOSER_SPLASHES: [
|
||||||
|
"What's on your mind?",
|
||||||
|
"What's happening?",
|
||||||
|
"Meow meow meow meow meow",
|
||||||
|
"Just arrived at Luna Nova Academy",
|
||||||
|
"I'm a little teapot, short and stout",
|
||||||
|
"Hey, you. You're finally awake. You were trying to cross the border, right?",
|
||||||
|
"Aren't you a little short for a stormtrooper?",
|
||||||
|
"I'm a leaf on the wind. Watch how I soar.",
|
||||||
|
"I am Groot.",
|
||||||
|
"Hello everybody, my name is Markiplier",
|
||||||
|
"I'm Commander Shepard, and this is my favorite website on the Citadel",
|
||||||
|
"I'm sorry, Dave. I'm afraid I can't do that.",
|
||||||
|
"I am the Senate",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,10 @@ useServerSeoMeta({
|
||||||
|
|
||||||
const { n } = useMagicKeys();
|
const { n } = useMagicKeys();
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(async () => {
|
||||||
if (n.value) {
|
if (n.value) {
|
||||||
|
// Wait 50ms
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||||
useEvent("composer:open");
|
useEvent("composer:open");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue