Merge remote-tracking branch 'origin/main' into force-css-regrab
This commit is contained in:
commit
3953a754bd
22 changed files with 258 additions and 82 deletions
|
@ -23,3 +23,17 @@ steps:
|
||||||
when:
|
when:
|
||||||
- branch: main
|
- branch: main
|
||||||
event: push
|
event: push
|
||||||
|
|
||||||
|
- name: container-build-and-publish (staging)
|
||||||
|
image: docker
|
||||||
|
commands:
|
||||||
|
- docker login --username radical --password $PASSWORD git.gorb.app
|
||||||
|
- docker buildx build --platform linux/amd64,linux/arm64 --rm --push -t git.gorb.app/gorb/frontend:staging .
|
||||||
|
environment:
|
||||||
|
PASSWORD:
|
||||||
|
from_secret: docker_password
|
||||||
|
volumes:
|
||||||
|
- /var/run/podman/podman.sock:/var/run/docker.sock
|
||||||
|
when:
|
||||||
|
- branch: staging
|
||||||
|
event: push
|
||||||
|
|
|
@ -24,9 +24,7 @@ let displayAvatar: string | null
|
||||||
const user = props.user || props.member?.user
|
const user = props.user || props.member?.user
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
displayName = props.member?.nickname
|
displayName = getDisplayName(user, props.member)
|
||||||
|| user.display_name
|
|
||||||
|| user.username
|
|
||||||
|
|
||||||
if (user.avatar) {
|
if (user.avatar) {
|
||||||
displayAvatar = user.avatar
|
displayAvatar = user.avatar
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="member-item" @click="togglePopup" @blur="hidePopup" tabindex="0">
|
<div class="member-item" @click="togglePopup" @blur="hidePopup" tabindex="0">
|
||||||
<Avatar :member="props.member" class="member-avatar"/>
|
<Avatar :member="props.member" class="member-avatar"/>
|
||||||
<span class="member-display-name">{{ props.member.user.display_name || props.member.user.username }}</span>
|
<span class="member-display-name">{{ getDisplayName(props.member.user, props.member) }}</span>
|
||||||
<UserPopup v-if="isPopupVisible" :user="props.member.user" id="profile-popup" />
|
<UserPopup v-if="isPopupVisible" :user="props.member.user" id="profile-popup" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</div>
|
</div>
|
||||||
<VerticalSpacer />
|
<VerticalSpacer />
|
||||||
|
|
||||||
<NuxtLink class="user-item" :href="`/me/friends`" tabindex="0">
|
<NuxtLink class="user-item" :href="`/me`" tabindex="0">
|
||||||
<Icon class="user-avatar" name="lucide:user" />
|
<Icon class="user-avatar" name="lucide:user" />
|
||||||
<span class="user-display-name">Friends</span>
|
<span class="user-display-name">Friends</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<UserEntry v-for="user of friends" :user="user" :name="user.display_name || user.username"
|
<UserEntry v-for="user of friends" :user="user" :name="getDisplayName(user)"
|
||||||
:href="`/me/${user.uuid}`"/>
|
:href="`/me/${user.uuid}`"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,9 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { fetchFriends } = useApi();
|
const { fetchFriends } = useApi();
|
||||||
|
|
||||||
const friends = await fetchFriends()
|
const friends = await fetchFriends().then((response) => {
|
||||||
|
return response.sort((a, b) => getDisplayName(a).localeCompare(getDisplayName(b)))
|
||||||
|
})
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
variant: string
|
variant: string
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<MessageReply v-if="props.replyMessage" :id="props.message.uuid"
|
<MessageReply v-if="props.replyMessage" :id="props.message.uuid"
|
||||||
:author="props.replyMessage.user.display_name || props.replyMessage.user.username"
|
:author="getDisplayName(props.replyMessage.user)"
|
||||||
:text="props.replyMessage?.message"
|
:text="props.replyMessage?.message"
|
||||||
:reply-id="props.replyMessage.uuid" max-width="reply" />
|
:reply-id="props.replyMessage.uuid" max-width="reply" />
|
||||||
<div class="left-column">
|
<div class="left-column">
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
<div class="message-data">
|
<div class="message-data">
|
||||||
<div class="message-metadata">
|
<div class="message-metadata">
|
||||||
<span class="message-author-username" tabindex="0" :style="`color: ${props.authorColor}`">
|
<span class="message-author-username" tabindex="0" :style="`color: ${props.authorColor}`">
|
||||||
{{ displayName }}
|
{{ getDisplayName(props.author) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="message-date" :title="date.toString()">
|
<span class="message-date" :title="date.toString()">
|
||||||
<span v-if="getDayDifference(date, currentDate) === 1">Yesterday at</span>
|
<span v-if="getDayDifference(date, currentDate) === 1">Yesterday at</span>
|
||||||
|
@ -77,7 +77,6 @@ const dateHidden = ref<boolean>(true);
|
||||||
|
|
||||||
const date = new Date(props.timestamp);
|
const date = new Date(props.timestamp);
|
||||||
const currentDate: Date = new Date()
|
const currentDate: Date = new Date()
|
||||||
const displayName = props.author?.display_name || props.author?.username
|
|
||||||
|
|
||||||
console.log("[MSG] message to render:", props.message);
|
console.log("[MSG] message to render:", props.message);
|
||||||
console.log("author:", props.author);
|
console.log("author:", props.author);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="message-area">
|
<div id="message-area">
|
||||||
<div id="messages" ref="messagesElement">
|
<div id="messages" ref="messagesElement">
|
||||||
<Message v-for="(message, i) of messages" :username="message.user.display_name ?? message.user.username"
|
<Message v-for="(message, i) of messages" :username="getDisplayName(message.user)"
|
||||||
:text="message.message" :timestamp="messageTimestamps[message.uuid]" :img="message.user.avatar"
|
:text="message.message" :timestamp="messageTimestamps[message.uuid]" :img="message.user.avatar"
|
||||||
:format="timeFormat" :type="messagesType[message.uuid]"
|
:format="timeFormat" :type="messagesType[message.uuid]"
|
||||||
:margin-bottom="(messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal') ?? false"
|
:margin-bottom="(messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal') ?? false"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<NuxtLink class="user-item" :href="`/me/${user.uuid}`" tabindex="0">
|
<NuxtLink class="user-item" :href="`/me/${user.uuid}`" tabindex="0">
|
||||||
<Avatar :user="props.user" class="user-avatar"/>
|
<Avatar :user="props.user" class="user-avatar"/>
|
||||||
|
|
||||||
<span class="user-display-name">{{ displayName }}</span>
|
<span class="user-display-name">{{ getDisplayName(props.user) }}</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ const props = defineProps<{
|
||||||
user: UserResponse
|
user: UserResponse
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const displayName = props.user.display_name || props.user.username
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div id="cover-color"></div>
|
<div id="cover-color"></div>
|
||||||
<div id="main-body">
|
<div id="main-body">
|
||||||
<p id="display-name">
|
<p id="display-name">
|
||||||
<strong>{{ props.user.display_name }}</strong>
|
<strong>{{ getDisplayName(props.user) }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<p id="username-and-pronouns">
|
<p id="username-and-pronouns">
|
||||||
{{ props.user.username }}
|
{{ props.user.username }}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div @click="props.callback()" class="button" :class="props.variant + '-button'">
|
<button @click="props.callback ? props.callback() : null" class="button" :class="props.variant + '-button'">
|
||||||
{{ props.text }}
|
{{ props.text }}
|
||||||
</div>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
text: string,
|
text: string,
|
||||||
callback: CallableFunction,
|
callback?: CallableFunction,
|
||||||
variant?: "normal" | "scary" | "neutral",
|
variant?: "normal" | "scary" | "neutral",
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ const props = defineProps<{
|
||||||
border-radius: 0.7rem;
|
border-radius: 0.7rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
|
|
|
@ -33,8 +33,13 @@ export const useApi = () => {
|
||||||
return await fetchWithApi(`/users/${userId}`);
|
return await fetchWithApi(`/users/${userId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFriends(): Promise<UserResponse[] | undefined> {
|
async function fetchFriends(): Promise<UserResponse[]> {
|
||||||
return await fetchWithApi('/me/friends')
|
const response = await fetchWithApi('/me/friends')
|
||||||
|
if (Array.isArray(response)) {
|
||||||
|
return response
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addFriend(username: string): Promise<void> {
|
async function addFriend(username: string): Promise<void> {
|
||||||
|
@ -74,6 +79,14 @@ export const useApi = () => {
|
||||||
await fetchWithApi("/auth/verify-email", { method: "POST", body: { email } });
|
await fetchWithApi("/auth/verify-email", { method: "POST", body: { email } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendPasswordResetEmail(identifier: string): Promise<void> {
|
||||||
|
await fetchWithApi("/auth/reset-password", { method: "GET", query: { identifier } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetPassword(password: string, token: string) {
|
||||||
|
await fetchWithApi("/auth/reset-password", { method: "POST", body: { password, token } });
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fetchGuilds,
|
fetchGuilds,
|
||||||
fetchGuild,
|
fetchGuild,
|
||||||
|
@ -92,6 +105,8 @@ export const useApi = () => {
|
||||||
joinGuild,
|
joinGuild,
|
||||||
createChannel,
|
createChannel,
|
||||||
fetchInstanceStats,
|
fetchInstanceStats,
|
||||||
sendVerificationEmail
|
sendVerificationEmail,
|
||||||
|
sendPasswordResetEmail,
|
||||||
|
resetPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-else id="auth-form-container">
|
<div v-else id="auth-form-container">
|
||||||
<slot />
|
<slot />
|
||||||
|
<div v-if="!['/recover', '/reset-password'].includes(route.path)">Forgot password? Recover <NuxtLink href="/recover">here</NuxtLink>!</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="instanceUrl">
|
<div v-if="instanceUrl">
|
||||||
Instance URL is set to <span style="color: var(--primary-color);">{{ instanceUrl }}</span>
|
Instance URL is set to <span style="color: var(--primary-color);">{{ instanceUrl }}</span>
|
||||||
|
@ -36,7 +37,11 @@ const apiVersion = useRuntimeConfig().public.apiVersion;
|
||||||
const apiBase = useCookie("api_base");
|
const apiBase = useCookie("api_base");
|
||||||
const registrationEnabled = useState("registrationEnabled", () => true);
|
const registrationEnabled = useState("registrationEnabled", () => true);
|
||||||
|
|
||||||
const auth = useAuth();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const query = route.query as Record<string, string>;
|
||||||
|
const searchParams = new URLSearchParams(query);
|
||||||
|
searchParams.delete("token");
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
instanceUrl.value = useCookie("instance_url").value;
|
instanceUrl.value = useCookie("instance_url").value;
|
||||||
|
@ -111,6 +116,7 @@ const form = reactive({
|
||||||
#auth-form-container form {
|
#auth-form-container form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-top: 10dvh;
|
margin-top: 10dvh;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
|
|
|
@ -127,7 +127,7 @@ const options = [
|
||||||
h("input", {
|
h("input", {
|
||||||
id: "guild-name-input",
|
id: "guild-name-input",
|
||||||
type: "text",
|
type: "text",
|
||||||
placeholder: `${user?.display_name || user?.username}'s Awesome Bouncy Castle'`,
|
placeholder: `${getDisplayName(user!)}'s Awesome Bouncy Castle'`,
|
||||||
style: "width: 100%"
|
style: "width: 100%"
|
||||||
}),
|
}),
|
||||||
h(Button, {
|
h(Button, {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (["/login", "/register"].includes(to.path) && !Object.keys(to.query).includes("special")) {
|
if (["/login", "/register", "/recover", "/reset-password"].includes(to.path) && !Object.keys(to.query).includes("special")) {
|
||||||
console.log("path is login or register");
|
console.log("path is login or register");
|
||||||
const apiBase = useCookie("api_base");
|
const apiBase = useCookie("api_base");
|
||||||
console.log("apiBase gotten:", apiBase.value);
|
console.log("apiBase gotten:", apiBase.value);
|
||||||
|
|
|
@ -36,6 +36,7 @@ const form = reactive({
|
||||||
|
|
||||||
const query = useRoute().query as Record<string, string>;
|
const query = useRoute().query as Record<string, string>;
|
||||||
const searchParams = new URLSearchParams(query);
|
const searchParams = new URLSearchParams(query);
|
||||||
|
searchParams.delete("token");
|
||||||
|
|
||||||
const registrationEnabled = ref<boolean>(true);
|
const registrationEnabled = ref<boolean>(true);
|
||||||
const apiBase = useCookie("api_base");
|
const apiBase = useCookie("api_base");
|
||||||
|
@ -48,7 +49,7 @@ if (apiBase.value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerUrl = `/register?${searchParams}`
|
const registerUrl = `/register?${searchParams}`;
|
||||||
|
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
<template>
|
|
||||||
<NuxtLayout name="client">
|
|
||||||
<DirectMessagesSidebar />
|
|
||||||
<div :id="$style['page-content']">
|
|
||||||
<div :id="$style['navigation-bar']">
|
|
||||||
<Button :text="`All Friends – ${friends?.length}`" variant="neutral" :callback="() => updateFilter('all')" />
|
|
||||||
<Button :text="`Online – ${0}`" variant="neutral" :callback="() => updateFilter('online')" />
|
|
||||||
<Button :text="`Pending – ${0}`" variant="neutral" :callback="() => updateFilter('pending')" />
|
|
||||||
<Button text="Add Friend" variant="normal" :callback="() => updateFilter('add')" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<AddFriend v-if="filter == 'add'"></AddFriend>
|
|
||||||
<FriendsList v-else :variant="filter"></FriendsList>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</NuxtLayout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import DirectMessagesSidebar from '~/components/Me/DirectMessagesSidebar.vue';
|
|
||||||
import Button from '~/components/UserInterface/Button.vue';
|
|
||||||
import AddFriend from '~/components/Me/AddFriend.vue';
|
|
||||||
import FriendsList from '~/components/Me/FriendsList.vue';
|
|
||||||
|
|
||||||
const { fetchFriends } = useApi();
|
|
||||||
|
|
||||||
let filter = ref("all");
|
|
||||||
|
|
||||||
const friends = await fetchFriends()
|
|
||||||
|
|
||||||
function updateFilter(newFilter: string) {
|
|
||||||
filter.value = newFilter;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style module>
|
|
||||||
#page-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
|
||||||
|
|
||||||
margin: .75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#navigation-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: left;
|
|
||||||
text-align: left;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
gap: .5em;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,13 +1,56 @@
|
||||||
<template>
|
<template>
|
||||||
<NuxtLayout name="client">
|
<NuxtLayout name="client">
|
||||||
<DirectMessagesSidebar />
|
<DirectMessagesSidebar />
|
||||||
|
<div :id="$style['page-content']">
|
||||||
|
<div :id="$style['navigation-bar']">
|
||||||
|
<Button :text="`All Friends – ${friends?.length}`" variant="neutral" :callback="() => updateFilter('all')" />
|
||||||
|
<Button :text="`Online – ${0}`" variant="neutral" :callback="() => updateFilter('online')" />
|
||||||
|
<Button :text="`Pending – ${0}`" variant="neutral" :callback="() => updateFilter('pending')" />
|
||||||
|
<Button text="Add Friend" variant="normal" :callback="() => updateFilter('add')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<AddFriend v-if="filter == 'add'"></AddFriend>
|
||||||
|
<FriendsList v-else :variant="filter"></FriendsList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import DirectMessagesSidebar from '~/components/Me/DirectMessagesSidebar.vue';
|
import DirectMessagesSidebar from '~/components/Me/DirectMessagesSidebar.vue';
|
||||||
|
import Button from '~/components/UserInterface/Button.vue';
|
||||||
|
import AddFriend from '~/components/Me/AddFriend.vue';
|
||||||
|
import FriendsList from '~/components/Me/FriendsList.vue';
|
||||||
|
|
||||||
|
const { fetchFriends } = useApi();
|
||||||
|
|
||||||
|
let filter = ref("all");
|
||||||
|
|
||||||
|
const friends = await fetchFriends()
|
||||||
|
|
||||||
|
function updateFilter(newFilter: string) {
|
||||||
|
filter.value = newFilter;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style module>
|
||||||
|
#page-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
margin: .75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: left;
|
||||||
|
text-align: left;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
gap: .5em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
89
pages/recover.vue
Normal file
89
pages/recover.vue
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<NuxtLayout name="auth">
|
||||||
|
<div v-if="errorValue">{{ errorValue }}</div>
|
||||||
|
<form v-if="!emailFormSent" @submit.prevent="sendEmail">
|
||||||
|
<div>
|
||||||
|
<label for="identifier">Email or username</label>
|
||||||
|
<br>
|
||||||
|
<input type="text" name="identifier" id="identifier" v-model="emailForm.identifier">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button type="submit" text="Send email" variant="normal" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div v-else>
|
||||||
|
If an account with that username or email exists, an email will be sent to it shortly.
|
||||||
|
</div>
|
||||||
|
<div v-if="registrationEnabled">
|
||||||
|
Don't have an account? <NuxtLink :href="registerUrl">Register</NuxtLink> one!
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Already have an account? <NuxtLink :href="loginUrl">Log in</NuxtLink>!
|
||||||
|
</div>
|
||||||
|
</NuxtLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Button from '~/components/UserInterface/Button.vue';
|
||||||
|
|
||||||
|
const emailForm = reactive({
|
||||||
|
identifier: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const emailFormSent = ref(false);
|
||||||
|
|
||||||
|
const passwordForm = reactive({
|
||||||
|
password: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorValue = ref<string>();
|
||||||
|
|
||||||
|
const registrationEnabled = ref<boolean>(true);
|
||||||
|
const apiBase = useCookie("api_base");
|
||||||
|
|
||||||
|
const query = useRoute().query as Record<string, string>;
|
||||||
|
const searchParams = new URLSearchParams(query);
|
||||||
|
|
||||||
|
const token = ref(searchParams.get("token"))
|
||||||
|
searchParams.delete("token");
|
||||||
|
const { resetPassword } = useApi();
|
||||||
|
|
||||||
|
const registerUrl = `/register?${searchParams}`;
|
||||||
|
const loginUrl = `/login?${searchParams}`;
|
||||||
|
|
||||||
|
if (apiBase.value) {
|
||||||
|
console.log("apiBase:", apiBase.value);
|
||||||
|
const stats = await useApi().fetchInstanceStats(apiBase.value);
|
||||||
|
if (stats) {
|
||||||
|
registrationEnabled.value = stats.registration_enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sendPasswordResetEmail } = useApi();
|
||||||
|
|
||||||
|
async function sendEmail() {
|
||||||
|
try {
|
||||||
|
await sendPasswordResetEmail(emailForm.identifier);
|
||||||
|
emailFormSent.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
errorValue.value = (error as any).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendPassword() {
|
||||||
|
try {
|
||||||
|
console.log("pass:", passwordForm.password);
|
||||||
|
const hashedPass = await hashPassword(passwordForm.password)
|
||||||
|
console.log("hashed pass:", hashedPass);
|
||||||
|
await resetPassword(hashedPass, token.value!);
|
||||||
|
return await navigateTo(`/login?${searchParams}`);
|
||||||
|
} catch (error) {
|
||||||
|
errorValue.value = (error as any).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
|
@ -86,6 +86,7 @@ const auth = useAuth();
|
||||||
const loggedIn = ref(await auth.getUser());
|
const loggedIn = ref(await auth.getUser());
|
||||||
|
|
||||||
const query = new URLSearchParams(useRoute().query as Record<string, string>);
|
const query = new URLSearchParams(useRoute().query as Record<string, string>);
|
||||||
|
query.delete("token");
|
||||||
|
|
||||||
const user = await useAuth().getUser();
|
const user = await useAuth().getUser();
|
||||||
|
|
||||||
|
|
56
pages/reset-password.vue
Normal file
56
pages/reset-password.vue
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<NuxtLayout name="auth">
|
||||||
|
<div v-if="errorValue">{{ errorValue }}</div>
|
||||||
|
<form @submit.prevent="sendPassword">
|
||||||
|
<div>
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<br>
|
||||||
|
<input type="password" name="password" id="password" v-model="passwordForm.password">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button type="submit" text="Submit" variant="normal" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div>
|
||||||
|
Already have an account? <NuxtLink :href="loginUrl">Log in</NuxtLink>!
|
||||||
|
</div>
|
||||||
|
</NuxtLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Button from '~/components/UserInterface/Button.vue';
|
||||||
|
|
||||||
|
const query = useRoute().query as Record<string, string>;
|
||||||
|
const searchParams = new URLSearchParams(query);
|
||||||
|
|
||||||
|
const loginUrl = `/login?${searchParams}`;
|
||||||
|
|
||||||
|
const token = ref(searchParams.get("token"))
|
||||||
|
|
||||||
|
if (!token.value) await navigateTo("/login");
|
||||||
|
|
||||||
|
const passwordForm = reactive({
|
||||||
|
password: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorValue = ref<string>();
|
||||||
|
|
||||||
|
const { resetPassword } = useApi();
|
||||||
|
|
||||||
|
async function sendPassword() {
|
||||||
|
try {
|
||||||
|
console.log("pass:", passwordForm.password);
|
||||||
|
const hashedPass = await hashPassword(passwordForm.password)
|
||||||
|
console.log("hashed pass:", hashedPass);
|
||||||
|
await resetPassword(hashedPass, token.value!);
|
||||||
|
return await navigateTo("/login?");
|
||||||
|
} catch (error) {
|
||||||
|
errorValue.value = (error as any).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
7
utils/getDisplayName.ts
Normal file
7
utils/getDisplayName.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import type { GuildMemberResponse, UserResponse } from "~/types/interfaces";
|
||||||
|
|
||||||
|
export function getDisplayName(user: UserResponse, member?: GuildMemberResponse): string {
|
||||||
|
if (member?.nickname) return member.nickname
|
||||||
|
if (user.display_name) return user.display_name
|
||||||
|
return user.username
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ export default (element: HTMLDivElement, props: MessageProps) => {
|
||||||
const messageBox = document.getElementById("message-box") as HTMLDivElement;
|
const messageBox = document.getElementById("message-box") as HTMLDivElement;
|
||||||
if (messageBox) {
|
if (messageBox) {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
const messageReply = h(MessageReply, { author: props.author?.display_name || props.author!.username, text: props.text || "", id: props.message.uuid, replyId: props.replyMessage?.uuid || element.dataset.messageId!, maxWidth: "full" });
|
const messageReply = h(MessageReply, { author: getDisplayName(props.author), text: props.text || "", id: props.message.uuid, replyId: props.replyMessage?.uuid || element.dataset.messageId!, maxWidth: "full" });
|
||||||
messageBox.prepend(div);
|
messageBox.prepend(div);
|
||||||
render(messageReply, div);
|
render(messageReply, div);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue