Add comments under events to activities

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-03-10 17:38:13 +01:00
parent 58ee8e679b
commit 1f926902aa
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
14 changed files with 105 additions and 15 deletions

View File

@ -35,7 +35,10 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { usernameWithDomain } from "@/types/actor"; import { usernameWithDomain } from "@/types/actor";
import { ActivityEventSubject } from "@/types/enums"; import {
ActivityEventCommentSubject,
ActivityEventSubject,
} from "@/types/enums";
import { mixins } from "vue-class-component"; import { mixins } from "vue-class-component";
import { Component } from "vue-property-decorator"; import { Component } from "vue-property-decorator";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
@ -69,6 +72,17 @@ export default class EventActivityItem extends mixins(ActivityMixin) {
return "You deleted the event {event}."; return "You deleted the event {event}.";
} }
return "The event {event} was deleted by {profile}."; return "The event {event} was deleted by {profile}.";
case ActivityEventCommentSubject.COMMENT_POSTED:
if (this.subjectParams.comment_reply_to) {
if (this.isAuthorCurrentActor) {
return "You replied to a comment on the event {event}.";
}
return "{profile} replied to a comment on the event {event}.";
}
if (this.isAuthorCurrentActor) {
return "You posted a comment on the event {event}.";
}
return "{profile} posted a comment on the event {event}.";
default: default:
return undefined; return undefined;
} }
@ -77,6 +91,7 @@ export default class EventActivityItem extends mixins(ActivityMixin) {
get iconColor(): string | undefined { get iconColor(): string | undefined {
switch (this.activity.subject) { switch (this.activity.subject) {
case ActivityEventSubject.EVENT_CREATED: case ActivityEventSubject.EVENT_CREATED:
case ActivityEventCommentSubject.COMMENT_POSTED:
return "is-success"; return "is-success";
case ActivityEventSubject.EVENT_UPDATED: case ActivityEventSubject.EVENT_UPDATED:
return "is-grey"; return "is-grey";

View File

@ -97,7 +97,7 @@
<span <span
style="cursor: pointer" style="cursor: pointer"
class="level-item reply-btn" class="level-item reply-btn"
@click="createReplyToComment(comment)" @click="createReplyToComment()"
> >
<span class="icon is-small"> <span class="icon is-small">
<b-icon icon="reply" /> <b-icon icon="reply" />
@ -235,17 +235,13 @@ export default class Comment extends Vue {
} }
} }
async createReplyToComment(comment: IComment): Promise<void> { async createReplyToComment(): Promise<void> {
if (this.replyTo) { if (this.replyTo) {
this.replyTo = false; this.replyTo = false;
this.newComment = new CommentModel(); this.newComment = new CommentModel();
return; return;
} }
this.replyTo = true; this.replyTo = true;
// this.newComment.inReplyToComment = comment;
await this.$nextTick();
await this.$nextTick(); // For some reason commenteditor needs two $nextTick() to fully render
this.commentEditor.replyToComment(comment);
} }
replyToComment(): void { replyToComment(): void {
@ -303,7 +299,7 @@ export default class Comment extends Vue {
get commentId(): string { get commentId(): string {
if (this.comment.originComment) if (this.comment.originComment)
return `#comment-${this.comment.originComment.uuid}:${this.comment.uuid}`; return `#comment-${this.comment.originComment.uuid}-${this.comment.uuid}`;
return `#comment-${this.comment.uuid}`; return `#comment-${this.comment.uuid}`;
} }

View File

@ -393,6 +393,9 @@ export const GROUP_TIMELINE = gql`
title title
slug slug
} }
... on Comment {
id
}
... on Group { ... on Group {
id id
preferredUsername preferredUsername

View File

@ -963,5 +963,9 @@
"Delete discussion": "Delete discussion", "Delete discussion": "Delete discussion",
"All activities": "All activities", "All activities": "All activities",
"From yourself": "From yourself", "From yourself": "From yourself",
"By others": "By others" "By others": "By others",
"You posted a comment on the event {event}.": "You posted a comment on the event {event}.",
"{profile} posted a comment on the event {event}.": "{profile} posted a comment on the event {event}.",
"You replied to a comment on the event {event}.": "You replied to a comment on the event {event}.",
"{profile} replied to a comment on the event {event}.": "{profile} replied to a comment on the event {event}."
} }

View File

@ -1057,5 +1057,9 @@
"Delete discussion": "Supprimer la discussion", "Delete discussion": "Supprimer la discussion",
"All activities": "Toutes les activités", "All activities": "Toutes les activités",
"From yourself": "De vous", "From yourself": "De vous",
"By others": "Des autres" "By others": "Des autres",
"You posted a comment on the event {event}.": "Vous avez posté un commentaire sur l'événement {event}.",
"{profile} posted a comment on the event {event}.": "{profile} a posté un commentaire sur l'événement {event}.",
"You replied to a comment on the event {event}.": "Vous avez répondu à un commentaire sur l'événement {event}.",
"{profile} replied to a comment on the event {event}.": "{profile} a répondu à un commentaire sur l'événement {event}."
} }

View File

@ -2,6 +2,7 @@ import { IActor, IGroup } from "./actor";
import { IMember } from "./actor/member.model"; import { IMember } from "./actor/member.model";
import { import {
ActivityDiscussionSubject, ActivityDiscussionSubject,
ActivityEventCommentSubject,
ActivityEventSubject, ActivityEventSubject,
ActivityGroupSubject, ActivityGroupSubject,
ActivityMemberSubject, ActivityMemberSubject,
@ -19,7 +20,8 @@ export type ActivitySubject =
| ActivityMemberSubject | ActivityMemberSubject
| ActivityResourceSubject | ActivityResourceSubject
| ActivityDiscussionSubject | ActivityDiscussionSubject
| ActivityGroupSubject; | ActivityGroupSubject
| ActivityEventCommentSubject;
export interface IActivity { export interface IActivity {
id: string; id: string;

View File

@ -197,6 +197,10 @@ export enum ActivityEventSubject {
EVENT_DELETED = "event_deleted", EVENT_DELETED = "event_deleted",
} }
export enum ActivityEventCommentSubject {
COMMENT_POSTED = "comment_posted",
}
export enum ActivityPostSubject { export enum ActivityPostSubject {
POST_CREATED = "post_created", POST_CREATED = "post_created",
POST_UPDATED = "post_updated", POST_UPDATED = "post_updated",

View File

@ -168,7 +168,6 @@ import { IActivity } from "../../types/activity.model";
import Observer from "../../components/Utils/Observer.vue"; import Observer from "../../components/Utils/Observer.vue";
import SkeletonActivityItem from "../../components/Activity/SkeletonActivityItem.vue"; import SkeletonActivityItem from "../../components/Activity/SkeletonActivityItem.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { Location } from "vue-router";
const PAGINATION_LIMIT = 25; const PAGINATION_LIMIT = 25;
const SKELETON_DAY_ITEMS = 2; const SKELETON_DAY_ITEMS = 2;

View File

@ -9,6 +9,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.GraphQL.API.Utils, as: APIUtils alias Mobilizon.GraphQL.API.Utils, as: APIUtils
alias Mobilizon.Service.Activity.Comment, as: CommentActivity
alias Mobilizon.Share alias Mobilizon.Share
alias Mobilizon.Tombstone alias Mobilizon.Tombstone
alias Mobilizon.Web.Endpoint alias Mobilizon.Web.Endpoint
@ -24,6 +25,10 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
:ok <- make_sure_event_allows_commenting(args), :ok <- make_sure_event_allows_commenting(args),
{:ok, %Comment{discussion_id: discussion_id} = comment} <- {:ok, %Comment{discussion_id: discussion_id} = comment} <-
Discussions.create_comment(args), Discussions.create_comment(args),
{:ok, _} <-
CommentActivity.insert_activity(comment,
subject: "comment_posted"
),
:ok <- maybe_publish_graphql_subscription(discussion_id), :ok <- maybe_publish_graphql_subscription(discussion_id),
comment_as_data <- Convertible.model_to_as(comment), comment_as_data <- Convertible.model_to_as(comment),
audience <- audience <-

View File

@ -78,6 +78,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Activity do
Actors.get_actor(group_id) Actors.get_actor(group_id)
end end
defp get_object(:comment, comment_id) do
Discussions.get_comment(comment_id)
end
@spec transform_params(map()) :: list() @spec transform_params(map()) :: list()
defp transform_params(params) do defp transform_params(params) do
Enum.map(params, fn {key, value} -> %{key: key, value: transform_value(value)} end) Enum.map(params, fn {key, value} -> %{key: key, value: transform_value(value)} end)

View File

@ -5,7 +5,7 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do
use Absinthe.Schema.Notation use Absinthe.Schema.Notation
alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Discussions.Discussion alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.Event alias Mobilizon.Events.Event
alias Mobilizon.Posts.Post alias Mobilizon.Posts.Post
alias Mobilizon.Resources.Resource alias Mobilizon.Resources.Resource
@ -51,6 +51,9 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do
%Discussion{}, _ -> %Discussion{}, _ ->
:discussion :discussion
%Comment{}, _ ->
:comment
%Actor{type: :Group}, _ -> %Actor{type: :Group}, _ ->
:group :group

View File

@ -11,7 +11,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
@desc "A comment" @desc "A comment"
object :comment do object :comment do
interfaces([:action_log_object]) interfaces([:action_log_object, :activity_object])
field(:id, :id, description: "Internal ID for this comment") field(:id, :id, description: "Internal ID for this comment")
field(:uuid, :uuid, description: "An UUID for this comment") field(:uuid, :uuid, description: "An UUID for this comment")
field(:url, :string, description: "Comment URL") field(:url, :string, description: "Comment URL")

View File

@ -53,7 +53,7 @@ defmodule Mobilizon.Activities do
@resource_activity_subjects ++ @resource_activity_subjects ++
@member_activity_subjects ++ @settings_activity_subjects @member_activity_subjects ++ @settings_activity_subjects
@object_type ["event", "actor", "post", "discussion", "resource", "member", "group"] @object_type ["event", "actor", "post", "discussion", "resource", "member", "group", "comment"]
defenum(Type, @activity_types) defenum(Type, @activity_types)
defenum(Subject, @subjects) defenum(Subject, @subjects)

View File

@ -0,0 +1,51 @@
defmodule Mobilizon.Service.Activity.Comment do
@moduledoc """
Insert a comment activity
"""
alias Mobilizon.{Actors, Events}
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Service.Activity
alias Mobilizon.Service.Workers.ActivityBuilder
@behaviour Activity
@impl Activity
def insert_activity(comment, options \\ [])
def insert_activity(
%Comment{
actor_id: actor_id,
event_id: event_id,
in_reply_to_comment_id: in_reply_to_comment_id
} = comment,
options
)
when not is_nil(actor_id) and not is_nil(event_id) do
with {:ok, %Event{attributed_to: %Actor{type: :Group} = group} = event} <-
Events.get_event_with_preload(event_id),
%Actor{id: actor_id} <- Actors.get_actor(actor_id),
subject <- Keyword.fetch!(options, :subject) do
ActivityBuilder.enqueue(:build_activity, %{
"type" => "event",
"subject" => subject,
"subject_params" => %{
event_title: event.title,
event_uuid: event.uuid,
comment_reply_to: !is_nil(in_reply_to_comment_id)
},
"group_id" => group.id,
"author_id" => actor_id,
"object_type" => "comment",
"object_id" => to_string(comment.id),
"inserted_at" => DateTime.utc_now()
})
else
# Event not from group
{:ok, %Event{}} -> {:ok, nil}
end
end
def insert_activity(_, _), do: {:ok, nil}
end