Fix front-end stuff

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2023-03-23 20:39:39 +01:00
parent 986ae45f52
commit c4e9f88e85
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
23 changed files with 229 additions and 83 deletions

View File

@ -44,5 +44,7 @@ import GroupSection from "@/components/Group/GroupSection.vue";
const { t } = useI18n({ useScope: "global" });
defineProps<{ group: IGroup }>();
defineProps<{
group: Pick<IGroup, "preferredUsername" | "domain" | "discussions">;
}>();
</script>

View File

@ -56,5 +56,7 @@ import GroupSection from "@/components/Group/GroupSection.vue";
const { t } = useI18n({ useScope: "global" });
defineProps<{ group: IGroup }>();
defineProps<{
group: Pick<IGroup, "preferredUsername" | "domain" | "resources">;
}>();
</script>

View File

@ -46,7 +46,7 @@ import { useRouter } from "vue-router";
import Draggable from "zhyswan-vuedraggable";
import { IResource } from "@/types/resource";
import RouteName from "@/router/name";
import { IGroup, usernameWithDomain } from "@/types/actor";
import { IMinimalActor, usernameWithDomain } from "@/types/actor";
import ResourceDropdown from "./ResourceDropdown.vue";
import { UPDATE_RESOURCE } from "@/graphql/resources";
import { inject, ref } from "vue";
@ -59,7 +59,7 @@ import { Snackbar } from "@/plugins/snackbar";
const props = withDefaults(
defineProps<{
resource: IResource;
group: IGroup;
group: IMinimalActor;
inline?: boolean;
}>(),
{ inline: false }

View File

@ -215,12 +215,14 @@ export function useRegistrationConfig() {
}>(REGISTRATIONS, undefined, { fetchPolicy: "cache-only" });
const registrationsOpen = computed(
() => result.value?.config.registrationsOpen
() => result.value?.config?.registrationsOpen
);
const registrationsAllowlist = computed(
() => result.value?.config.registrationsAllowlist
() => result.value?.config?.registrationsAllowlist
);
const databaseLogin = computed(
() => result.value?.config?.auth?.databaseLogin
);
const databaseLogin = computed(() => result.value?.config.auth.databaseLogin);
return {
registrationsOpen,
registrationsAllowlist,

View File

@ -0,0 +1,44 @@
import { useQuery } from "@vue/apollo-composable";
import { computed, unref } from "vue";
import { useCurrentUserClient } from "./user";
import type { Ref } from "vue";
import { IGroup } from "@/types/actor";
import { GROUP_DISCUSSIONS_LIST } from "@/graphql/discussion";
export function useGroupDiscussionsList(
name: string | undefined | Ref<string | undefined>,
options?: {
discussionsPage?: number;
discussionsLimit?: number;
}
) {
const { currentUser } = useCurrentUserClient();
const { result, error, loading, onResult, onError, refetch } = useQuery<
{
group: Pick<
IGroup,
"id" | "preferredUsername" | "name" | "domain" | "discussions"
>;
},
{
name: string;
discussionsPage?: number;
discussionsLimit?: number;
}
>(
GROUP_DISCUSSIONS_LIST,
() => ({
name: unref(name),
...options,
}),
() => ({
enabled:
unref(name) !== undefined &&
unref(name) !== "" &&
currentUser.value?.isLoggedIn,
fetchPolicy: "cache-and-network",
})
);
const group = computed(() => result.value?.group);
return { group, error, loading, onResult, onError, refetch };
}

View File

@ -2,7 +2,7 @@ import { PERSON_MEMBERSHIPS } from "@/graphql/actor";
import {
CREATE_GROUP,
DELETE_GROUP,
FETCH_GROUP,
FETCH_GROUP_PUBLIC,
LEAVE_GROUP,
UPDATE_GROUP,
} from "@/graphql/group";
@ -50,7 +50,7 @@ export function useGroup(
discussionsLimit?: number;
}
>(
FETCH_GROUP,
FETCH_GROUP_PUBLIC,
() => ({
name: unref(name),
...options,

View File

@ -0,0 +1,44 @@
import { useQuery } from "@vue/apollo-composable";
import { computed, unref } from "vue";
import { useCurrentUserClient } from "./user";
import type { Ref } from "vue";
import { IGroup } from "@/types/actor";
import { GROUP_RESOURCES_LIST } from "@/graphql/resources";
export function useGroupResourcesList(
name: string | undefined | Ref<string | undefined>,
options?: {
resourcesPage?: number;
resourcesLimit?: number;
}
) {
const { currentUser } = useCurrentUserClient();
const { result, error, loading, onResult, onError, refetch } = useQuery<
{
group: Pick<
IGroup,
"id" | "preferredUsername" | "name" | "domain" | "resources"
>;
},
{
name: string;
resourcesPage?: number;
resourcesLimit?: number;
}
>(
GROUP_RESOURCES_LIST,
() => ({
name: unref(name),
...options,
}),
() => ({
enabled:
unref(name) !== undefined &&
unref(name) !== "" &&
currentUser.value?.isLoggedIn,
fetchPolicy: "cache-and-network",
})
);
const group = computed(() => result.value?.group);
return { group, error, loading, onResult, onError, refetch };
}

View File

@ -18,12 +18,13 @@ export function useCurrentUserClient() {
result: currentUserResult,
error,
loading,
onResult,
} = useQuery<{
currentUser: ICurrentUser;
}>(CURRENT_USER_CLIENT);
const currentUser = computed(() => currentUserResult.value?.currentUser);
return { currentUser, error, loading };
return { currentUser, error, loading, onResult };
}
export function useLoggedUser() {

View File

@ -148,3 +148,25 @@ export const DISCUSSION_COMMENT_CHANGED = gql`
}
${ACTOR_FRAGMENT}
`;
export const GROUP_DISCUSSIONS_LIST = gql`
query GroupDiscussionsList(
$name: String!
$discussionsPage: Int
$discussionsLimit: Int
) {
group(preferredUsername: $name) {
id
preferredUsername
name
domain
discussions(page: $discussionsPage, limit: $discussionsLimit) {
total
elements {
...DiscussionBasicFields
}
}
}
}
${DISCUSSION_BASIC_FIELDS_FRAGMENT}
`;

View File

@ -54,8 +54,8 @@ export const LIST_GROUPS = gql`
${ACTOR_FRAGMENT}
`;
export const GROUP_FIELDS_FRAGMENTS = gql`
fragment GroupFullFields on Group {
export const GROUP_BASIC_FIELDS_FRAGMENTS = gql`
fragment GroupBasicFields on Group {
...ActorFragment
suspended
visibility
@ -137,18 +137,23 @@ export const GROUP_FIELDS_FRAGMENTS = gql`
}
total
}
discussions(page: $discussionsPage, limit: $discussionsLimit) {
total
elements {
...DiscussionBasicFields
}
}
posts(page: $postsPage, limit: $postsLimit) {
total
elements {
...PostBasicFields
}
}
}
${ACTOR_FRAGMENT}
${ADDRESS_FRAGMENT}
${EVENT_OPTIONS_FRAGMENT}
${TAG_FRAGMENT}
${POST_BASIC_FIELDS}
`;
export const GROUP_FIELDS_FRAGMENTS = gql`
fragment GroupFullFields on Group {
...GroupBasicFields
members(page: $membersPage, limit: $membersLimit) {
elements {
id
@ -196,14 +201,13 @@ export const GROUP_FIELDS_FRAGMENTS = gql`
total
}
}
${ACTOR_FRAGMENT}
${ADDRESS_FRAGMENT}
${EVENT_OPTIONS_FRAGMENT}
${TAG_FRAGMENT}
${GROUP_BASIC_FIELDS_FRAGMENTS}
${DISCUSSION_BASIC_FIELDS_FRAGMENT}
${RESOURCE_METADATA_BASIC_FIELDS_FRAGMENT}
`;
export const FETCH_GROUP = gql`
query FetchGroup(
export const FETCH_GROUP_PUBLIC = gql`
query FetchGroupPublic(
$name: String!
$afterDateTime: DateTime
$beforeDateTime: DateTime
@ -211,19 +215,12 @@ export const FETCH_GROUP = gql`
$organisedEventsLimit: Int
$postsPage: Int
$postsLimit: Int
$membersPage: Int
$membersLimit: Int
$discussionsPage: Int
$discussionsLimit: Int
) {
group(preferredUsername: $name) {
...GroupFullFields
...GroupBasicFields
}
}
${GROUP_FIELDS_FRAGMENTS}
${DISCUSSION_BASIC_FIELDS_FRAGMENT}
${POST_BASIC_FIELDS}
${RESOURCE_METADATA_BASIC_FIELDS_FRAGMENT}
${GROUP_BASIC_FIELDS_FRAGMENTS}
`;
export const GET_GROUP = gql`

View File

@ -161,3 +161,34 @@ export const PREVIEW_RESOURCE_LINK = gql`
}
${RESOURCE_METADATA_BASIC_FIELDS_FRAGMENT}
`;
export const GROUP_RESOURCES_LIST = gql`
query GroupResourcesList(
$name: String!
$resourcesPage: Int
$resourcesLimit: Int
) {
group(preferredUsername: $name) {
id
preferredUsername
name
domain
resources(page: $resourcesPage, limit: $resourcesLimit) {
elements {
id
title
resourceUrl
summary
updatedAt
type
path
metadata {
...ResourceMetadataBasicFields
}
}
total
}
}
}
${RESOURCE_METADATA_BASIC_FIELDS_FRAGMENT}
`;

View File

@ -44,7 +44,7 @@ export const userRoutes: RouteRecordRaw[] = [
},
},
{
path: "/resend-instructions",
path: "/resend-instructions/:email?",
name: UserRouteName.RESEND_CONFIRMATION,
component: (): Promise<any> =>
import("@/views/User/ResendConfirmation.vue"),
@ -57,7 +57,7 @@ export const userRoutes: RouteRecordRaw[] = [
},
},
{
path: "/password-reset/send",
path: "/password-reset/send/:email?",
name: UserRouteName.SEND_PASSWORD_RESET,
component: (): Promise<any> => import("@/views/User/SendPasswordReset.vue"),
props: true,

View File

@ -15,6 +15,8 @@ export interface IActor {
type: ActorType;
}
export type IMinimalActor = Pick<IActor, "preferredUsername" | "domain">;
export class Actor implements IActor {
id?: string;
@ -57,7 +59,7 @@ export class Actor implements IActor {
}
export function usernameWithDomain(
actor: IActor | undefined,
actor: IMinimalActor | undefined,
force = false
): string {
if (!actor) return "";

View File

@ -77,10 +77,9 @@ import { displayName, usernameWithDomain } from "@/types/actor";
import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue";
import RouteName from "../../router/name";
import { MemberRole } from "@/types/enums";
import { useGroupDiscussionsList } from "@/composition/apollo/discussions";
import { IMember } from "@/types/actor/member.model";
import EmptyContent from "@/components/Utils/EmptyContent.vue";
import { useGroup } from "@/composition/apollo/group";
import { usePersonStatusGroup } from "@/composition/apollo/actor";
import { useI18n } from "vue-i18n";
import { useRouteQuery, integerTransformer } from "vue-use-route-query";
@ -92,10 +91,13 @@ const DISCUSSIONS_PER_PAGE = 10;
const props = defineProps<{ preferredUsername: string }>();
const { group, loading: groupLoading } = useGroup(props.preferredUsername, {
discussionsPage: page.value,
discussionsLimit: DISCUSSIONS_PER_PAGE,
});
const { group, loading: groupLoading } = useGroupDiscussionsList(
props.preferredUsername,
{
discussionsPage: page.value,
discussionsLimit: DISCUSSIONS_PER_PAGE,
}
);
const { person, loading: personLoading } = usePersonStatusGroup(
props.preferredUsername
@ -109,7 +111,7 @@ useHead({
const groupMemberships = computed((): (string | undefined)[] => {
if (!person.value || !person.value.id) return [];
return person.value.memberships.elements
return (person.value.memberships?.elements ?? [])
.filter(
(membership: IMember) =>
![

View File

@ -649,7 +649,7 @@ const FullAddressAutoComplete = defineAsyncComponent(
// },
// },
// group: {
// query: FETCH_GROUP,
// query: FETCH_GROUP_PUBLIC,
// fetchPolicy: "cache-and-network",
// variables() {
// return {

View File

@ -73,9 +73,9 @@
t(
"{count} members",
{
count: group.members.total,
count: group.members?.total,
},
group.members.total
group.members?.total
)
}}
<router-link
@ -388,9 +388,9 @@
<!-- Private things -->
<div class="flex-1 m-0 flex flex-col flex-wrap gap-2">
<!-- Group discussions -->
<Discussions :group="group" class="flex-1" />
<Discussions :group="discussionGroup ?? group" class="flex-1" />
<!-- Resources -->
<Resources :group="group" class="flex-1" />
<Resources :group="resourcesGroup ?? group" class="flex-1" />
</div>
<!-- Public things -->
<div class="flex-1 m-0 flex flex-col flex-wrap gap-2">
@ -452,9 +452,9 @@
t(
"{count} members",
{
count: group.members.total,
count: group.members?.total,
},
group.members.total
group.members?.total
)
}}
</event-metadata-block>
@ -671,6 +671,7 @@ import { useAnonymousReportsConfig } from "../../composition/apollo/config";
import { computed, defineAsyncComponent, inject, ref, watch } from "vue";
import { useCurrentActorClient } from "@/composition/apollo/actor";
import { useGroup, useLeaveGroup } from "@/composition/apollo/group";
import { useGroupDiscussionsList } from "@/composition/apollo/discussions";
import { useRouter } from "vue-router";
import { useMutation, useQuery } from "@vue/apollo-composable";
import AccountGroup from "vue-material-design-icons/AccountGroup.vue";
@ -691,6 +692,7 @@ import Posts from "@/components/Group/Sections/PostsSection.vue";
import Events from "@/components/Group/Sections/EventsSection.vue";
import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier";
import { useGroupResourcesList } from "@/composition/apollo/resources";
const props = defineProps<{
preferredUsername: string;
@ -705,6 +707,14 @@ const {
} = useGroup(props.preferredUsername, { afterDateTime: new Date() });
const router = useRouter();
const { group: discussionGroup } = useGroupDiscussionsList(
props.preferredUsername
);
const { group: resourcesGroup } = useGroupResourcesList(
props.preferredUsername,
{ resourcesPage: 1, resourcesLimit: 3 }
);
const { t } = useI18n({ useScope: "global" });
// const { person } = usePersonStatusGroup(group);
@ -1037,7 +1047,7 @@ const isCurrentActorOnADifferentDomainThanGroup = computed((): boolean => {
const members = computed((): IMember[] => {
return (
group.value?.members.elements.filter(
(group.value?.members?.elements ?? []).filter(
(member: IMember) =>
![
MemberRole.INVITED,

View File

@ -290,7 +290,10 @@ const { result: membershipsResult, loading: membershipsLoading } = useQuery<{
}>(
PERSON_MEMBERSHIPS,
() => ({ id: currentActor.value?.id }),
() => ({ enabled: currentActor.value?.id !== undefined })
() => ({
enabled:
currentActor.value?.id !== undefined && currentActor.value?.id !== null,
})
);
const memberships = computed(() => membershipsResult.value?.person.memberships);

View File

@ -106,7 +106,7 @@
variant="text"
:to="{
name: RouteName.REGISTER,
params: {
query: {
default_email: credentials.email,
default_password: credentials.password,
},
@ -143,14 +143,6 @@ import { LoginError, LoginErrorCode } from "@/types/enums";
import { useCurrentUserClient } from "@/composition/apollo/user";
import { useHead } from "@vueuse/head";
const props = withDefaults(
defineProps<{
email?: string;
password?: string;
}>(),
{ email: "", password: "" }
);
const { t } = useI18n({ useScope: "global" });
const router = useRouter();
const route = useRoute();
@ -177,8 +169,9 @@ const errors = ref<string[]>([]);
const submitted = ref(false);
const credentials = reactive({
email: "",
password: "",
email: typeof route.query.email === "string" ? route.query.email : "",
password:
typeof route.query.password === "string" ? route.query.password : "",
});
const redirect = ref<string | undefined>("");
@ -298,9 +291,6 @@ const currentProvider = computed(() => {
});
onMounted(() => {
credentials.email = props.email;
credentials.password = props.password;
const query = route?.query;
errorCode.value = query?.code as LoginErrorCode;
redirect.value = query?.redirect as string | undefined;

View File

@ -181,7 +181,7 @@
variant="text"
:to="{
name: RouteName.LOGIN,
params: {
query: {
email: credentials.email,
password: credentials.password,
},
@ -212,7 +212,7 @@ import AuthProviders from "../../components/User/AuthProviders.vue";
import { computed, reactive, ref, watch } from "vue";
import { useMutation, useQuery } from "@vue/apollo-composable";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import { useHead } from "@vueuse/head";
import { AbsintheGraphQLErrors } from "@/types/errors.model";
@ -221,6 +221,7 @@ type errorMessage = { type: errorType; message: string };
type credentialsType = { email: string; password: string; locale: string };
const { t, locale } = useI18n({ useScope: "global" });
const route = useRoute();
const router = useRouter();
const { result: configResult } = useQuery<{ config: IConfig }>(CONFIG);
@ -229,17 +230,10 @@ const config = computed(() => configResult.value?.config);
const showGravatar = ref(false);
const props = withDefaults(
defineProps<{
email?: string;
password?: string;
}>(),
{ email: "", password: "" }
);
const credentials = reactive<credentialsType>({
email: props.email,
password: props.password,
email: typeof route.query.email === "string" ? route.query.email : "",
password:
typeof route.query.password === "string" ? route.query.password : "",
locale: "en",
});

View File

@ -36,7 +36,7 @@ test("Login has everything we need", async ({ page }) => {
await page.goBack();
await registerLink.click();
await page.waitForURL("/register/user");
await page.waitForURL("/register/user?default_email=&default_password=");
expect(page.url()).toContain("/register/user");
await page.goBack();
});

View File

@ -80,8 +80,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
)
# This one should have a privacy setting
field :organized_events, :paginated_event_list,
meta: [private: true, rule: :"read:group:events"] do
field :organized_events, :paginated_event_list do
arg(:after_datetime, :datetime,
default_value: nil,
description: "Filter events that begin after this datetime"

View File

@ -85,7 +85,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
Language information
"""
object :language do
meta(:authorize, :administrator)
meta(:authorize, :all)
field(:code, :string, description: "The iso-639-3 language code")
field(:name, :string, description: "The language name")
end

View File

@ -51,7 +51,8 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Discussion,
rule: :"read:group:discussions"
rule: :"read:group:discussions",
args: %{slug: :slug}
)
resolve(&Discussion.get_discussion/3)