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,124 @@
<template>
<div class="activity-item">
<o-icon :icon="'chat'" :variant="iconColor" custom-size="24" />
<div class="mt-1 ml-2 prose dark:prose-invert prose-p:m-0">
<i18n-t :keypath="translation" tag="p">
<template #discussion>
<router-link
v-if="activity.object"
:to="{
name: RouteName.DISCUSSION,
params: { slug: subjectParams.discussion_slug },
}"
>{{ subjectParams.discussion_title }}</router-link
>
<b v-else>{{ subjectParams.discussion_title }}</b>
</template>
<template #old_discussion>
<router-link
v-if="activity.object && subjectParams.old_discussion_title"
:to="{
name: RouteName.DISCUSSION,
params: { slug: subjectParams.discussion_slug },
}"
>{{ subjectParams.old_discussion_title }}</router-link
>
<b v-else-if="subjectParams.old_discussion_title">{{
subjectParams.old_discussion_title
}}</b>
</template>
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b>
{{
$t("{'@'}{username}", {
username: usernameWithDomain(activity.author),
})
}}</b
></popover-actor-card
></template
></i18n-t
>
<small class="has-text-grey-dark activity-date">{{
formatTimeString(activity.insertedAt)
}}</small>
</div>
</div>
</template>
<script lang="ts" setup>
import { usernameWithDomain } from "@/types/actor";
import { ActivityDiscussionSubject } from "@/types/enums";
import RouteName from "../../router/name";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
import { IActivity } from "@/types/activity.model";
import { computed } from "vue";
import { formatTimeString } from "@/filters/datetime";
import {
useActivitySubjectParams,
useIsActivityAuthorCurrentActor,
} from "@/composition/activity";
const props = defineProps<{
activity: IActivity;
}>();
const useIsActivityAuthorCurrentActorFct = useIsActivityAuthorCurrentActor();
const useActivitySubjectParamsFct = useActivitySubjectParams();
const isAuthorCurrentActor = computed(() =>
useIsActivityAuthorCurrentActorFct(props.activity)
);
const subjectParams = computed(() =>
useActivitySubjectParamsFct(props.activity)
);
const translation = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityDiscussionSubject.DISCUSSION_CREATED:
if (isAuthorCurrentActor.value) {
return "You created the discussion {discussion}.";
}
return "{profile} created the discussion {discussion}.";
case ActivityDiscussionSubject.DISCUSSION_REPLIED:
if (isAuthorCurrentActor.value) {
return "You replied to the discussion {discussion}.";
}
return "{profile} replied to the discussion {discussion}.";
case ActivityDiscussionSubject.DISCUSSION_RENAMED:
if (isAuthorCurrentActor.value) {
return "You renamed the discussion from {old_discussion} to {discussion}.";
}
return "{profile} renamed the discussion from {old_discussion} to {discussion}.";
case ActivityDiscussionSubject.DISCUSSION_ARCHIVED:
if (isAuthorCurrentActor.value) {
return "You archived the discussion {discussion}.";
}
return "{profile} archived the discussion {discussion}.";
case ActivityDiscussionSubject.DISCUSSION_DELETED:
if (isAuthorCurrentActor.value) {
return "You deleted the discussion {discussion}.";
}
return "{profile} deleted the discussion {discussion}.";
default:
return undefined;
}
});
const iconColor = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityDiscussionSubject.DISCUSSION_CREATED:
case ActivityDiscussionSubject.DISCUSSION_REPLIED:
return "success";
case ActivityDiscussionSubject.DISCUSSION_RENAMED:
case ActivityDiscussionSubject.DISCUSSION_ARCHIVED:
return "grey";
case ActivityDiscussionSubject.DISCUSSION_DELETED:
return "danger";
default:
return undefined;
}
});
</script>
<style lang="scss" scoped>
@import "./activity.scss";
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="activity-item">
<o-icon :icon="'calendar'" :variant="iconColor" custom-size="24" />
<div class="mt-1 ml-2 prose dark:prose-invert prose-p:m-0">
<i18n-t :keypath="translation" tag="p">
<template #event>
<router-link
v-if="activity.object"
:to="{
name: RouteName.EVENT,
params: { uuid: subjectParams.event_uuid },
}"
>{{ subjectParams.event_title }}</router-link
>
<b v-else>{{ subjectParams.event_title }}</b>
</template>
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b>
{{
$t("{'@'}{username}", {
username: usernameWithDomain(activity.author),
})
}}</b
></popover-actor-card
></template
></i18n-t
>
<small class="activity-date">{{
formatTimeString(activity.insertedAt)
}}</small>
</div>
</div>
</template>
<script lang="ts" setup>
import {
useActivitySubjectParams,
useIsActivityAuthorCurrentActor,
} from "@/composition/activity";
import { IActivity } from "@/types/activity.model";
import { usernameWithDomain } from "@/types/actor";
import { formatTimeString } from "@/filters/datetime";
import {
ActivityEventCommentSubject,
ActivityEventParticipantSubject,
ActivityEventSubject,
} from "@/types/enums";
import { computed } from "vue";
import RouteName from "../../router/name";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
const props = defineProps<{
activity: IActivity;
}>();
const useIsActivityAuthorCurrentActorFct = useIsActivityAuthorCurrentActor();
const useActivitySubjectParamsFct = useActivitySubjectParams();
const isAuthorCurrentActor = computed(() =>
useIsActivityAuthorCurrentActorFct(props.activity)
);
const subjectParams = computed(() =>
useActivitySubjectParamsFct(props.activity)
);
const translation = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityEventSubject.EVENT_CREATED:
if (isAuthorCurrentActor.value) {
return "You created the event {event}.";
}
return "The event {event} was created by {profile}.";
case ActivityEventSubject.EVENT_UPDATED:
if (isAuthorCurrentActor.value) {
return "You updated the event {event}.";
}
return "The event {event} was updated by {profile}.";
case ActivityEventSubject.EVENT_DELETED:
if (isAuthorCurrentActor.value) {
return "You deleted the event {event}.";
}
return "The event {event} was deleted by {profile}.";
case ActivityEventCommentSubject.COMMENT_POSTED:
if (subjectParams.value.comment_reply_to) {
if (isAuthorCurrentActor.value) {
return "You replied to a comment on the event {event}.";
}
return "{profile} replied to a comment on the event {event}.";
}
if (isAuthorCurrentActor.value) {
return "You posted a comment on the event {event}.";
}
return "{profile} posted a comment on the event {event}.";
case ActivityEventParticipantSubject.EVENT_NEW_PARTICIPATION:
if (isAuthorCurrentActor.value) {
return "You joined the event {event}.";
}
if (props.activity.author.preferredUsername === "anonymous") {
return "An anonymous profile joined the event {event}.";
}
return "{profile} joined the the event {event}.";
default:
return undefined;
}
});
const iconColor = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityEventSubject.EVENT_CREATED:
case ActivityEventCommentSubject.COMMENT_POSTED:
return "success";
case ActivityEventSubject.EVENT_UPDATED:
return "grey";
case ActivityEventSubject.EVENT_DELETED:
return "danger";
default:
return undefined;
}
});
</script>
<style lang="scss" scoped>
@import "./activity.scss";
</style>

View File

@@ -0,0 +1,179 @@
<template>
<div class="activity-item">
<o-icon :icon="'cog'" :variant="iconColor" custom-size="24" />
<div class="mt-1 ml-2 prose dark:prose-invert prose-p:m-0">
<i18n-t :keypath="translation" tag="p">
<template #group>
<router-link
v-if="activity.object"
:to="{
name: RouteName.GROUP,
params: {
preferredUsername: subjectParams.group_federated_username,
},
}"
>{{ subjectParams.group_name }}</router-link
>
<b v-else>{{ subjectParams.group_name }}</b>
</template>
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b>
{{
$t("{'@'}{username}", {
username: usernameWithDomain(activity.author),
})
}}</b
></popover-actor-card
></template
></i18n-t
>
<i18n-t :keypath="detail" v-for="detail in details" :key="detail" tag="p">
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b>
{{
$t("{'@'}{username}", {
username: usernameWithDomain(activity.author),
})
}}</b
></popover-actor-card
>
</template>
<template #group>
<router-link
v-if="activity.object"
:to="{
name: RouteName.GROUP,
params: {
preferredUsername: usernameWithDomain(
activity.object as IActor
),
},
}"
>{{ subjectParams.group_name }}</router-link
>
<b v-else>{{ subjectParams.group_name }}</b>
</template>
<template #old_group_name>
<b v-if="subjectParams.old_group_name">{{
subjectParams.old_group_name
}}</b>
</template>
</i18n-t>
<small>{{ formatTimeString(activity.insertedAt) }}</small>
</div>
</div>
</template>
<script lang="ts" setup>
import {
useIsActivityAuthorCurrentActor,
useActivitySubjectParams,
} from "@/composition/activity";
import { IActivity } from "@/types/activity.model";
import { IActor, IGroup, usernameWithDomain } from "@/types/actor";
import { ActivityGroupSubject, GroupVisibility, Openness } from "@/types/enums";
import { computed } from "vue";
import RouteName from "../../router/name";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
import { formatTimeString } from "@/filters/datetime";
const props = defineProps<{
activity: IActivity;
}>();
const useIsActivityAuthorCurrentActorFct = useIsActivityAuthorCurrentActor();
const useActivitySubjectParamsFct = useActivitySubjectParams();
const isAuthorCurrentActor = computed(() =>
useIsActivityAuthorCurrentActorFct(props.activity)
);
const subjectParams = computed(() =>
useActivitySubjectParamsFct(props.activity)
);
const translation = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityGroupSubject.GROUP_CREATED:
if (isAuthorCurrentActor.value) {
return "You created the group {group}.";
}
return "{profile} created the group {group}.";
case ActivityGroupSubject.GROUP_UPDATED:
if (isAuthorCurrentActor.value) {
return "You updated the group {group}.";
}
return "{profile} updated the group {group}.";
default:
return undefined;
}
});
const iconColor = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityGroupSubject.GROUP_CREATED:
return "success";
case ActivityGroupSubject.GROUP_UPDATED:
return "grey";
default:
return undefined;
}
});
const group = computed(() => props.activity.object as IGroup);
const details = computed((): string[] => {
const localDetails = [];
const changes = subjectParams.value.group_changes.split(",");
if (changes.includes("name") && subjectParams.value.old_group_name) {
localDetails.push("{old_group_name} was renamed to {group}.");
}
if (changes.includes("visibility") && group.value.visibility) {
switch (group.value.visibility) {
case GroupVisibility.PRIVATE:
localDetails.push("Visibility was set to private.");
break;
case GroupVisibility.PUBLIC:
localDetails.push("Visibility was set to public.");
break;
default:
localDetails.push("Visibility was set to an unknown value.");
break;
}
}
if (changes.includes("openness") && group.value.openness) {
switch (group.value.openness) {
case Openness.INVITE_ONLY:
localDetails.push("The group can now only be joined with an invite.");
break;
case Openness.MODERATED:
localDetails.push(
"The group can now be joined by anyone, but new members need to be approved by an administrator."
);
break;
case Openness.OPEN:
localDetails.push("The group can now be joined by anyone.");
break;
default:
localDetails.push("Unknown value for the openness setting.");
break;
}
}
if (changes.includes("address") && group.value.physicalAddress) {
localDetails.push("The group's physical address was changed.");
}
if (changes.includes("avatar") && group.value.avatar) {
localDetails.push("The group's avatar was changed.");
}
if (changes.includes("banner") && group.value.banner) {
localDetails.push("The group's banner was changed.");
}
if (changes.includes("summary") && group.value.summary) {
localDetails.push("The group's short description was changed.");
}
return localDetails;
});
</script>
<style lang="scss" scoped>
@import "./activity.scss";
</style>

View File

@@ -0,0 +1,238 @@
<template>
<div class="activity-item">
<o-icon :icon="icon" :variant="iconColor" custom-size="24" />
<div class="mt-1 ml-2 prose dark:prose-invert prose-p:m-0">
<i18n-t :keypath="translation" tag="p">
<template #member>
<popover-actor-card
v-if="member"
:actor="member.actor"
:inline="true"
>
<b> {{ displayName(member.actor) }}</b></popover-actor-card
>
<b v-else>{{ subjectParams.member_actor_federated_username }}</b>
</template>
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b> {{ displayName(activity.author) }}</b></popover-actor-card
></template
></i18n-t
>
<small class="activity-date">{{
formatTimeString(activity.insertedAt)
}}</small>
</div>
</div>
</template>
<script lang="ts">
export const MEMBER_ROLE_VALUE: Record<string, number> = {
[MemberRole.MEMBER]: 20,
[MemberRole.MODERATOR]: 50,
[MemberRole.ADMINISTRATOR]: 90,
[MemberRole.CREATOR]: 100,
};
</script>
<script lang="ts" setup>
import { displayName } from "@/types/actor";
import { ActivityMemberSubject, MemberRole } from "@/types/enums";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
import { formatTimeString } from "@/filters/datetime";
import {
useIsActivityAuthorCurrentActor,
useActivitySubjectParams,
useIsActivityObjectCurrentActor,
} from "@/composition/activity";
import { IActivity } from "@/types/activity.model";
import { computed } from "vue";
import { IMember } from "@/types/actor/member.model";
const props = defineProps<{
activity: IActivity;
}>();
const isActivityAuthorCurrentActorFct = useIsActivityAuthorCurrentActor();
const activitySubjectParamsFct = useActivitySubjectParams();
const isActivityObjectCurrentActor = useIsActivityObjectCurrentActor();
const isAuthorCurrentActor = computed(() =>
isActivityAuthorCurrentActorFct(props.activity)
);
const subjectParams = computed(() => activitySubjectParamsFct(props.activity));
const member = computed(() => props.activity.object as IMember);
const isObjectMemberCurrentActor = computed(() =>
isActivityObjectCurrentActor(props.activity)
);
const translation = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityMemberSubject.MEMBER_REQUEST:
if (isAuthorCurrentActor.value) {
return "You requested to join the group.";
}
return "{member} requested to join the group.";
case ActivityMemberSubject.MEMBER_INVITED:
if (isAuthorCurrentActor.value) {
return "You invited {member}.";
}
return "{member} was invited by {profile}.";
case ActivityMemberSubject.MEMBER_ADDED:
if (isAuthorCurrentActor.value) {
return "You added the member {member}.";
}
return "{profile} added the member {member}.";
case ActivityMemberSubject.MEMBER_APPROVED:
if (isAuthorCurrentActor.value) {
return "You approved {member}'s membership.";
}
if (isObjectMemberCurrentActor.value) {
return "Your membership was approved by {profile}.";
}
return "{profile} approved {member}'s membership.";
case ActivityMemberSubject.MEMBER_JOINED:
return "{member} joined the group.";
case ActivityMemberSubject.MEMBER_UPDATED:
if (subjectParams.value.member_role && subjectParams.value.old_role) {
return roleUpdate.value;
}
if (isAuthorCurrentActor.value) {
return "You updated the member {member}.";
}
return "{profile} updated the member {member}.";
case ActivityMemberSubject.MEMBER_REMOVED:
if (subjectParams.value.member_role === MemberRole.NOT_APPROVED) {
if (isAuthorCurrentActor.value) {
return "You rejected {member}'s membership request.";
}
return "{profile} rejected {member}'s membership request.";
}
if (isAuthorCurrentActor.value) {
return "You excluded member {member}.";
}
return "{profile} excluded member {member}.";
case ActivityMemberSubject.MEMBER_QUIT:
return "{profile} quit the group.";
case ActivityMemberSubject.MEMBER_REJECTED_INVITATION:
return "{member} rejected the invitation to join the group.";
case ActivityMemberSubject.MEMBER_ACCEPTED_INVITATION:
if (isAuthorCurrentActor.value) {
return "You accepted the invitation to join the group.";
}
return "{member} accepted the invitation to join the group.";
default:
return undefined;
}
});
const icon = computed((): string => {
switch (props.activity.subject) {
case ActivityMemberSubject.MEMBER_REQUEST:
case ActivityMemberSubject.MEMBER_ADDED:
case ActivityMemberSubject.MEMBER_INVITED:
case ActivityMemberSubject.MEMBER_ACCEPTED_INVITATION:
return "account-multiple-plus";
case ActivityMemberSubject.MEMBER_REMOVED:
case ActivityMemberSubject.MEMBER_REJECTED_INVITATION:
case ActivityMemberSubject.MEMBER_QUIT:
return "account-multiple-minus";
case ActivityMemberSubject.MEMBER_UPDATED:
default:
return "account-multiple";
}
});
const iconColor = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityMemberSubject.MEMBER_ADDED:
case ActivityMemberSubject.MEMBER_INVITED:
case ActivityMemberSubject.MEMBER_JOINED:
case ActivityMemberSubject.MEMBER_APPROVED:
case ActivityMemberSubject.MEMBER_ACCEPTED_INVITATION:
return "success";
case ActivityMemberSubject.MEMBER_REQUEST:
case ActivityMemberSubject.MEMBER_UPDATED:
return "grey";
case ActivityMemberSubject.MEMBER_REMOVED:
case ActivityMemberSubject.MEMBER_REJECTED_INVITATION:
case ActivityMemberSubject.MEMBER_QUIT:
return "danger";
default:
return undefined;
}
});
const roleUpdate = computed((): string | undefined => {
if (
Object.keys(MEMBER_ROLE_VALUE).includes(subjectParams.value.member_role) &&
Object.keys(MEMBER_ROLE_VALUE).includes(subjectParams.value.old_role)
) {
if (
MEMBER_ROLE_VALUE[subjectParams.value.member_role] >
MEMBER_ROLE_VALUE[subjectParams.value.old_role]
) {
switch (subjectParams.value.member_role) {
case MemberRole.MODERATOR:
if (isAuthorCurrentActor.value) {
return "You promoted {member} to moderator.";
}
if (isObjectMemberCurrentActor.value) {
return "You were promoted to moderator by {profile}.";
}
return "{profile} promoted {member} to moderator.";
case MemberRole.ADMINISTRATOR:
if (isAuthorCurrentActor.value) {
return "You promoted {member} to administrator.";
}
if (isObjectMemberCurrentActor.value) {
return "You were promoted to administrator by {profile}.";
}
return "{profile} promoted {member} to administrator.";
default:
if (isAuthorCurrentActor.value) {
return "You promoted the member {member} to an unknown role.";
}
if (isObjectMemberCurrentActor.value) {
return "You were promoted to an unknown role by {profile}.";
}
return "{profile} promoted {member} to an unknown role.";
}
} else {
switch (subjectParams.value.member_role) {
case MemberRole.MODERATOR:
if (isAuthorCurrentActor.value) {
return "You demoted {member} to moderator.";
}
if (isObjectMemberCurrentActor.value) {
return "You were demoted to moderator by {profile}.";
}
return "{profile} demoted {member} to moderator.";
case MemberRole.MEMBER:
if (isAuthorCurrentActor.value) {
return "You demoted {member} to simple member.";
}
if (isObjectMemberCurrentActor.value) {
return "You were demoted to simple member by {profile}.";
}
return "{profile} demoted {member} to simple member.";
default:
if (isAuthorCurrentActor.value) {
return "You demoted the member {member} to an unknown role.";
}
if (isObjectMemberCurrentActor.value) {
return "You were demoted to an unknown role by {profile}.";
}
return "{profile} demoted {member} to an unknown role.";
}
}
} else {
if (isAuthorCurrentActor.value) {
return "You updated the member {member}.";
}
return "{profile} updated the member {member}";
}
});
</script>
<style lang="scss" scoped>
@import "./activity.scss";
</style>

View File

@@ -0,0 +1,99 @@
<template>
<div class="activity-item">
<o-icon :icon="'bullhorn'" :variant="iconColor" custom-size="24" />
<div class="mt-1 ml-2 prose dark:prose-invert prose-p:m-0">
<i18n-t :keypath="translation" tag="p">
<template #post>
<router-link
v-if="activity.object"
:to="{
name: RouteName.POST,
params: { slug: subjectParams.post_slug },
}"
>{{ subjectParams.post_title }}</router-link
>
<b v-else>{{ subjectParams.post_title }}</b>
</template>
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b>
{{
$t("{'@'}{username}", {
username: usernameWithDomain(activity.author),
})
}}</b
></popover-actor-card
></template
></i18n-t
>
<small class="activity-date">{{
formatTimeString(activity.insertedAt)
}}</small>
</div>
</div>
</template>
<script lang="ts" setup>
import { usernameWithDomain } from "@/types/actor";
import { ActivityPostSubject } from "@/types/enums";
import RouteName from "../../router/name";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
import { formatTimeString } from "@/filters/datetime";
import {
useIsActivityAuthorCurrentActor,
useActivitySubjectParams,
} from "@/composition/activity";
import { IActivity } from "@/types/activity.model";
import { computed } from "vue";
const props = defineProps<{
activity: IActivity;
}>();
const useIsActivityAuthorCurrentActorFct = useIsActivityAuthorCurrentActor();
const useActivitySubjectParamsFct = useActivitySubjectParams();
const isAuthorCurrentActor = computed(() =>
useIsActivityAuthorCurrentActorFct(props.activity)
);
const subjectParams = computed(() =>
useActivitySubjectParamsFct(props.activity)
);
const translation = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityPostSubject.POST_CREATED:
if (isAuthorCurrentActor.value) {
return "You created the post {post}.";
}
return "The post {post} was created by {profile}.";
case ActivityPostSubject.POST_UPDATED:
if (isAuthorCurrentActor.value) {
return "You updated the post {post}.";
}
return "The post {post} was updated by {profile}.";
case ActivityPostSubject.POST_DELETED:
if (isAuthorCurrentActor.value) {
return "You deleted the post {post}.";
}
return "The post {post} was deleted by {profile}.";
default:
return undefined;
}
});
const iconColor = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityPostSubject.POST_CREATED:
return "success";
case ActivityPostSubject.POST_UPDATED:
return "grey";
case ActivityPostSubject.POST_DELETED:
return "danger";
default:
return undefined;
}
});
</script>
<style lang="scss" scoped>
@import "./activity.scss";
</style>

View File

@@ -0,0 +1,199 @@
<template>
<div class="activity-item">
<o-icon :icon="'link'" :variant="iconColor" custom-size="24" />
<div class="mt-1 ml-2 prose dark:prose-invert prose-p:m-0">
<i18n-t :keypath="translation" tag="p">
<template #resource>
<router-link v-if="activity.object" :to="path">{{
subjectParams.resource_title
}}</router-link>
<b v-else>{{ subjectParams.resource_title }}</b>
</template>
<template #new_path>
<router-link v-if="activity.object" :to="path">{{
parentDirectory
}}</router-link>
<b v-else>{{ parentDirectory }}</b>
</template>
<template #old_resource_title>
<router-link
v-if="activity.object && subjectParams.old_resource_title"
:to="path"
>{{ subjectParams.old_resource_title }}</router-link
>
<b v-else-if="subjectParams.old_resource_title">{{
subjectParams.old_resource_title
}}</b>
</template>
<template #profile>
<popover-actor-card :actor="activity.author" :inline="true">
<b>
{{
$t("{'@'}{username}", {
username: usernameWithDomain(activity.author),
})
}}</b
></popover-actor-card
></template
></i18n-t
>
<small class="activity-date">{{
formatTimeString(activity.insertedAt)
}}</small>
</div>
</div>
</template>
<script lang="ts" setup>
import { usernameWithDomain } from "@/types/actor";
import { ActivityResourceSubject } from "@/types/enums";
import RouteName from "../../router/name";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
import { formatTimeString } from "@/filters/datetime";
import {
useIsActivityAuthorCurrentActor,
useActivitySubjectParams,
} from "@/composition/activity";
import { IActivity } from "@/types/activity.model";
import { computed } from "vue";
import { IResource } from "@/types/resource";
const props = defineProps<{
activity: IActivity;
}>();
const useIsActivityAuthorCurrentActorFct = useIsActivityAuthorCurrentActor();
const useActivitySubjectParamsFct = useActivitySubjectParams();
const isAuthorCurrentActor = computed(() =>
useIsActivityAuthorCurrentActorFct(props.activity)
);
const subjectParams = computed(() =>
useActivitySubjectParamsFct(props.activity)
);
const resource = computed(() => props.activity.object as IResource);
const translation = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityResourceSubject.RESOURCE_CREATED:
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (props.activity?.object?.type === "folder") {
if (isAuthorCurrentActor.value) {
return "You created the folder {resource}.";
}
return "{profile} created the folder {resource}.";
}
if (isAuthorCurrentActor.value) {
return "You created the resource {resource}.";
}
return "{profile} created the resource {resource}.";
case ActivityResourceSubject.RESOURCE_MOVED:
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (props.activity?.object?.type === "folder") {
if (parentDirectory.value === null) {
if (isAuthorCurrentActor.value) {
return "You moved the folder {resource} to the root folder.";
}
return "{profile} moved the folder {resource} to the root folder.";
}
if (isAuthorCurrentActor.value) {
return "You moved the folder {resource} into {new_path}.";
}
return "{profile} moved the folder {resource} into {new_path}.";
}
if (parentDirectory.value === null) {
if (isAuthorCurrentActor.value) {
return "You moved the resource {resource} to the root folder.";
}
return "{profile} moved the resource {resource} to the root folder.";
}
if (isAuthorCurrentActor.value) {
return "You moved the resource {resource} into {new_path}.";
}
return "{profile} moved the resource {resource} into {new_path}.";
case ActivityResourceSubject.RESOURCE_UPDATED:
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (props.activity?.object?.type === "folder") {
if (isAuthorCurrentActor.value) {
return "You renamed the folder from {old_resource_title} to {resource}.";
}
return "{profile} renamed the folder from {old_resource_title} to {resource}.";
}
if (isAuthorCurrentActor.value) {
return "You renamed the resource from {old_resource_title} to {resource}.";
}
return "{profile} renamed the resource from {old_resource_title} to {resource}.";
case ActivityResourceSubject.RESOURCE_DELETED:
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (props.activity?.object?.type === "folder") {
if (isAuthorCurrentActor.value) {
return "You deleted the folder {resource}.";
}
return "{profile} deleted the folder {resource}.";
}
if (isAuthorCurrentActor.value) {
return "You deleted the resource {resource}.";
}
return "{profile} deleted the resource {resource}.";
default:
return undefined;
}
});
const iconColor = computed((): string | undefined => {
switch (props.activity.subject) {
case ActivityResourceSubject.RESOURCE_CREATED:
return "success";
case ActivityResourceSubject.RESOURCE_MOVED:
case ActivityResourceSubject.RESOURCE_UPDATED:
return "grey";
case ActivityResourceSubject.RESOURCE_DELETED:
return "danger";
default:
return undefined;
}
});
const path = computed(() => {
const localPath = parentPath(resource.value?.path);
if (localPath === "") {
return {
name: RouteName.RESOURCE_FOLDER_ROOT,
params: {
preferredUsername: usernameWithDomain(props.activity.group),
},
};
}
return {
name: RouteName.RESOURCE_FOLDER,
params: {
path: localPath,
preferredUsername: usernameWithDomain(props.activity.group),
},
};
});
const parentPath = (parent: string | undefined): string | undefined => {
if (!parent) return undefined;
const localPath = parent.split("/");
localPath.pop();
return localPath.join("/").replace(/^\//, "");
};
const parentDirectory = computed((): string | undefined | null => {
if (subjectParams.value.resource_path) {
const parentPathResult = parentPath(subjectParams.value.resource_path);
const directory = parentPathResult?.split("/");
const res = directory?.pop();
res === "" ? null : res;
}
return null;
});
</script>
<style lang="scss" scoped>
@import "./activity.scss";
</style>

View File

@@ -0,0 +1,30 @@
<template>
<div class="activity-item">
<span>
<o-skeleton circle width="32px" height="32px"></o-skeleton>
</span>
<div class="subject">
<div class="prose dark:prose-invert">
<p>
<o-skeleton active></o-skeleton>
<o-skeleton active class="datetime"></o-skeleton>
</p>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
@import "./activity.scss";
div.activity-item {
flex: 1;
.subject {
flex: 1;
max-width: 600px;
.content p > div:last-child {
max-width: 50px;
}
}
}
</style>

View File

@@ -0,0 +1,12 @@
.activity-item {
display: flex;
span.o-icon {
width: 2em;
height: 2em;
box-sizing: border-box;
border-radius: 50%;
border: 2px solid;
z-index: 2;
flex-shrink: 0;
}
}