fix(announcements): load group announcements
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
b635937091
commit
7ef85fe19b
|
@ -3,13 +3,13 @@
|
||||||
1C29EE70E90ECED01AF28EC58D2575B5
|
1C29EE70E90ECED01AF28EC58D2575B5
|
||||||
31CE26BC979C57B9E3CC97B40C290CE5
|
31CE26BC979C57B9E3CC97B40C290CE5
|
||||||
3529E7A4CECC24D02678820E6F521162
|
3529E7A4CECC24D02678820E6F521162
|
||||||
4A4B7002DEB734A943B467DF7D2BD1AA
|
37E854EA3BDF7275C6A7631F80804EC4
|
||||||
4E7C044C59E0BCB76AA826789998F624
|
4E7C044C59E0BCB76AA826789998F624
|
||||||
53CBBEB6243FAF5C37249CBA17DE6F4C
|
53CBBEB6243FAF5C37249CBA17DE6F4C
|
||||||
5804C3D68F833A3E8D258C0704DEE775
|
|
||||||
5BCE3651A03711295046DE48BDFE007E
|
5BCE3651A03711295046DE48BDFE007E
|
||||||
|
5C16A2AE6A24E4795F95DDE20EEC458E
|
||||||
|
5C4CED447689F00D9D1ACEB9B895ED29
|
||||||
630C0972985257251EDF89A7117DE423
|
630C0972985257251EDF89A7117DE423
|
||||||
81BAE1F18B4148E83C3265F81FFA6F5C
|
|
||||||
94ACF7B17C3FF42F64E57DD1DA936BD8
|
94ACF7B17C3FF42F64E57DD1DA936BD8
|
||||||
A32E125003F1EDFAD95C487C6A969725
|
A32E125003F1EDFAD95C487C6A969725
|
||||||
ACF6272A1DBB3A2ABD96C0C120B5CA69
|
ACF6272A1DBB3A2ABD96C0C120B5CA69
|
||||||
|
|
|
@ -29,7 +29,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Conversation do
|
||||||
if Actors.is_member?(actor_id, attributed_to_id) do
|
if Actors.is_member?(actor_id, attributed_to_id) do
|
||||||
{:ok,
|
{:ok,
|
||||||
event_id
|
event_id
|
||||||
|> Conversations.find_conversations_for_event(actor_id, page, limit)
|
|> Conversations.find_conversations_for_event(attributed_to_id, page, limit)
|
||||||
|> conversation_participant_to_view()}
|
|> conversation_participant_to_view()}
|
||||||
else
|
else
|
||||||
{:ok, %Page{total: 0, elements: []}}
|
{:ok, %Page{total: 0, elements: []}}
|
||||||
|
|
|
@ -13,16 +13,15 @@ defmodule Mobilizon.Service.Formatter.Text do
|
||||||
def paragraph(string, max_line_length, prefix \\ "") do
|
def paragraph(string, max_line_length, prefix \\ "") do
|
||||||
string
|
string
|
||||||
|> String.split("\n\n", trim: true)
|
|> String.split("\n\n", trim: true)
|
||||||
|> Enum.map(&subparagraph(&1, max_line_length, prefix))
|
|> Enum.map_join("\n#{prefix}\n", &subparagraph(&1, max_line_length, prefix))
|
||||||
|> Enum.join("\n#{prefix}\n")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp subparagraph(string, max_line_length, prefix) do
|
defp subparagraph(string, max_line_length, prefix) do
|
||||||
[word | rest] = String.split(string, ~r/\s+/, trim: true)
|
[word | rest] = String.split(string, ~r/\s+/, trim: true)
|
||||||
|
|
||||||
lines_assemble(rest, max_line_length - String.length(prefix), String.length(word), word, [])
|
rest
|
||||||
|> Enum.map(&"#{prefix}#{&1}")
|
|> lines_assemble(max_line_length - String.length(prefix), String.length(word), word, [])
|
||||||
|> Enum.join("\n")
|
|> Enum.map_join("\n", &"#{prefix}#{&1}")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp lines_assemble([], _, _, line, acc), do: [line | acc] |> Enum.reverse()
|
defp lines_assemble([], _, _, line, acc), do: [line | acc] |> Enum.reverse()
|
||||||
|
|
97
src/components/Conversations/AnnouncementListItem.vue
Normal file
97
src/components/Conversations/AnnouncementListItem.vue
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<router-link
|
||||||
|
class="flex gap-4 w-full items-center px-2 py-4 border-b-stone-200 border-b bg-white dark:bg-transparent my-2 rounded"
|
||||||
|
dir="auto"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.CONVERSATION,
|
||||||
|
params: { id: announcement.conversationParticipantId },
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="overflow-hidden flex-1">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
t("Sent to {count} participants", otherParticipants.length, {
|
||||||
|
count: otherParticipants.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div class="inline-flex items-center px-1.5">
|
||||||
|
<time
|
||||||
|
class="whitespace-nowrap"
|
||||||
|
:datetime="actualDate.toString()"
|
||||||
|
:title="formatDateTimeString(actualDate)"
|
||||||
|
>
|
||||||
|
{{ distanceToNow }}</time
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="line-clamp-4 my-1"
|
||||||
|
dir="auto"
|
||||||
|
v-if="!announcement.lastComment?.deletedAt"
|
||||||
|
>
|
||||||
|
{{ htmlTextEllipsis }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="">
|
||||||
|
{{ t("[This comment has been deleted]") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { formatDistanceToNowStrict } from "date-fns";
|
||||||
|
import { IConversation } from "../../types/conversation";
|
||||||
|
import RouteName from "../../router/name";
|
||||||
|
import { computed, inject } from "vue";
|
||||||
|
import { formatDateTimeString } from "../../filters/datetime";
|
||||||
|
import type { Locale } from "date-fns";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useCurrentActorClient } from "@/composition/apollo/actor";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
announcement: IConversation;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const announcement = computed(() => props.announcement);
|
||||||
|
|
||||||
|
const dateFnsLocale = inject<Locale>("dateFnsLocale");
|
||||||
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
|
const distanceToNow = computed(() => {
|
||||||
|
return (
|
||||||
|
formatDistanceToNowStrict(new Date(actualDate.value), {
|
||||||
|
locale: dateFnsLocale,
|
||||||
|
}) ?? t("Right now")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const htmlTextEllipsis = computed((): string => {
|
||||||
|
const element = document.createElement("div");
|
||||||
|
if (announcement.value.lastComment && announcement.value.lastComment.text) {
|
||||||
|
element.innerHTML = announcement.value.lastComment.text
|
||||||
|
.replace(/<br\s*\/?>/gi, " ")
|
||||||
|
.replace(/<p>/gi, " ");
|
||||||
|
}
|
||||||
|
return element.innerText;
|
||||||
|
});
|
||||||
|
|
||||||
|
const actualDate = computed((): string => {
|
||||||
|
if (
|
||||||
|
announcement.value.updatedAt === announcement.value.insertedAt &&
|
||||||
|
announcement.value.lastComment?.publishedAt
|
||||||
|
) {
|
||||||
|
return announcement.value.lastComment.publishedAt;
|
||||||
|
}
|
||||||
|
return announcement.value.updatedAt;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { currentActor } = useCurrentActorClient();
|
||||||
|
|
||||||
|
const otherParticipants = computed(
|
||||||
|
() =>
|
||||||
|
announcement.value?.participants.filter(
|
||||||
|
(participant) => participant.id !== currentActor.value?.id
|
||||||
|
) ?? []
|
||||||
|
);
|
||||||
|
</script>
|
|
@ -2,18 +2,10 @@
|
||||||
<div class="container mx-auto section">
|
<div class="container mx-auto section">
|
||||||
<breadcrumbs-nav :links="[]" />
|
<breadcrumbs-nav :links="[]" />
|
||||||
<section>
|
<section>
|
||||||
<h1>{{ t("Conversations") }}</h1>
|
<h1>{{ t("Announcements") }}</h1>
|
||||||
<!-- <o-button
|
|
||||||
tag="router-link"
|
|
||||||
:to="{
|
|
||||||
name: RouteName.CREATE_CONVERSATION,
|
|
||||||
params: { uuid: event.uuid },
|
|
||||||
}"
|
|
||||||
>{{ t("New private message") }}</o-button
|
|
||||||
> -->
|
|
||||||
<div v-if="conversations.elements.length > 0">
|
<div v-if="conversations.elements.length > 0">
|
||||||
<conversation-list-item
|
<announcement-list-item
|
||||||
:conversation="conversation"
|
:announcement="conversation"
|
||||||
v-for="conversation in conversations.elements"
|
v-for="conversation in conversations.elements"
|
||||||
:key="conversation.id"
|
:key="conversation.id"
|
||||||
/>
|
/>
|
||||||
|
@ -30,15 +22,14 @@
|
||||||
>
|
>
|
||||||
</o-pagination>
|
</o-pagination>
|
||||||
</div>
|
</div>
|
||||||
<empty-content v-else icon="chat">
|
<empty-content v-else icon="bullhorn" inline>
|
||||||
{{ t("There's no conversations yet") }}
|
{{ t("There's no announcements yet") }}
|
||||||
</empty-content>
|
</empty-content>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ConversationListItem from "../../components/Conversations/ConversationListItem.vue";
|
import AnnouncementListItem from "../../components/Conversations/AnnouncementListItem.vue";
|
||||||
// import RouteName from "../../router/name";
|
|
||||||
import EmptyContent from "../../components/Utils/EmptyContent.vue";
|
import EmptyContent from "../../components/Utils/EmptyContent.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRouteQuery, integerTransformer } from "vue-use-route-query";
|
import { useRouteQuery, integerTransformer } from "vue-use-route-query";
|
||||||
|
|
|
@ -42,7 +42,12 @@
|
||||||
>
|
>
|
||||||
{{ error }}
|
{{ error }}
|
||||||
</o-notification>
|
</o-notification>
|
||||||
<o-button class="mt-3" nativeType="submit">{{ t("Send") }}</o-button>
|
<o-button
|
||||||
|
class="mt-3"
|
||||||
|
nativeType="submit"
|
||||||
|
:disabled="selectedRoles.length == 0"
|
||||||
|
>{{ t("Send") }}</o-button
|
||||||
|
>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1628,5 +1628,6 @@
|
||||||
"I've been mentionned in a conversation": "I've been mentionned in a conversation",
|
"I've been mentionned in a conversation": "I've been mentionned in a conversation",
|
||||||
"New announcement": "New announcement",
|
"New announcement": "New announcement",
|
||||||
"This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.": "This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.",
|
"This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.": "This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.",
|
||||||
"The following participants are groups, which means group members are able to reply to this conversation:": "The following participants are groups, which means group members are able to reply to this conversation:"
|
"The following participants are groups, which means group members are able to reply to this conversation:": "The following participants are groups, which means group members are able to reply to this conversation:",
|
||||||
|
"Sent to {count} participants": "Sent to no participants|Sent to one participant|Sent to {count} participants"
|
||||||
}
|
}
|
|
@ -1624,5 +1624,6 @@
|
||||||
"I've been mentionned in a conversation": "J'ai été mentionnée dans une conversation",
|
"I've been mentionned in a conversation": "J'ai été mentionnée dans une conversation",
|
||||||
"New announcement": "Nouvelle annonce",
|
"New announcement": "Nouvelle annonce",
|
||||||
"This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.": "Cette annonce sera envoyée à tous les participant·es ayant le statut sélectionné ci-dessous. Iels ne pourront pas répondre à votre annonce, mais iels peuvent créer une nouvelle conversation avec vous.",
|
"This announcement will be send to all participants with the statuses selected below. They will not be allowed to reply to your announcement, but they can create a new conversation with you.": "Cette annonce sera envoyée à tous les participant·es ayant le statut sélectionné ci-dessous. Iels ne pourront pas répondre à votre annonce, mais iels peuvent créer une nouvelle conversation avec vous.",
|
||||||
"The following participants are groups, which means group members are able to reply to this conversation:": "Les participants suivants sont des groupes, ce qui signifie que les membres du groupes peuvent répondre dans cette conversation:"
|
"The following participants are groups, which means group members are able to reply to this conversation:": "Les participants suivants sont des groupes, ce qui signifie que les membres du groupes peuvent répondre dans cette conversation:",
|
||||||
|
"Sent to {count} participants": "Envoyé à aucun·e participant·e|Envoyé à une participant·e|Envoyé à {count} participant·es"
|
||||||
}
|
}
|
|
@ -138,7 +138,8 @@ defmodule Mobilizon.Service.Notifier.EmailTest do
|
||||||
@email "someone@somewhere.tld"
|
@email "someone@somewhere.tld"
|
||||||
|
|
||||||
test "send activity notification to anonymous user" do
|
test "send activity notification to anonymous user" do
|
||||||
%Activity{} = activity = insert(:mobilizon_activity, inserted_at: DateTime.utc_now())
|
%Activity{} =
|
||||||
|
activity = insert(:mobilizon_activity, inserted_at: DateTime.utc_now(), type: :comment)
|
||||||
|
|
||||||
Email.send_anonymous_activity(@email, activity, locale: "en")
|
Email.send_anonymous_activity(@email, activity, locale: "en")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue