Migrate to Vue 3 and Vite

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2022-07-12 10:55:28 +02:00
parent 8f4099ee33
commit ee20e03cc2
464 changed files with 31515 additions and 32758 deletions

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://framapiaf.s3.framasoft.org/framapiaf/accounts/avatars/000/000/399/original/aa56a445efb72803.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

@@ -1,9 +1,9 @@
<template>
<div
class="bg-white rounded-lg flex space-x-4 items-center"
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>
<div class="flex pl-2">
<figure class="w-12 h-12" v-if="actor.avatar">
<img
class="rounded-lg"
@@ -13,16 +13,15 @@
height="48"
/>
</figure>
<b-icon
<AccountCircle
v-else
:size="inline ? 'is-medium' : 'is-large'"
icon="account-circle"
: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 whitespace-pre-line line-clamp-2"
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>
@@ -54,9 +53,9 @@
height="48"
/>
</figure>
<b-icon
<o-icon
v-else
size="is-large"
size="large"
icon="account-circle"
class="ltr:-mr-0.5 rtl:-ml-0.5"
/>
@@ -78,29 +77,28 @@
</div>
</div> -->
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
<script lang="ts" setup>
import { displayName, IActor, usernameWithDomain } from "../../types/actor";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
@Component
export default class ActorCard extends Vue {
@Prop({ required: true, type: Object }) actor!: IActor;
@Prop({ required: false, type: Boolean, default: false }) full!: boolean;
@Prop({ required: false, type: Boolean, default: false }) inline!: boolean;
@Prop({ required: false, type: Boolean, default: false }) popover!: boolean;
@Prop({ required: false, type: Boolean, default: true }) limit!: boolean;
usernameWithDomain = usernameWithDomain;
displayName = displayName;
}
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 ::v-deep :not(:first-child) {
.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://framapiaf.s3.framasoft.org/framapiaf/accounts/avatars/000/000/399/original/aa56a445efb72803.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

@@ -1,34 +1,37 @@
<template>
<div class="inline-flex items-start">
<div
class="inline-flex items-start bg-white dark:bg-violet-1 dark:text-white p-2 rounded-md"
>
<div class="flex-none mr-2">
<figure class="image is-48x48" v-if="actor.avatar">
<img class="is-rounded" :src="actor.avatar.url" alt="" />
<figure v-if="actor.avatar">
<img
class="rounded-xl"
:src="actor.avatar.url"
alt=""
width="36"
height="36"
/>
</figure>
<b-icon v-else size="is-large" icon="account-circle" />
<AccountCircle :size="36" v-else />
</div>
<div class="flex-auto">
<p class="text-base line-clamp-3 md:line-clamp-2 max-w-xl">
<p class="text-lg line-clamp-3 md:line-clamp-2 max-w-xl">
{{ displayName(actor) }}
</p>
<p class="text-sm text-gray-500 truncate">
<p class="text-sm text-gray-500 dark:text-gray-300 truncate">
@{{ usernameWithDomain(actor) }}
</p>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
<script lang="ts" setup>
import { displayName, IActor, usernameWithDomain } from "../../types/actor";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
@Component
export default class ActorInline extends Vue {
@Prop({ required: true, type: Object }) actor!: IActor;
usernameWithDomain = usernameWithDomain;
displayName = displayName;
}
defineProps<{
actor: IActor;
}>();
</script>
<style lang="scss" scoped>
@use "@/styles/_mixins" as *;
@@ -42,7 +45,7 @@ div.actor-inline {
flex-basis: auto;
flex-grow: 0;
flex-shrink: 0;
@include margin-right(0.5rem);
// @include margin-right(0.5rem);
}
div.actor-name {
flex-basis: auto;

View File

@@ -1,90 +0,0 @@
<template>
<section>
<h1 class="title">
{{ $t("My identities") }}
</h1>
<ul class="identities">
<li v-for="identity in identities" :key="identity.id">
<router-link
:to="{
name: 'UpdateIdentity',
params: { identityName: identity.preferredUsername },
}"
class="media identity"
v-bind:class="{ 'is-current-identity': isCurrentIdentity(identity) }"
>
<div class="media-left">
<figure class="image is-48x48" v-if="identity.avatar">
<img class="is-rounded" :src="identity.avatar.url" />
</figure>
</div>
<div class="media-content">
{{ identity.displayName() }}
</div>
</router-link>
</li>
</ul>
<router-link
:to="{ name: 'CreateIdentity' }"
class="button create-identity is-primary"
>
{{ $t("Create a new identity") }}
</router-link>
</section>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { IDENTITIES } from "../../graphql/actor";
import { IPerson, Person } from "../../types/actor";
@Component({
apollo: {
identities: {
query: IDENTITIES,
update(result) {
return result.identities.map((i: IPerson) => new Person(i));
},
},
},
})
export default class Identities extends Vue {
@Prop({ type: String }) currentIdentityName!: string;
identities: Person[] = [];
errors: string[] = [];
isCurrentIdentity(identity: IPerson): boolean {
return identity.preferredUsername === this.currentIdentityName;
}
}
</script>
<style lang="scss" scoped>
.identities {
border-right: 1px solid grey;
padding: 15px 0;
}
.media.identity {
align-items: center;
font-size: 1.3rem;
padding-bottom: 0;
margin-bottom: 15px;
color: #000;
&.is-current-identity {
background-color: rgba(0, 0, 0, 0.1);
}
}
.title {
margin-bottom: 30px;
}
</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://framapiaf.s3.framasoft.org/framapiaf/accounts/avatars/000/000/399/original/aa56a445efb72803.jpg",
},
};
function setupApp({ app }) {
app.use(FloatingVue);
}
</script>

View File

@@ -1,44 +1,38 @@
<template>
<v-popover
offset="16"
trigger="hover"
<VMenu
:distance="16"
:triggers="['hover']"
class="popover"
:class="{ inline, clickable: actor && actor.type === ActorType.GROUP }"
>
<slot></slot>
<template slot="popover">
<template #popper>
<actor-card :full="true" :actor="actor" :popover="true" />
</template>
</v-popover>
</VMenu>
</template>
<script lang="ts">
<script lang="ts" setup>
import { ActorType } from "@/types/enums";
import { Component, Vue, Prop } from "vue-property-decorator";
import { IActor } from "../../types/actor";
import ActorCard from "./ActorCard.vue";
@Component({
components: {
ActorCard,
},
})
export default class PopoverActorCard extends Vue {
@Prop({ required: true, type: Object }) actor!: IActor;
@Prop({ required: false, type: Boolean, default: false }) inline!: boolean;
ActorType = ActorType;
}
withDefaults(
defineProps<{
actor: IActor;
inline?: boolean;
}>(),
{
inline: false,
}
);
</script>
<style lang="scss" scoped>
.inline {
display: inline;
<style lang="scss">
.v-popper__inner {
padding: 0 !important;
background-color: transparent !important;
}
.popover {
cursor: default;
}
.clickable {
cursor: pointer;
.v-popper__arrow-outer {
border-color: $violet-1 !important;
}
</style>

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

@@ -1,71 +1,60 @@
<template>
<div class="section container">
<div class="setting-title">
<h2>{{ $t("Profiles and federation") }}</h2>
</div>
<div>
<p class="content">
{{
$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="content">
<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>
<span
v-if="config"
v-html="
$t(
'This instance, <b>{instanceName} ({domain})</b>, hosts your profile, so remember its name.',
{
domain,
instanceName: config.name,
}
)
"
/>
</p>
<hr role="presentation" />
<p class="content">
{{
$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="has-text-centered">
<code>{{ `${currentActor.preferredUsername}@${domain}` }}</code>
</div>
</div>
<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 v-slot: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">
import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
import { CONFIG } from "@/graphql/config";
<script lang="ts" setup>
import { IPerson } from "@/types/actor";
import { IConfig } from "@/types/config.model";
import { Component, Vue } from "vue-property-decorator";
import { computed } from "vue";
import { useI18n } from "vue-i18n";
@Component({
apollo: {
config: CONFIG,
currentActor: CURRENT_ACTOR_CLIENT,
},
})
export default class ProfileOnboarding extends Vue {
config!: IConfig;
defineProps<{
currentActor: IPerson;
instanceName: string;
}>();
currentActor!: IPerson;
const { t } = useI18n({ useScope: "global" });
domain = window.location.hostname;
}
const domain = computed(() => window.location.hostname);
</script>