build: switch from yarn to npm to manage js dependencies and move js contents to root

yarn v1 is being deprecated and starts to have some issues

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2023-11-14 17:24:42 +01:00
parent 32055122c3
commit 2e72f6faf4
595 changed files with 12078 additions and 7843 deletions

View File

@@ -0,0 +1,77 @@
<template>
<o-inputitems
:modelValue="modelValue"
@update:modelValue="(val: IActor[]) => $emit('update:modelValue', val)"
:data="availableActors"
:allow-autocomplete="true"
:allow-new="false"
:open-on-focus="false"
field="displayName"
placeholder="Add a recipient"
@typing="getActors"
>
<template #default="props">
<ActorInline :actor="props.option" />
</template>
</o-inputitems>
</template>
<script setup lang="ts">
import { SEARCH_PERSON_AND_GROUPS } from "@/graphql/search";
import { IActor, IGroup, IPerson, displayName } from "@/types/actor";
import { Paginate } from "@/types/paginate";
import { useLazyQuery } from "@vue/apollo-composable";
import { ref } from "vue";
import ActorInline from "./ActorInline.vue";
defineProps<{
modelValue: IActor[];
}>();
defineEmits<{
"update:modelValue": [value: IActor[]];
}>();
const {
load: loadSearchPersonsAndGroupsQuery,
refetch: refetchSearchPersonsAndGroupsQuery,
} = useLazyQuery<
{ searchPersons: Paginate<IPerson>; searchGroups: Paginate<IGroup> },
{ searchText: string }
>(SEARCH_PERSON_AND_GROUPS);
const availableActors = ref<IActor[]>([]);
const getActors = async (text: string) => {
availableActors.value = await fetchActors(text);
};
const fetchActors = async (text: string): Promise<IActor[]> => {
if (text === "") return [];
try {
const res =
(await loadSearchPersonsAndGroupsQuery(SEARCH_PERSON_AND_GROUPS, {
searchText: text,
})) ||
(
await refetchSearchPersonsAndGroupsQuery({
searchText: text,
})
)?.data;
if (!res) return [];
return [
...res.searchPersons.elements.map((person) => ({
...person,
displayName: displayName(person),
})),
...res.searchGroups.elements.map((group) => ({
...group,
displayName: displayName(group),
})),
];
} catch (e) {
console.error(e);
return [];
}
};
</script>

View File

@@ -0,0 +1,52 @@
<template>
<Story>
<Variant title="local">
<ActorCard :actor="stateLocal"></ActorCard>
<template #controls>
<HstText v-model="stateLocal.preferredUsername" title="username" />
<HstText v-model="stateLocal.name" title="Name" />
</template>
</Variant>
<Variant title="remote">
<ActorCard :actor="stateRemote"></ActorCard>
<template #controls>
<HstText v-model="stateRemote.preferredUsername" title="username" />
<HstText v-model="stateRemote.name" title="Name" />
<HstText v-model="stateRemote.domain" title="Domain" />
<HstText v-model="avatarUrl" title="Avatar" />
</template>
</Variant>
</Story>
</template>
<script lang="ts" setup>
import ActorCard from "./ActorCard.vue";
import { reactive, ref } from "vue";
import { IActor } from "@/types/actor";
import { ActorType } from "@/types/enums";
const avatarUrl = ref<string>(
"https://stockage.framapiaf.org/framapiaf/accounts/avatars/000/000/399/original/52b08a3e80b43d40.jpg"
);
const stateLocal = reactive<IActor>({
name: "Thomas Citharel",
preferredUsername: "tcit",
avatar: null,
domain: null,
url: "",
summary: "",
suspended: false,
type: ActorType.PERSON,
});
const stateRemote = reactive<IActor>({
name: "Framasoft",
preferredUsername: "framasoft",
avatar: { url: avatarUrl.value, id: "", name: "", alt: "", metadata: {} },
domain: "framapiaf.org",
url: "",
summary: "",
suspended: false,
type: ActorType.PERSON,
});
</script>

View File

@@ -0,0 +1,109 @@
<template>
<div
class="bg-white dark:bg-mbz-purple rounded-lg flex space-x-4 items-center"
:class="{ 'flex-col p-4 shadow-md sm:p-8 pb-10 w-80': !inline }"
>
<div class="flex pl-2">
<figure class="w-12 h-12" v-if="actor.avatar">
<img
class="rounded-full object-cover h-full"
:src="actor.avatar.url"
alt=""
width="48"
height="48"
loading="lazy"
/>
</figure>
<AccountCircle
v-else
:size="inline ? 24 : 48"
class="ltr:-mr-0.5 rtl:-ml-0.5"
/>
</div>
<div :class="{ 'text-center': !inline }" class="overflow-hidden w-full">
<h5
class="text-xl font-medium violet-title tracking-tight text-gray-900 dark:text-gray-200 whitespace-pre-line line-clamp-2"
>
{{ displayName(actor) }}
</h5>
<p class="text-gray-500 dark:text-gray-200 truncate" v-if="actor.name">
<span dir="ltr">@{{ usernameWithDomain(actor) }}</span>
</p>
<div
v-if="full"
class="only-first-child"
:class="{
'line-clamp-3': limit,
'line-clamp-10': !limit,
}"
v-html="actor.summary"
/>
</div>
<div class="flex pr-2">
<Email />
</div>
</div>
<!-- <div
class="p-4 bg-white rounded-lg shadow-md sm:p-8 flex items-center space-x-4"
dir="auto"
>
<div class="flex-shrink-0">
<figure class="w-12 h-12" v-if="actor.avatar">
<img
class="rounded-lg"
:src="actor.avatar.url"
alt=""
width="48"
height="48"
/>
</figure>
<o-icon
v-else
size="large"
icon="account-circle"
class="ltr:-mr-0.5 rtl:-ml-0.5"
/>
</div>
<div class="flex-1 min-w-0">
<h5 class="text-xl font-medium violet-title tracking-tight text-gray-900">
{{ displayName(actor) }}
</h5>
<p class="text-gray-500 truncate" v-if="actor.name">
<span dir="ltr">@{{ usernameWithDomain(actor) }}</span>
</p>
<div
v-if="full"
class="line-clamp-3"
:class="{ limit: limit }"
v-html="actor.summary"
/>
</div>
</div> -->
</template>
<script lang="ts" setup>
import { displayName, IActor, usernameWithDomain } from "../../types/actor";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Email from "vue-material-design-icons/Email.vue";
withDefaults(
defineProps<{
actor: IActor;
full?: boolean;
inline?: boolean;
popover?: boolean;
limit?: boolean;
}>(),
{
full: false,
inline: false,
popover: false,
limit: true,
}
);
</script>
<style scoped>
.only-first-child :deep(:not(:first-child)) {
display: none;
}
</style>

View File

@@ -0,0 +1,52 @@
<template>
<Story>
<Variant title="local">
<ActorInline :actor="stateLocal" />
<template #controls>
<HstText v-model="stateLocal.preferredUsername" title="username" />
<HstText v-model="stateLocal.name" title="Name" />
</template>
</Variant>
<Variant title="remote">
<ActorInline :actor="stateRemote" />
<template #controls>
<HstText v-model="stateRemote.preferredUsername" title="username" />
<HstText v-model="stateRemote.name" title="Name" />
<HstText v-model="stateRemote.domain" title="Domain" />
<HstText v-model="avatarUrl" title="Avatar" />
</template>
</Variant>
</Story>
</template>
<script lang="ts" setup>
import ActorInline from "./ActorInline.vue";
import { reactive, ref } from "vue";
import { IActor } from "@/types/actor";
import { ActorType } from "@/types/enums";
const avatarUrl = ref<string>(
"https://stockage.framapiaf.org/framapiaf/accounts/avatars/000/000/399/original/52b08a3e80b43d40.jpg"
);
const stateLocal = reactive<IActor>({
name: "Thomas Citharel",
preferredUsername: "tcit",
avatar: null,
domain: null,
url: "",
summary: "",
suspended: false,
type: ActorType.PERSON,
});
const stateRemote = reactive<IActor>({
name: "Framasoft",
preferredUsername: "framasoft",
avatar: { url: avatarUrl.value, id: "", name: "", alt: "", metadata: {} },
domain: "framapiaf.org",
url: "",
summary: "",
suspended: false,
type: ActorType.PERSON,
});
</script>

View File

@@ -0,0 +1,62 @@
<template>
<div
class="inline-flex items-start gap-2 bg-white dark:bg-violet-1 dark:text-white p-2 rounded-md"
>
<div class="flex-none">
<figure v-if="actor.avatar">
<img
class="rounded-xl"
:src="actor.avatar.url"
alt=""
width="36"
height="36"
loading="lazy"
/>
</figure>
<AccountCircle :size="36" v-else />
</div>
<div class="flex-auto">
<p class="text-lg line-clamp-3 md:line-clamp-2 max-w-xl">
{{ displayName(actor) }}
</p>
<p class="text-sm text-gray-500 dark:text-gray-300 truncate">
@{{ usernameWithDomain(actor) }}
</p>
</div>
<div class="flex pr-2 self-center">
<Email />
</div>
</div>
</template>
<script lang="ts" setup>
import { displayName, IActor, usernameWithDomain } from "../../types/actor";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Email from "vue-material-design-icons/Email.vue";
defineProps<{
actor: IActor;
}>();
</script>
<style lang="scss" scoped>
@use "@/styles/_mixins" as *;
div.actor-inline {
align-items: flex-start;
display: inline-flex;
text-align: inherit;
align-items: top;
div.actor-avatar {
flex-basis: auto;
flex-grow: 0;
flex-shrink: 0;
// @include margin-right(0.5rem);
}
div.actor-name {
flex-basis: auto;
flex-grow: 1;
flex-shrink: 1;
text-align: inherit;
}
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<Story>
<Variant :setup-app="setupApp" title="Person">
<div class="p-5">
<PopoverActorCard :actor="baseActor">
<div><b> Popover me !</b></div></PopoverActorCard
>
</div>
</Variant>
<Variant :setup-app="setupApp" title="Group">
<div class="p-5">
<PopoverActorCard :actor="group">
<div><b> Popover me !</b></div></PopoverActorCard
>
</div>
</Variant>
</Story>
</template>
<script lang="ts" setup>
import PopoverActorCard from "./PopoverActorCard.vue";
import FloatingVue from "floating-vue";
import "floating-vue/dist/style.css";
import { ActorType } from "@/types/enums";
const baseActorAvatar = {
id: "",
name: "",
alt: "",
metadata: {},
url: "https://social.tcit.fr/system/accounts/avatars/000/000/001/original/a28c50ce5f2b13fd.jpg",
};
const baseActor = {
name: "Thomas Citharel",
preferredUsername: "tcit",
avatar: baseActorAvatar,
domain: null,
url: "",
summary: "",
suspended: false,
type: ActorType.PERSON,
};
const group = {
...baseActor,
name: "Framasoft",
preferredUsername: "framasoft",
domain: "mobilizon.fr",
avatar: {
...baseActorAvatar,
url: "https://stockage.framapiaf.org/framapiaf/accounts/avatars/000/000/399/original/52b08a3e80b43d40.jpg",
},
};
function setupApp({ app }) {
app.use(FloatingVue);
}
</script>

View File

@@ -0,0 +1,28 @@
<template>
<VMenu
:distance="16"
:triggers="['hover']"
class="popover"
:class="{ inline, clickable: actor && actor.type === ActorType.GROUP }"
>
<slot></slot>
<template #popper>
<actor-card :full="true" :actor="actor" :popover="true" />
</template>
</VMenu>
</template>
<script lang="ts" setup>
import { ActorType } from "@/types/enums";
import { IActor } from "../../types/actor";
import ActorCard from "./ActorCard.vue";
withDefaults(
defineProps<{
actor: IActor;
inline?: boolean;
}>(),
{
inline: false,
}
);
</script>

View File

@@ -0,0 +1,29 @@
<template>
<Story>
<Variant>
<div class="p-5">
<ProfileOnboarding
:current-actor="baseActor"
instance-name="Instance name"
/>
</div>
</Variant>
</Story>
</template>
<script lang="ts" setup>
import ProfileOnboarding from "./ProfileOnboarding.vue";
import { ActorType } from "@/types/enums";
import { IPerson } from "@/types/actor";
const baseActor: IPerson = {
name: "Thomas Citharel",
preferredUsername: "tcit",
avatar: null,
domain: null,
url: "",
summary: "",
suspended: false,
type: ActorType.PERSON,
};
</script>

View File

@@ -0,0 +1,60 @@
<template>
<div class="">
<h2 class="text-2xl">{{ t("Profiles and federation") }}</h2>
</div>
<p class="my-2">
{{
t(
"Mobilizon uses a system of profiles to compartiment your activities. You will be able to create as many profiles as you want."
)
}}
</p>
<hr role="presentation" />
<p class="my-2">
<span>
{{
t(
"Mobilizon is a federated software, meaning you can interact - depending on your admin's federation settings - with content from other instances, such as joining groups or events that were created elsewhere."
)
}}
</span>
<i18n-t
keypath="This instance, {instanceName}, hosts your profile, so remember its name."
>
<template #instanceName>
<b>{{
t("{instanceName} ({domain})", {
domain,
instanceName,
})
}}</b>
</template>
</i18n-t>
</p>
<hr role="presentation" />
<p class="my-2">
{{
t(
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:"
)
}}
</p>
<div class="text-center">
<code>{{ `${currentActor?.preferredUsername}@${domain}` }}</code>
</div>
</template>
<script lang="ts" setup>
import { IPerson } from "@/types/actor";
import { computed } from "vue";
import { useI18n } from "vue-i18n";
defineProps<{
currentActor: IPerson;
instanceName: string;
}>();
const { t } = useI18n({ useScope: "global" });
const domain = computed(() => window.location.hostname);
</script>