Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
2f3ea848c4
|
@ -7,6 +7,7 @@ defmodule Mobilizon.Federation.ActivityPub.Publisher do
|
||||||
alias Mobilizon.Config
|
alias Mobilizon.Config
|
||||||
alias Mobilizon.Federation.ActivityPub.{Activity, Federator, Relay, Transmogrifier, Visibility}
|
alias Mobilizon.Federation.ActivityPub.{Activity, Federator, Relay, Transmogrifier, Visibility}
|
||||||
alias Mobilizon.Federation.HTTPSignatures.Signature
|
alias Mobilizon.Federation.HTTPSignatures.Signature
|
||||||
|
alias Mobilizon.Service.HTTP.ActivityPub, as: ActivityPubClient
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
import Mobilizon.Federation.ActivityPub.Utils,
|
import Mobilizon.Federation.ActivityPub.Utils,
|
||||||
|
@ -95,16 +96,16 @@ defmodule Mobilizon.Federation.ActivityPub.Publisher do
|
||||||
date: date
|
date: date
|
||||||
})
|
})
|
||||||
|
|
||||||
Tesla.post(
|
headers = [
|
||||||
inbox,
|
{"Content-Type", "application/activity+json"},
|
||||||
json,
|
{"signature", signature},
|
||||||
headers: [
|
{"digest", digest},
|
||||||
{"Content-Type", "application/activity+json"},
|
{"date", date}
|
||||||
{"signature", signature},
|
]
|
||||||
{"digest", digest},
|
|
||||||
{"date", date}
|
client = ActivityPubClient.client(headers: headers)
|
||||||
]
|
|
||||||
)
|
ActivityPubClient.post(client, inbox, json)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec convert_followers_in_recipients(list(String.t())) :: {list(String.t()), list(String.t())}
|
@spec convert_followers_in_recipients(list(String.t())) :: {list(String.t()), list(String.t())}
|
||||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -15,7 +15,7 @@
|
||||||
"@fullcalendar/daygrid": "^6.1.10",
|
"@fullcalendar/daygrid": "^6.1.10",
|
||||||
"@fullcalendar/interaction": "^6.1.10",
|
"@fullcalendar/interaction": "^6.1.10",
|
||||||
"@fullcalendar/vue3": "^6.1.10",
|
"@fullcalendar/vue3": "^6.1.10",
|
||||||
"@oruga-ui/oruga-next": "^0.8.2",
|
"@oruga-ui/oruga-next": "^0.8.10",
|
||||||
"@oruga-ui/theme-oruga": "^0.2.0",
|
"@oruga-ui/theme-oruga": "^0.2.0",
|
||||||
"@sentry/tracing": "^7.1",
|
"@sentry/tracing": "^7.1",
|
||||||
"@sentry/vue": "^7.1",
|
"@sentry/vue": "^7.1",
|
||||||
|
@ -3138,9 +3138,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@oruga-ui/oruga-next": {
|
"node_modules/@oruga-ui/oruga-next": {
|
||||||
"version": "0.8.5",
|
"version": "0.8.10",
|
||||||
"resolved": "https://registry.npmjs.org/@oruga-ui/oruga-next/-/oruga-next-0.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/@oruga-ui/oruga-next/-/oruga-next-0.8.10.tgz",
|
||||||
"integrity": "sha512-HnODRTrurmke7O5rRNdrbqYuNIdMrnBJ+P3jh6J7/Lk/zgMnpsObSGj/6JfQRvdf5Wq++Ch5yVUys0V4Lm08JQ==",
|
"integrity": "sha512-ETPSoGZu1parbj8C3V2ZojQnN4ptQMiJEwS9Hx44NcaDzu4q/FDsYkKYiz6G9kx8cDceXXxvydfOUpZePVVdzw==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.0.0"
|
"vue": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
"@fullcalendar/daygrid": "^6.1.10",
|
"@fullcalendar/daygrid": "^6.1.10",
|
||||||
"@fullcalendar/interaction": "^6.1.10",
|
"@fullcalendar/interaction": "^6.1.10",
|
||||||
"@fullcalendar/vue3": "^6.1.10",
|
"@fullcalendar/vue3": "^6.1.10",
|
||||||
"@oruga-ui/oruga-next": "^0.8.2",
|
"@oruga-ui/oruga-next": "^0.8.10",
|
||||||
"@oruga-ui/theme-oruga": "^0.2.0",
|
"@oruga-ui/theme-oruga": "^0.2.0",
|
||||||
"@fullcalendar/core": "^6.1.10",
|
"@fullcalendar/core": "^6.1.10",
|
||||||
"@fullcalendar/daygrid": "^6.1.10",
|
"@fullcalendar/daygrid": "^6.1.10",
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<div class="flex-1" v-else>
|
<div class="flex-1" v-else>
|
||||||
{{ `@${selectedActor.preferredUsername}` }}
|
{{ `@${selectedActor.preferredUsername}` }}
|
||||||
</div>
|
</div>
|
||||||
<o-button type="text" @click="isComponentModalActive = true">
|
<o-button @click="isComponentModalActive = true">
|
||||||
{{ $t("Change") }}
|
{{ $t("Change") }}
|
||||||
</o-button>
|
</o-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
</template>
|
</template>
|
||||||
<o-taginput
|
<o-taginput
|
||||||
:modelValue="tagsStrings"
|
:modelValue="tagsStrings"
|
||||||
@update:modelValue="updateTags"
|
@remove="remove"
|
||||||
|
@add="add"
|
||||||
:data="filteredTags"
|
:data="filteredTags"
|
||||||
:allow-autocomplete="true"
|
:allow-autocomplete="true"
|
||||||
:allow-new="true"
|
:allow-new="true"
|
||||||
|
@ -69,6 +70,8 @@ const id = computed((): string => {
|
||||||
|
|
||||||
const { load: fetchTags } = useFetchTags();
|
const { load: fetchTags } = useFetchTags();
|
||||||
|
|
||||||
|
initTagsStringsValue();
|
||||||
|
|
||||||
const getFilteredTags = async (newText: string): Promise<void> => {
|
const getFilteredTags = async (newText: string): Promise<void> => {
|
||||||
text.value = newText;
|
text.value = newText;
|
||||||
const res = await fetchTags(
|
const res = await fetchTags(
|
||||||
|
@ -91,11 +94,16 @@ const filteredTags = computed((): ITag[] => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(props.modelValue, (newValue, oldValue) => {
|
// TODO It seems that '@update:modelValue="updateTags"' does not works anymore...
|
||||||
if (newValue != oldValue) {
|
// so temporarily call the function updateTags() at remove and add tag event
|
||||||
tagsStrings.value = propsValue.value.map((tag: ITag) => tag.title);
|
// https://github.com/oruga-ui/oruga/issues/967
|
||||||
}
|
function remove() {
|
||||||
});
|
updateTags(tagsStrings.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add() {
|
||||||
|
updateTags(tagsStrings.value);
|
||||||
|
}
|
||||||
|
|
||||||
const updateTags = (newTagsStrings: string[]) => {
|
const updateTags = (newTagsStrings: string[]) => {
|
||||||
const tagEntities = newTagsStrings.map((tag: string | ITag) => {
|
const tagEntities = newTagsStrings.map((tag: string | ITag) => {
|
||||||
|
@ -106,4 +114,34 @@ const updateTags = (newTagsStrings: string[]) => {
|
||||||
});
|
});
|
||||||
emit("update:modelValue", tagEntities);
|
emit("update:modelValue", tagEntities);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isArraysEquals(array1: string[], array2: string[]) {
|
||||||
|
if (array1.length !== array2.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < array1.length; i++) {
|
||||||
|
if (array1[i] !== array2[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTagsStringsValue() {
|
||||||
|
// This is useful when tag data is already cached from the API during navigation inside the app
|
||||||
|
tagsStrings.value = propsValue.value.map((tag: ITag) => tag.title);
|
||||||
|
|
||||||
|
// This watch() function is useful when tag data loads directly from the API upon page load
|
||||||
|
watch(propsValue, () => {
|
||||||
|
const newTagsStrings = propsValue.value.map((tag: ITag) => tag.title);
|
||||||
|
|
||||||
|
// Changing tagsStrings.value triggers updateTags(), updateTags() triggers this watch() function again.
|
||||||
|
// To stop the loop, edit tagsStrings.value only if it has changed !
|
||||||
|
if (!isArraysEquals(tagsStrings.value, newTagsStrings)) {
|
||||||
|
tagsStrings.value = newTagsStrings;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -23,10 +23,27 @@
|
||||||
<div
|
<div
|
||||||
class="rounded p-3 flex-auto md:flex-none bg-zinc-300 dark:bg-zinc-700"
|
class="rounded p-3 flex-auto md:flex-none bg-zinc-300 dark:bg-zinc-700"
|
||||||
>
|
>
|
||||||
<o-field>
|
<o-field
|
||||||
<o-switch v-model="showUpcoming">{{
|
class="date-filter"
|
||||||
showUpcoming ? t("Upcoming events") : t("Past events")
|
expanded
|
||||||
}}</o-switch>
|
:label="
|
||||||
|
showUpcoming
|
||||||
|
? t('Showing events starting on')
|
||||||
|
: t('Showing events before')
|
||||||
|
"
|
||||||
|
labelFor="events-start-datepicker"
|
||||||
|
>
|
||||||
|
<o-datepicker
|
||||||
|
v-model="datePick"
|
||||||
|
:first-day-of-week="firstDayOfWeek"
|
||||||
|
id="events-start-datepicker"
|
||||||
|
/>
|
||||||
|
<o-button
|
||||||
|
@click="datePick = new Date()"
|
||||||
|
class="reset-area !h-auto"
|
||||||
|
icon-left="close"
|
||||||
|
:title="t('Clear date filter field')"
|
||||||
|
/>
|
||||||
</o-field>
|
</o-field>
|
||||||
<o-field v-if="showUpcoming">
|
<o-field v-if="showUpcoming">
|
||||||
<o-checkbox v-model="showDrafts">{{ t("Drafts") }}</o-checkbox>
|
<o-checkbox v-model="showDrafts">{{ t("Drafts") }}</o-checkbox>
|
||||||
|
@ -50,28 +67,6 @@
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
<o-field
|
|
||||||
class="date-filter"
|
|
||||||
expanded
|
|
||||||
:label="
|
|
||||||
showUpcoming
|
|
||||||
? t('Showing events starting on')
|
|
||||||
: t('Showing events before')
|
|
||||||
"
|
|
||||||
labelFor="events-start-datepicker"
|
|
||||||
>
|
|
||||||
<o-datepicker
|
|
||||||
v-model="dateFilter"
|
|
||||||
:first-day-of-week="firstDayOfWeek"
|
|
||||||
id="events-start-datepicker"
|
|
||||||
/>
|
|
||||||
<o-button
|
|
||||||
@click="dateFilter = new Date()"
|
|
||||||
class="reset-area !h-auto"
|
|
||||||
icon-left="close"
|
|
||||||
:title="t('Clear date filter field')"
|
|
||||||
/>
|
|
||||||
</o-field>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-w-[300px]">
|
<div class="flex-1 min-w-[300px]">
|
||||||
<section
|
<section
|
||||||
|
@ -250,27 +245,43 @@ const futurePage = ref(1);
|
||||||
const pastPage = ref(1);
|
const pastPage = ref(1);
|
||||||
const limit = ref(10);
|
const limit = ref(10);
|
||||||
|
|
||||||
|
function startOfDay(d: Date): string {
|
||||||
|
const pad = (n: int): string => {
|
||||||
|
return (n > 9 ? "" : "0") + n.toString();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
d.getFullYear() +
|
||||||
|
"-" +
|
||||||
|
pad(d.getMonth() + 1) +
|
||||||
|
"-" +
|
||||||
|
pad(d.getDate()) +
|
||||||
|
"T00:00:00Z"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const showUpcoming = useRouteQuery("showUpcoming", true, booleanTransformer);
|
const showUpcoming = useRouteQuery("showUpcoming", true, booleanTransformer);
|
||||||
const showDrafts = useRouteQuery("showDrafts", true, booleanTransformer);
|
const showDrafts = useRouteQuery("showDrafts", true, booleanTransformer);
|
||||||
const showAttending = useRouteQuery("showAttending", true, booleanTransformer);
|
const showAttending = useRouteQuery("showAttending", true, booleanTransformer);
|
||||||
const showMyGroups = useRouteQuery("showMyGroups", false, booleanTransformer);
|
const showMyGroups = useRouteQuery("showMyGroups", false, booleanTransformer);
|
||||||
const dateFilter = useRouteQuery("dateFilter", new Date(), {
|
const dateFilter = useRouteQuery("dateFilter", startOfDay(new Date()), {
|
||||||
fromQuery(query) {
|
fromQuery(query) {
|
||||||
if (query && /(\d{4}-\d{2}-\d{2})/.test(query)) {
|
if (query && /(\d{4}-\d{2}-\d{2})/.test(query)) {
|
||||||
return new Date(`${query}T00:00:00Z`);
|
return `${query}T00:00:00Z`;
|
||||||
}
|
}
|
||||||
return new Date();
|
return startOfDay(new Date());
|
||||||
},
|
},
|
||||||
toQuery(value: Date) {
|
toQuery(value: string) {
|
||||||
const pad = (number: number) => {
|
return value.slice(0, 10);
|
||||||
if (number < 10) {
|
},
|
||||||
return "0" + number;
|
});
|
||||||
}
|
|
||||||
return number;
|
// bridge between datepicker expecting a Date object and dateFilter being a string
|
||||||
};
|
const datePick = computed({
|
||||||
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(
|
get: () => {
|
||||||
value.getDate()
|
return new Date(dateFilter.value);
|
||||||
)}`;
|
},
|
||||||
|
set: (d: Date) => {
|
||||||
|
dateFilter.value = startOfDay(d);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -323,10 +334,7 @@ const pastParticipations = computed(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const monthlyEvents = (
|
const monthlyEvents = (elements: Eventable[]): Map<string, Eventable[]> => {
|
||||||
elements: Eventable[],
|
|
||||||
revertSort = false
|
|
||||||
): Map<string, Eventable[]> => {
|
|
||||||
const res = elements.filter((element: Eventable) => {
|
const res = elements.filter((element: Eventable) => {
|
||||||
if ("role" in element) {
|
if ("role" in element) {
|
||||||
return (
|
return (
|
||||||
|
@ -336,19 +344,12 @@ const monthlyEvents = (
|
||||||
}
|
}
|
||||||
return element.beginsOn != null;
|
return element.beginsOn != null;
|
||||||
});
|
});
|
||||||
if (revertSort) {
|
// sort by start date, ascending
|
||||||
res.sort((a: Eventable, b: Eventable) => {
|
res.sort((a: Eventable, b: Eventable) => {
|
||||||
const aTime = "role" in a ? a.event.beginsOn : a.beginsOn;
|
const aTime = "role" in a ? a.event.beginsOn : a.beginsOn;
|
||||||
const bTime = "role" in b ? b.event.beginsOn : b.beginsOn;
|
const bTime = "role" in b ? b.event.beginsOn : b.beginsOn;
|
||||||
return new Date(bTime).getTime() - new Date(aTime).getTime();
|
return new Date(aTime).getTime() - new Date(bTime).getTime();
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
res.sort((a: Eventable, b: Eventable) => {
|
|
||||||
const aTime = "role" in a ? a.event.beginsOn : a.beginsOn;
|
|
||||||
const bTime = "role" in b ? b.event.beginsOn : b.beginsOn;
|
|
||||||
return new Date(aTime).getTime() - new Date(bTime).getTime();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return res.reduce((acc: Map<string, Eventable[]>, element: Eventable) => {
|
return res.reduce((acc: Map<string, Eventable[]>, element: Eventable) => {
|
||||||
const month = new Date(
|
const month = new Date(
|
||||||
"role" in element ? element.event.beginsOn : element.beginsOn
|
"role" in element ? element.event.beginsOn : element.beginsOn
|
||||||
|
|
|
@ -195,6 +195,10 @@ onMounted(async () => {
|
||||||
pictureFile.value = await buildFileFromIMedia(post.value?.picture);
|
pictureFile.value = await buildFileFromIMedia(post.value?.picture);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This is useful when post data is already cached from the API during navigation inside the app
|
||||||
|
editablePost.value = { ...editablePost.value, ...post.value };
|
||||||
|
|
||||||
|
// This watch() function is useful when post data loads directly from the API upon page load
|
||||||
watch(post, async (newPost: IPost | undefined, oldPost: IPost | undefined) => {
|
watch(post, async (newPost: IPost | undefined, oldPost: IPost | undefined) => {
|
||||||
if (oldPost?.picture !== newPost?.picture) {
|
if (oldPost?.picture !== newPost?.picture) {
|
||||||
pictureFile.value = await buildFileFromIMedia(post.value?.picture);
|
pictureFile.value = await buildFileFromIMedia(post.value?.picture);
|
||||||
|
|
Loading…
Reference in a new issue