clear memory cache for instances statistics when appropriate

Add a loading state to buttons InstanceView

Automatically refresh DashboardView data

Fixes #1915
This commit is contained in:
Massedil
2025-12-17 14:15:19 +01:00
parent 33e0d13b4e
commit 2c0adc8670
5 changed files with 102 additions and 68 deletions

View File

@@ -608,6 +608,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
def remove_relay(_parent, %{address: address}, %{context: %{current_user: %User{role: role}}})
when is_admin(role) do
with {:ok, _activity, follow} <- Relay.unfollow(address) do
Statistics.clear_cached_value(:instance_followers)
Statistics.clear_cached_value(:instance_followings)
{:ok, follow}
end
end
@@ -621,6 +623,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
)
when is_admin(role) do
with {:ok, _activity, follow} <- Relay.accept(address) do
Statistics.clear_cached_value(:instance_followers)
Statistics.clear_cached_value(:instance_followings)
{:ok, follow}
end
end
@@ -634,6 +638,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
)
when is_admin(role) do
with {:ok, _activity, follow} <- Relay.reject(address) do
Statistics.clear_cached_value(:instance_followers)
Statistics.clear_cached_value(:instance_followings)
{:ok, follow}
end
end

View File

@@ -20,6 +20,10 @@ defmodule Mobilizon.Service.Statistics do
end
end
def clear_cached_value(key) do
Cachex.del(:statistics, key)
end
defp create_cache(:local_users) do
Users.count_users()
end

View File

@@ -50,7 +50,7 @@
"
:to="{
name: RouteName.INSTANCES,
query: { followStatus: InstanceFilterFollowStatus.FOLLOWING },
query: { followStatus: InstanceFilterFollowStatus.THEY_FOLLOW_US },
}"
/>
<LinkedNumberDashboardTile
@@ -60,7 +60,7 @@
"
:to="{
name: RouteName.INSTANCES,
query: { followStatus: InstanceFilterFollowStatus.FOLLOWED },
query: { followStatus: InstanceFilterFollowStatus.WE_FOLLOW_THEM },
}"
/>
</div>
@@ -98,7 +98,11 @@ import GroupCard from "@/components/Group/GroupCard.vue";
import EventCard from "@/components/Event/EventCard.vue";
const { result: dashboardResult } = useQuery<{ dashboard: IDashboard }>(
DASHBOARD
DASHBOARD,
{},
{
fetchPolicy: "cache-and-network",
}
);
const dashboard = computed(() => dashboardResult.value?.dashboard);

View File

@@ -119,35 +119,41 @@
)
"
>
<button
<o-button
@click="
removeInstanceFollow({
address: instance?.relayAddress,
})
"
:loading="removeInstanceFollowLoading"
v-if="instance.followedStatus == InstanceFollowStatus.APPROVED"
variant="primary"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ t("Stop following instance") }}
</button>
<button
</o-button>
<o-button
@click="
removeInstanceFollow({
address: instance?.relayAddress,
})
"
:loading="removeInstanceFollowLoading"
v-else-if="instance.followedStatus == InstanceFollowStatus.PENDING"
variant="primary"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ t("Cancel follow request") }}
</button>
<button
</o-button>
<o-button
@click="followInstance"
:loading="followInstanceLoading"
v-else
variant="primary"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ t("Follow instance") }}
</button>
</o-button>
</div>
<div
v-else
@@ -158,31 +164,35 @@
<div
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2 justify-center"
>
<button
<o-button
@click="
acceptInstance({
address: instance?.relayAddress,
})
"
:loading="acceptInstanceLoading"
v-if="instance.followerStatus == InstanceFollowStatus.PENDING"
variant="primary"
class="bg-green-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ t("Accept follow") }}
</button>
<button
</o-button>
<o-button
@click="
rejectInstance({
address: instance?.relayAddress,
})
"
:loading="rejectInstanceLoading"
v-if="
instance.followerStatus == InstanceFollowStatus.PENDING ||
instance.followerStatus == InstanceFollowStatus.APPROVED
"
variant="primary"
class="bg-red-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ t("Reject follow") }}
</button>
</o-button>
<p v-if="instance.followerStatus == InstanceFollowStatus.NONE">
{{ t("This instance doesn't follow yours.") }}
</p>
@@ -222,9 +232,11 @@ const notifier = inject<Notifier>("notifier");
const { t } = useI18n({ useScope: "global" });
const { mutate: acceptInstance, onError: onAcceptInstanceError } = useMutation(
ACCEPT_RELAY,
() => ({
const {
mutate: acceptInstance,
loading: acceptInstanceLoading,
onError: onAcceptInstanceError,
} = useMutation(ACCEPT_RELAY, () => ({
update(cache: ApolloCache<any>) {
cache.writeFragment({
id: cache.identify(instance.value as unknown as Reference),
@@ -238,8 +250,7 @@ const { mutate: acceptInstance, onError: onAcceptInstanceError } = useMutation(
},
});
},
})
);
}));
onAcceptInstanceError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
@@ -250,9 +261,11 @@ onAcceptInstanceError((error) => {
/**
* Reject instance follow
*/
const { mutate: rejectInstance, onError: onRejectInstanceError } = useMutation(
REJECT_RELAY,
() => ({
const {
mutate: rejectInstance,
loading: rejectInstanceLoading,
onError: onRejectInstanceError,
} = useMutation(REJECT_RELAY, () => ({
update(cache: ApolloCache<any>) {
cache.writeFragment({
id: cache.identify(instance.value as unknown as Reference),
@@ -266,8 +279,7 @@ const { mutate: rejectInstance, onError: onRejectInstanceError } = useMutation(
},
});
},
})
);
}));
onRejectInstanceError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
@@ -275,8 +287,11 @@ onRejectInstanceError((error) => {
}
});
const { mutate: followInstanceMutation, onError: onFollowInstanceError } =
useMutation<{ addInstance: IInstance }>(ADD_INSTANCE);
const {
mutate: followInstanceMutation,
loading: followInstanceLoading,
onError: onFollowInstanceError,
} = useMutation<{ addInstance: IInstance }>(ADD_INSTANCE);
onFollowInstanceError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
@@ -292,8 +307,11 @@ const followInstance = async (e: Event): Promise<void> => {
/**
* Stop following instance
*/
const { mutate: removeInstanceFollow, onError: onRemoveInstanceFollowError } =
useMutation(REMOVE_RELAY, () => ({
const {
mutate: removeInstanceFollow,
loading: removeInstanceFollowLoading,
onError: onRemoveInstanceFollowError,
} = useMutation(REMOVE_RELAY, () => ({
update(cache: ApolloCache<any>) {
cache.writeFragment({
id: cache.identify(instance.value as unknown as Reference),

View File

@@ -21,7 +21,9 @@ exports[`InstanceView > Show simple 1`] = `
</section>
<section>
<div class="mt-3 grid xl:grid-cols-2 gap-4">
<div class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2 justify-center"><button class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto">Follow instance</button></div>
<div class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2 justify-center"><button data-oruga="button" type="button" role="button" tabindex="0" class="o-button o-button--primary bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"><span class="o-button__wrapper"><!----><span class="o-button__label">Follow instance</span>
<!----></span>
</button></div>
<div class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2 justify-center">
<!--v-if-->
<!--v-if-->