Improve post & events cards, homepage and my events page
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -119,9 +119,11 @@
|
||||
}}</b-button>
|
||||
</span>
|
||||
<span class="navbar-item" v-if="this.isUpdate">
|
||||
<b-button type="is-danger is-outlined" @click="deletePost">{{
|
||||
$t("Delete post")
|
||||
}}</b-button>
|
||||
<b-button
|
||||
type="is-danger is-outlined"
|
||||
@click="openDeletePostModal"
|
||||
>{{ $t("Delete post") }}</b-button
|
||||
>
|
||||
</span>
|
||||
<!-- If an post has been published we can't make it draft anymore -->
|
||||
<span class="navbar-item" v-if="post.draft === true">
|
||||
@@ -167,12 +169,7 @@ import {
|
||||
import GroupMixin from "@/mixins/group";
|
||||
import { PostVisibility } from "@/types/enums";
|
||||
import { CONFIG } from "../../graphql/config";
|
||||
import {
|
||||
FETCH_POST,
|
||||
CREATE_POST,
|
||||
UPDATE_POST,
|
||||
DELETE_POST,
|
||||
} from "../../graphql/post";
|
||||
import { CREATE_POST, UPDATE_POST } from "../../graphql/post";
|
||||
|
||||
import { IPost } from "../../types/post.model";
|
||||
import Editor from "../../components/Editor.vue";
|
||||
@@ -183,6 +180,7 @@ import Subtitle from "../../components/Utils/Subtitle.vue";
|
||||
import PictureUpload from "../../components/PictureUpload.vue";
|
||||
import { PERSON_STATUS_GROUP } from "@/graphql/actor";
|
||||
import { FETCH_GROUP } from "@/graphql/group";
|
||||
import PostMixin from "../../mixins/post";
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
@@ -198,18 +196,6 @@ import { FETCH_GROUP } from "@/graphql/group";
|
||||
return !this.preferredUsername;
|
||||
},
|
||||
},
|
||||
post: {
|
||||
query: FETCH_POST,
|
||||
fetchPolicy: "cache-and-network",
|
||||
variables() {
|
||||
return {
|
||||
slug: this.slug,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
return !this.slug;
|
||||
},
|
||||
},
|
||||
person: {
|
||||
query: PERSON_STATUS_GROUP,
|
||||
fetchPolicy: "cache-and-network",
|
||||
@@ -242,7 +228,7 @@ import { FETCH_GROUP } from "@/graphql/group";
|
||||
};
|
||||
},
|
||||
})
|
||||
export default class EditPost extends mixins(GroupMixin) {
|
||||
export default class EditPost extends mixins(GroupMixin, PostMixin) {
|
||||
@Prop({ required: false, type: String }) slug: undefined | string;
|
||||
|
||||
@Prop({ required: false, type: String }) preferredUsername!: string;
|
||||
@@ -338,23 +324,6 @@ export default class EditPost extends mixins(GroupMixin) {
|
||||
}
|
||||
}
|
||||
|
||||
async deletePost(): Promise<void> {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: DELETE_POST,
|
||||
variables: {
|
||||
id: this.post.id,
|
||||
},
|
||||
});
|
||||
if (data && this.post.attributedTo) {
|
||||
this.$router.push({
|
||||
name: RouteName.POSTS,
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(this.post.attributedTo),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static transformMessage(message: string[] | string): string | undefined {
|
||||
if (Array.isArray(message) && message.length > 0) {
|
||||
return message[0];
|
||||
|
||||
@@ -49,10 +49,8 @@
|
||||
>
|
||||
</div>
|
||||
<div class="post-list">
|
||||
<post-element-item
|
||||
v-for="post in group.posts.elements"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
<multi-post-list-item
|
||||
:posts="group.posts.elements"
|
||||
:isCurrentActorMember="isCurrentActorMember"
|
||||
/>
|
||||
</div>
|
||||
@@ -88,7 +86,7 @@ import { Paginate } from "../../types/paginate";
|
||||
import { IPost } from "../../types/post.model";
|
||||
import { usernameWithDomain } from "../../types/actor";
|
||||
import RouteName from "../../router/name";
|
||||
import PostElementItem from "../../components/Post/PostElementItem.vue";
|
||||
import MultiPostListItem from "../../components/Post/MultiPostListItem.vue";
|
||||
|
||||
const POSTS_PAGE_LIMIT = 10;
|
||||
|
||||
@@ -124,7 +122,7 @@ const POSTS_PAGE_LIMIT = 10;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
PostElementItem,
|
||||
MultiPostListItem,
|
||||
},
|
||||
metaInfo() {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
@@ -132,7 +130,7 @@ const POSTS_PAGE_LIMIT = 10;
|
||||
const { group } = this;
|
||||
return {
|
||||
title: this.$t("{group} posts", {
|
||||
group: group.name || usernameWithDomain(group),
|
||||
group: group?.name || usernameWithDomain(group),
|
||||
}) as string,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -7,7 +7,16 @@
|
||||
<div class="heading-section">
|
||||
<div class="heading-wrapper">
|
||||
<div class="title-metadata">
|
||||
<h1 class="title">{{ post.title }}</h1>
|
||||
<div class="title-wrapper">
|
||||
<b-tag
|
||||
class="mr-2"
|
||||
type="is-warning"
|
||||
size="is-medium"
|
||||
v-if="post.draft"
|
||||
>{{ $t("Draft") }}</b-tag
|
||||
>
|
||||
<h1 class="title">{{ post.title }}</h1>
|
||||
</div>
|
||||
<p class="metadata">
|
||||
<router-link
|
||||
slot="author"
|
||||
@@ -49,7 +58,14 @@
|
||||
}}
|
||||
</span>
|
||||
<span
|
||||
v-if="post.visibility === PostVisibility.PRIVATE"
|
||||
v-if="post.visibility === PostVisibility.UNLISTED"
|
||||
class="has-text-grey-dark"
|
||||
>
|
||||
<b-icon icon="link" size="is-small" />
|
||||
{{ $t("Accessible only by link") }}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="post.visibility === PostVisibility.PRIVATE"
|
||||
class="has-text-grey-dark"
|
||||
>
|
||||
<b-icon icon="lock" size="is-small" />
|
||||
@@ -61,21 +77,72 @@
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="buttons" v-if="isCurrentActorMember">
|
||||
<b-tag type="is-warning" size="is-medium" v-if="post.draft">{{
|
||||
$t("Draft")
|
||||
}}</b-tag>
|
||||
<router-link
|
||||
<b-dropdown position="is-bottom-left" aria-role="list">
|
||||
<b-button slot="trigger" role="button" icon-right="dots-horizontal">
|
||||
{{ $t("Actions") }}
|
||||
</b-button>
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
has-link
|
||||
v-if="
|
||||
currentActor.id === post.author.id ||
|
||||
isCurrentActorAGroupModerator
|
||||
"
|
||||
:to="{ name: RouteName.POST_EDIT, params: { slug: post.slug } }"
|
||||
tag="button"
|
||||
class="button is-text"
|
||||
>{{ $t("Edit") }}</router-link
|
||||
>
|
||||
</p>
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.POST_EDIT,
|
||||
params: { slug: post.slug },
|
||||
}"
|
||||
>{{ $t("Edit") }} <b-icon icon="pencil"
|
||||
/></router-link>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
v-if="
|
||||
currentActor.id === post.author.id ||
|
||||
isCurrentActorAGroupModerator
|
||||
"
|
||||
@click="openDeletePostModal"
|
||||
@keyup.enter="openDeletePostModal"
|
||||
>
|
||||
{{ $t("Delete") }}
|
||||
<b-icon icon="delete" />
|
||||
</b-dropdown-item>
|
||||
|
||||
<hr
|
||||
role="presentation"
|
||||
class="dropdown-divider"
|
||||
aria-role="menuitem"
|
||||
v-if="
|
||||
currentActor.id === post.author.id ||
|
||||
isCurrentActorAGroupModerator
|
||||
"
|
||||
/>
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
v-if="!post.draft"
|
||||
@click="triggerShare()"
|
||||
@keyup.enter="triggerShare()"
|
||||
>
|
||||
<span>
|
||||
{{ $t("Share this event") }}
|
||||
<b-icon icon="share" />
|
||||
</span>
|
||||
</b-dropdown-item>
|
||||
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
v-if="ableToReport"
|
||||
@click="isReportModalActive = true"
|
||||
@keyup.enter="isReportModalActive = true"
|
||||
>
|
||||
<span>
|
||||
{{ $t("Report") }}
|
||||
<b-icon icon="flag" />
|
||||
</span>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -108,6 +175,21 @@
|
||||
<tag>{{ tag.title }}</tag>
|
||||
</router-link>
|
||||
</section>
|
||||
<b-modal
|
||||
:active.sync="isReportModalActive"
|
||||
has-modal-card
|
||||
ref="reportModal"
|
||||
>
|
||||
<report-modal
|
||||
:on-confirm="reportPost"
|
||||
:title="$t('Report this post')"
|
||||
:outside-domain="groupDomain"
|
||||
@close="$refs.reportModal.close()"
|
||||
/>
|
||||
</b-modal>
|
||||
<b-modal :active.sync="isShareModalActive" has-modal-card ref="shareModal">
|
||||
<share-post-modal :post="post" />
|
||||
</b-modal>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
@@ -122,8 +204,6 @@ import {
|
||||
PERSON_MEMBERSHIPS,
|
||||
PERSON_STATUS_GROUP,
|
||||
} from "../../graphql/actor";
|
||||
import { FETCH_POST } from "../../graphql/post";
|
||||
import { IPost } from "../../types/post.model";
|
||||
import { usernameWithDomain } from "../../types/actor";
|
||||
import RouteName from "../../router/name";
|
||||
import Tag from "../../components/Tag.vue";
|
||||
@@ -132,9 +212,17 @@ import ActorInline from "../../components/Account/ActorInline.vue";
|
||||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
import { CURRENT_USER_CLIENT } from "@/graphql/user";
|
||||
import { ICurrentUser } from "@/types/current-user.model";
|
||||
import { CONFIG } from "@/graphql/config";
|
||||
import { IConfig } from "@/types/config.model";
|
||||
import SharePostModal from "../../components/Post/SharePostModal.vue";
|
||||
import { IReport } from "@/types/report.model";
|
||||
import { CREATE_REPORT } from "@/graphql/report";
|
||||
import ReportModal from "../../components/Report/ReportModal.vue";
|
||||
import PostMixin from "../../mixins/post";
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
config: CONFIG,
|
||||
currentUser: CURRENT_USER_CLIENT,
|
||||
currentActor: CURRENT_ACTOR_CLIENT,
|
||||
memberships: {
|
||||
@@ -150,21 +238,6 @@ import { ICurrentUser } from "@/types/current-user.model";
|
||||
return !this.currentActor || !this.currentActor.id;
|
||||
},
|
||||
},
|
||||
post: {
|
||||
query: FETCH_POST,
|
||||
fetchPolicy: "cache-and-network",
|
||||
variables() {
|
||||
return {
|
||||
slug: this.slug,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
return !this.slug;
|
||||
},
|
||||
error({ graphQLErrors }) {
|
||||
this.handleErrors(graphQLErrors);
|
||||
},
|
||||
},
|
||||
person: {
|
||||
query: PERSON_STATUS_GROUP,
|
||||
fetchPolicy: "cache-and-network",
|
||||
@@ -187,6 +260,8 @@ import { ICurrentUser } from "@/types/current-user.model";
|
||||
Tag,
|
||||
LazyImageWrapper,
|
||||
ActorInline,
|
||||
SharePostModal,
|
||||
ReportModal,
|
||||
},
|
||||
metaInfo() {
|
||||
return {
|
||||
@@ -200,13 +275,13 @@ import { ICurrentUser } from "@/types/current-user.model";
|
||||
};
|
||||
},
|
||||
})
|
||||
export default class Post extends mixins(GroupMixin) {
|
||||
export default class Post extends mixins(GroupMixin, PostMixin) {
|
||||
@Prop({ required: true, type: String }) slug!: string;
|
||||
|
||||
post!: IPost;
|
||||
|
||||
memberships!: IMember[];
|
||||
|
||||
config!: IConfig;
|
||||
|
||||
RouteName = RouteName;
|
||||
|
||||
currentUser!: ICurrentUser;
|
||||
@@ -217,11 +292,9 @@ export default class Post extends mixins(GroupMixin) {
|
||||
|
||||
PostVisibility = PostVisibility;
|
||||
|
||||
handleErrors(errors: any[]): void {
|
||||
if (errors.some((error) => error.status_code === 404)) {
|
||||
this.$router.replace({ name: RouteName.PAGE_NOT_FOUND });
|
||||
}
|
||||
}
|
||||
isShareModalActive = false;
|
||||
|
||||
isReportModalActive = false;
|
||||
|
||||
get isCurrentActorMember(): boolean {
|
||||
if (!this.post.attributedTo || !this.memberships) return false;
|
||||
@@ -236,6 +309,62 @@ export default class Post extends mixins(GroupMixin) {
|
||||
ICurrentUserRole.MODERATOR,
|
||||
].includes(this.currentUser.role);
|
||||
}
|
||||
|
||||
get ableToReport(): boolean {
|
||||
return (
|
||||
this.config &&
|
||||
(this.currentActor.id != null || this.config.anonymous.reports.allowed)
|
||||
);
|
||||
}
|
||||
|
||||
triggerShare(): void {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-start
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
.share({
|
||||
title: this.post.title,
|
||||
url: this.post.url,
|
||||
})
|
||||
.then(() => console.log("Successful share"))
|
||||
.catch((error: any) => console.log("Error sharing", error));
|
||||
} else {
|
||||
this.isShareModalActive = true;
|
||||
// send popup
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-end
|
||||
}
|
||||
|
||||
async reportPost(content: string, forward: boolean): Promise<void> {
|
||||
this.isReportModalActive = false;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.$refs.reportModal.close();
|
||||
const postTitle = this.post.title;
|
||||
|
||||
try {
|
||||
await this.$apollo.mutate<IReport>({
|
||||
mutation: CREATE_REPORT,
|
||||
variables: {
|
||||
postId: this.post.id,
|
||||
reportedId: this.post.attributedTo?.id,
|
||||
content,
|
||||
forward,
|
||||
},
|
||||
});
|
||||
this.$notifier.success(
|
||||
this.$t("Post {eventTitle} reported", { postTitle }) as string
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
get groupDomain(): string | undefined | null {
|
||||
return this.post.attributedTo?.domain;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -261,16 +390,31 @@ article.post {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.title-metadata {
|
||||
min-width: 300px;
|
||||
flex: 20;
|
||||
|
||||
.title-wrapper {
|
||||
display: inline;
|
||||
|
||||
.tag {
|
||||
height: 38px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
& > h1 {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
p.metadata {
|
||||
margin-top: 16px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
|
||||
*:not(:first-child) {
|
||||
padding-left: 5px;
|
||||
@@ -328,5 +472,14 @@ article.post {
|
||||
}
|
||||
|
||||
margin: 0 auto;
|
||||
|
||||
a.dropdown-item,
|
||||
.dropdown .dropdown-menu .has-link a,
|
||||
button.dropdown-item {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
padding-right: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user