Compare commits

..

10 commits

11 changed files with 114 additions and 76 deletions

View file

@ -1,5 +1,5 @@
<template>
<div v-if="props.type == 'normal'" class="message normal-message" :class="{ 'message-margin-bottom': props.marginBottom }">
<div v-if="props.type == 'normal'" :id="props.last ? 'last-message' : undefined" class="message normal-message" :class="{ 'message-margin-bottom': props.marginBottom }">
<div class="left-column">
<img v-if="props.img" class="message-author-avatar" :src="props.img" :alt="username">
<Icon v-else name="lucide:user" class="message-author-avatar" />
@ -18,7 +18,7 @@
</div>
</div>
</div>
<div v-else ref="messageElement" class="message grouped-message">
<div v-else ref="messageElement" :id="props.last ? 'last-message' : undefined" class="message grouped-message">
<div class="left-column">
<div>
<span :class="{ 'invisible': dateHidden }" class="message-date" :title="date.toString()">
@ -43,7 +43,8 @@ const props = defineProps<{
timestamp: number,
format: "12" | "24",
type: "normal" | "grouped",
marginBottom: boolean
marginBottom: boolean,
last: boolean
}>();
const messageDate = ref<string>();
@ -56,6 +57,7 @@ const date = new Date(props.timestamp);
console.log("Message.vue: message:", props.text);
console.log("Message.vue: message type:", props.type);
console.log("Message.vue: is last?", props.last);
let dateHour = date.getHours();
let dateMinute = date.getMinutes();
@ -102,6 +104,10 @@ onMounted(() => {
margin-bottom: 1dvh;
}
#last-message {
margin-bottom: 2dvh;
}
.message-metadata {
display: flex;
gap: .5dvw;

View file

@ -1,12 +1,11 @@
<template>
<div id="message-area">
<div id="messages" ref="messagesElement">
<div v-for="(message, i) of messages">
<Message :username="message.user.display_name ?? message.user.username"
:text="message.message" :timestamp="messageTimestamps[message.uuid]" :img="message.user.avatar"
format="12" :type="messagesType[message.uuid]"
:margin-bottom="messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal'" />
</div>
<Message v-for="(message, i) of messages" :username="message.user.display_name ?? message.user.username"
:text="message.message" :timestamp="messageTimestamps[message.uuid]" :img="message.user.avatar"
format="12" :type="messagesType[message.uuid]"
:margin-bottom="messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal'"
:last="i == messages.length - 1" />
</div>
<div id="message-box" class="rounded-corners">
<form id="message-form" @submit="sendMessage">
@ -176,7 +175,8 @@ onMounted(async () => {
padding-bottom: 1dvh;
padding-top: 1dvh;
margin-bottom: 1dvh;
margin-top: 2dvh;
margin-left: 1dvw;
margin-right: 1dvw;
}
#message-form {
@ -198,6 +198,8 @@ onMounted(async () => {
display: flex;
flex-direction: column;
gap: 1dvh;
padding-left: 1dvw;
padding-right: 1dvw;
}
#submit-button {

56
composables/api.ts Normal file
View file

@ -0,0 +1,56 @@
import type { ChannelResponse, GuildMemberResponse, GuildResponse, MessageResponse } from "~/types/interfaces";
export const useApi = () => {
async function fetchGuilds(): Promise<GuildResponse[] | undefined> {
return await fetchWithApi(`/guilds`);
}
async function fetchGuild(guildId: string): Promise<GuildResponse | undefined> {
return await fetchWithApi(`/guilds/${guildId}`);
}
async function fetchChannels(guildId: string): Promise<ChannelResponse[] | undefined> {
return await fetchWithApi(`/guilds/${guildId}/channels`);
}
async function fetchChannel(channelId: string): Promise<ChannelResponse | undefined> {
return await fetchWithApi(`/channels/${channelId}`)
}
async function fetchMembers(guildId: string): Promise<GuildMemberResponse[] | undefined> {
return await fetchWithApi(`/guilds/${guildId}/members`);
}
async function fetchMember(guildId: string, memberId: string): Promise<GuildMemberResponse | undefined> {
return await fetchWithApi(`/guilds/${guildId}/members/${memberId}`);
}
async function fetchUsers() {
return await fetchWithApi(`/users`);
}
async function fetchUser(userId: string) {
return await fetchWithApi(`/users/${userId}`);
}
async function fetchMessages(channelId: string, options?: { amount?: number, offset?: number }): Promise<MessageResponse[] | undefined> {
return await fetchWithApi(`/channels/${channelId}/messages`, { query: { amount: options?.amount ?? 100, offset: options?.offset ?? 0 } });
}
async function fetchMessage(channelId: string, messageId: string): Promise<MessageResponse | undefined> {
return await fetchWithApi(`/channels/${channelId}/messages/${messageId}`);
}
return {
fetchGuilds,
fetchGuild,
fetchChannels,
fetchChannel,
fetchMembers,
fetchMember,
fetchUsers,
fetchUser,
fetchMessages,
fetchMessage
}
}

View file

@ -67,7 +67,7 @@ if (status.value == "success" && gorbTxt.value) {
console.log("got gorb.txt:", gorbTxt.value);
const parsed = parseWellKnown(gorbTxt.value as string);
if (parsed.ApiBaseUrl) {
apiBase.value = parsed.ApiBaseUrl;
apiBase.value = `${parsed.ApiBaseUrl}/${apiVersion}`;
console.log("set apiBase to:", parsed.ApiBaseUrl);
}
} else {

View file

@ -23,7 +23,6 @@
<script lang="ts" setup>
import type { GuildResponse } from '~/types/interfaces';
const loading = useState("loading", () => false);
const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");

15
middleware/server.ts Normal file
View file

@ -0,0 +1,15 @@
import type { ChannelResponse } from "~/types/interfaces";
export default defineNuxtRouteMiddleware(async (to, from) => {
const { fetchChannels } = useApi();
const guildId = to.params.serverId as string;
const channels: ChannelResponse[] | undefined = await fetchChannels(guildId);
console.log("channels:", channels);
if (channels && channels.length > 0) {
console.log("wah");
return await navigateTo(`/servers/${guildId}/channels/${channels[0].uuid}`, { replace: true });
}
})

View file

@ -26,9 +26,9 @@
<MessageArea :channel-url="channelUrlPath" />
<div id="members-list">
<div class="member-item" v-for="member of members">
<img v-if="member.avatar" :src="member.avatar" :alt="member.displayName" height="30" />
<Icon v-else name="lucide:user" size="30" />
<span class="member-display-name">{{ member.displayName }}</span>
<img v-if="member.user.avatar" class="member-avatar" :src="member.user.avatar" :alt="member.user.display_name ?? member.user.username" />
<Icon v-else class="member-avatar" name="lucide:user" />
<span class="member-display-name">{{ member.user.display_name ?? member.user.username }}</span>
</div>
</div>
</NuxtLayout>
@ -52,53 +52,9 @@ import type { ChannelResponse, GuildResponse, MessageResponse } from "~/types/in
//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[];
//console.log("channelid: servers:", servers);
const members = [
{
id: "3287484395",
displayName: "SauceyRed",
avatar: ""
},
{
id: "3287484395",
displayName: "JustTemmie",
avatar: ""
},
{
id: "3287484395",
displayName: "GOIN!!!!!!",
avatar: ""
},
{
id: "3287484395",
displayName: "SauceyRed",
avatar: ""
},
{
id: "3287484395",
displayName: "Hatsune Miku Official",
avatar: ""
},
{
id: "3287484395",
displayName: "Hatsune Miku Official",
avatar: ""
},
{
id: "3287484395",
displayName: "Hatsune Miku Official",
avatar: ""
},
{
id: "3287484395",
displayName: "SauceyRed",
avatar: ""
},
{
id: "3287484395",
displayName: "SauceyRed",
avatar: ""
}
];
const { fetchMembers } = useApi();
const members = await fetchMembers(route.params.serverId as string);
onMounted(async () => {
console.log("channelid: set loading to true");
@ -129,6 +85,7 @@ function toggleInvitePopup(e: Event) {
align-items: center;
margin-top: .5em;
margin-bottom: .5em;
gap: .5em;
}
#members-list {
@ -153,4 +110,10 @@ function toggleInvitePopup(e: Event) {
gap: 1dvh;
}
.member-avatar {
height: 2.3em;
width: 2.3em;
border-radius: 50%;
}
</style>

View file

@ -5,8 +5,9 @@
</template>
<script lang="ts" setup>
const guild = await fetchWithApi(`/guilds/${useRoute().params.serverId}`);
console.log("guild:", guild);
definePageMeta({
middleware: "server"
});
</script>

View file

@ -23,6 +23,14 @@ export interface GuildResponse {
member_count: number
}
export interface GuildMemberResponse {
uuid: string,
nickname: string,
user_uuid: string,
guild_uuid: string,
user: UserResponse
}
export interface ChannelResponse {
uuid: string,
guild_uuid: string,

View file

@ -1,6 +0,0 @@
import type { UserResponse } from "~/types/interfaces"
export default async (serverId: string, memberId: string): Promise<UserResponse> => {
const user = await fetchWithApi(`/guilds/${serverId}/members/${memberId}`) as UserResponse;
return user;
}

View file

@ -1,6 +0,0 @@
import type { UserResponse } from "~/types/interfaces"
export default async (serverId: string, userId: string): Promise<UserResponse> => {
const user = await fetchWithApi(`/users/${userId}`) as UserResponse;
return user;
}