Skip to content

Commit

Permalink
feat: ♻️ Change code to build in static mode, add timelines, profiles…
Browse files Browse the repository at this point in the history
… and notes, new design
  • Loading branch information
CPlusPatch committed Apr 22, 2024
1 parent 9edfd5a commit acd50ec
Show file tree
Hide file tree
Showing 75 changed files with 1,610 additions and 556 deletions.
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
FROM oven/bun:1.1.3 AS base
# Temporarily switch back to the non-alpine version of Bun because of Bun's issues with musl
FROM oven/bun:1.1.4-alpine AS base

# Install dependencies into temp directory
# This will cache them and speed up future builds
Expand All @@ -18,6 +17,8 @@ FROM base as final

COPY --from=builder /app/.output/ /app

RUN bun add --global serve

LABEL org.opencontainers.image.authors "Gaspard Wierzbinski (https://cpluspatch.com)"
LABEL org.opencontainers.image.source "https://github.com/lysand-org/lysand-fe"
LABEL org.opencontainers.image.vendor "Lysand Org"
Expand All @@ -26,4 +27,4 @@ LABEL org.opencontainers.image.title "Lysand-FE"
LABEL org.opencontainers.image.description "Frontend for the Lysand Project"

WORKDIR /app/server
CMD ["bun", "index.mjs"]
CMD ["bunx", "serve", "public"]
17 changes: 13 additions & 4 deletions app.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<NuxtPage>
<slot />
</NuxtPage>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>

<script setup lang="ts">
Expand All @@ -10,4 +10,13 @@ useServerSeoMeta({
return titleChunk ? `${titleChunk} · Lysand` : "Lysand";
},
});
</script>
// Use SSR-safe IDs for Headless UI
provideHeadlessUseId(() => useId());
</script>

<style>
body {
font-family: Inter, sans-serif;
}
</style>
Binary file modified bun.lockb
Binary file not shown.
26 changes: 8 additions & 18 deletions components/LoginInput.vue
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
<template>
<div class="flex items-center justify-between">
<label for="password" class="block text-sm font-medium leading-6 text-gray-900">{{ label }}</label>
<label for="password" class="block text-sm font-medium leading-6 text-gray-50">{{ label }}</label>
</div>
<div class="mt-2">
<input v-bind="$attrs" @input="checkValid" :class="['block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6',
(isInvalid || error) && 'invalid:!ring-red-600 invalid:ring-2']">
<span v-if="isInvalid || error" class="mt-1 text-xs text-red-600">{{ error ? error : `${label} is invalid` }}</span>
<input v-bind="$attrs" :class="['block disabled:opacity-70 disabled:hover:cursor-wait w-full bg-dark-500 rounded-md border-0 py-1.5 text-gray-50 shadow-sm ring-1 ring-inset ring-white/10 placeholder:text-gray-500 focus:ring-2 focus:ring-inset focus:ring-pink-600 sm:text-sm sm:leading-6',
isInvalid && '!ring-red-600 ring-2']">
</div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import type { InputHTMLAttributes } from 'vue';
const props = defineProps<{
interface Props extends /* @vue-ignore */ InputHTMLAttributes {
isInvalid?: boolean;
label: string;
error?: string;
}>();
}
const isInvalid = ref(false);
const checkValid = (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.checkValidity()) {
isInvalid.value = false;
} else {
isInvalid.value = true;
}
};
defineProps<Props>();
</script>
16 changes: 16 additions & 0 deletions components/buttons/base.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<button v-bind="$props" type="button"
class="rounded-md duration-200 hover:shadow disabled:opacity-70 disabled:cursor-wait px-3 py-2 text-sm font-semibold text-white shadow-sm">
<slot />
</button>
</template>

<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes { }
defineProps<Props>();
</script>

<style></style>
15 changes: 15 additions & 0 deletions components/buttons/primary.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<ButtonsBase class="bg-pink-500 hover:bg-pink-400">
<slot />
</ButtonsBase>
</template>

<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}
defineProps<Props>();
</script>

<style></style>
15 changes: 15 additions & 0 deletions components/buttons/secondary.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<ButtonsBase class="bg-white/10 hover:bg-white/20">
<slot />
</ButtonsBase>
</template>

<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}
defineProps<Props>();
</script>

<style></style>
81 changes: 81 additions & 0 deletions components/headers/navbar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<header class="absolute inset-x-0 top-0 z-50">
<div
class="relative isolate warning-background flex items-center gap-x-6 overflow-hidden bg-dark-900 px-6 py-2.5 sm:px-3.5 sm:before:flex-1">
<div class="flex flex-wrap justify-center gap-x-4 gap-y-2 w-full">
<p class="text-sm text-gray-50 bg-dark-900 px-2 rounded py-1">
<strong class="font-semibold">Warning!</strong>&nbsp;&nbsp;This is a testing site used for
development, not a finished page.
</p>
</div>
<div class="flex justify-end">
</div>
</div>
<nav class="flex items-center justify-between p-6 lg:px-8" aria-label="Global">
<div class="flex lg:flex-1">
<NuxtLink href="/" class="-m-1.5 p-1.5">
<span class="sr-only">Lysand</span>
<img class="h-8 w-auto" src="https://lysand.org/logo.png" alt="" />
</NuxtLink>
</div>
<div class="flex lg:hidden">
<button type="button"
class="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-200"
@click="mobileMenuOpen = true">
<span class="sr-only">Open main menu</span>
<Icon name="tabler:menu-2" class="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div class="hidden lg:flex lg:gap-x-12">
<NuxtLink v-for="item in navigation" :key="item.name" :href="item.href"
class="text-sm font-semibold leading-6 text-gray-50">{{ item.name }}</NuxtLink>
</div>
<div class="hidden lg:flex lg:flex-1 lg:justify-end">

</div>
</nav>
<HeadlessDialog as="div" class="lg:hidden" @close="mobileMenuOpen = false" :open="mobileMenuOpen">
<div class="fixed inset-0 z-50" />
<HeadlessDialogPanel
class="fixed inset-y-0 right-0 z-50 w-full overflow-y-auto bg-dark-800 px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-50/10">
<div class="flex items-center justify-between">
<NuxtLink href="/" class="-m-1.5 p-1.5">
<span class="sr-only">Lysand</span>
<img class="h-8 w-auto" src="https://lysand.org/logo.png" alt="" />
</NuxtLink>
<button type="button" class="-m-2.5 rounded-md p-2.5 text-gray-200" @click="mobileMenuOpen = false">
<span class="sr-only">Close menu</span>
<Icon name="tabler:x" class="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div class="mt-6 flow-root">
<div class="-my-6 divide-y divide-gray-400/10">
<div class="space-y-2 py-6">
<NuxtLink v-for="item in navigation" :key="item.name" :href="item.href"
class="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-50 hover:bg-gray-900">
{{
item.name }}</NuxtLink>
</div>
</div>
</div>
</HeadlessDialogPanel>
</HeadlessDialog>
</header>
</template>

<script lang="ts" setup>
const navigation = [
{ name: "About", href: "/" },
{ name: "Roadmap", href: "/" },
{ name: "Documentation", href: "/" },
{ name: "Team", href: "/" },
];
const mobileMenuOpen = ref(false);
</script>

<style>
.warning-background {
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23f9d63d' fill-opacity='0.4' fill-rule='evenodd'%3E%3Cpath d='M0 40L40 0H20L0 20M40 40V20L20 40'/%3E%3C/g%3E%3C/svg%3E");
}
</style>
44 changes: 44 additions & 0 deletions components/skeleton/Skeleton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<div v-for="index of lines" :class="[
'duration-200 animate-pulse bg-dark-100 [&:not(:first-child)]:mt-2',
shape === 'circle' ? 'rounded-full' : 'rounded',
['text', 'content'].includes(type) && 'h-[1em]',
props.class
]" v-if="enabled" :style="{
width: getWidth(index, lines),
}">
</div>
<slot v-else />
</template>

<script lang="ts" setup>
const props = withDefaults(defineProps<{
enabled: boolean;
shape?: "circle" | "rect";
type?: "text" | "content";
minWidth?: number;
maxWidth?: number;
widthUnit?: "px" | "%";
class?: string;
}>(), {
shape: "rect",
type: "text",
widthUnit: "px",
});
const isContent = computed(() => props.type === "content");
const isText = computed(() => props.type === "text");
const isWidthSpecified = computed(() => props.minWidth && props.maxWidth);
const calculatedWidth = computed(() => Math.random() * ((props.maxWidth ?? 0) - (props.minWidth ?? 0)) + (props.minWidth ?? 0));
const getWidth = (index: number, lines: number) => {
if (isWidthSpecified.value) {
if (isContent.value)
return index === lines ? `${calculatedWidth.value}${props.widthUnit}` : '100%';
return `${calculatedWidth.value}${props.widthUnit}`;
}
return undefined;
};
const lines = isContent.value ? Math.ceil(Math.random() * 5) : 1;
</script>
55 changes: 55 additions & 0 deletions components/social-elements/notes/attachment.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div @click="lightbox = true"
class="aspect-video w-full rounded ring-white/5 shadow overflow-hidden ring-1 hover:ring-2 duration-100">
<img v-if="attachment.type === 'image'"
class="object-cover w-full h-full rounded duration-150 hover:scale-[102%] ease-in-out" :src="attachment.url"
:alt="attachment.description ?? ''" :title="attachment.description ?? ''" />
<video v-else-if="attachment.type === 'video'" class="object-cover w-full h-full rounded" controls
:alt="attachment.description ?? ''" :title="attachment.description ?? ''">
<source :src="attachment.url" type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
<HeadlessTransitionRoot appear :show="lightbox" as="template">
<HeadlessDialog @close="lightbox = false">
<div class="fixed inset-0 overflow-y-auto bg-black/70">
<div class="flex min-h-full items-center justify-center text-center">
<HeadlessTransitionChild as="template" enter="duration-100 ease-out" enter-from="opacity-0 scale-95"
enter-to="opacity-100 scale-100">
<HeadlessDialogPanel
class="w-screen h-screen flex justify-center items-center flex-col relative overflow-hidden p-10"
@click="lightbox = false">
<div class="w-full absolute inset-x-0 top-0 p-10 shrink text-gray-400 flex flex-row gap-3">
<a @click.stop :href="attachment.url" target="_blank" download class="ml-auto">
<Icon name="tabler:download" class="w-6 h-6" />
<span class="sr-only">Close</span>
</a>
<button @click.stop="lightbox = false" class="">
<Icon name="tabler:x" class="w-6 h-6" />
<span class="sr-only">Close</span>
</button>
</div>
<img @click.stop v-if="attachment.type === 'image'"
class="rounded max-w-full min-w-[30%] max-h-[70%]" :src="attachment.url"
:alt="attachment.description ?? ''" :title="attachment.description ?? ''" />
<span @click.stop v-if="attachment.description"
class="text-gray-300 rounded mt-6 -mb-20 px-4 py-2 max-w-xl ring-1 ring-white/5 bg-dark-900 max-h-40 overflow-y-auto">
{{ attachment.description }}
</span>
</HeadlessDialogPanel>
</HeadlessTransitionChild>
</div>
</div>
</HeadlessDialog>
</HeadlessTransitionRoot>
</template>

<script lang="ts" setup>
import type { Attachment } from '~/types/mastodon/attachment';
const lightbox = ref(false);
defineProps<{
attachment: Attachment;
}>();
</script>
16 changes: 16 additions & 0 deletions components/social-elements/notes/mention.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<span
class="shrink break-all rounded bg-pink-700/30 text-pink-200 px-2 py-1 not-prose font-semibold cursor-pointer [&:not(:last-child)]:mr-1 duration-200 hover:bg-pink-600/30">
<img class="h-[1em] w-[1em] rounded ring-1 ring-white/5 inline align-middle mb-1 mr-1" :src="account.avatar"
alt="" />
{{ account.display_name }}
</span>
</template>

<script lang="ts" setup>
import type { Account } from '~/types/mastodon/account';
const props = defineProps<{
account: Account;
}>();
</script>
Loading

0 comments on commit acd50ec

Please sign in to comment.