pl-fe: cleanup, avoid parsing schemas more than once
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
c43178fc09
commit
859149230e
28 changed files with 52 additions and 64 deletions
|
@ -133,7 +133,7 @@
|
|||
"multiselect-react-dropdown": "^2.0.25",
|
||||
"object-to-formdata": "^4.5.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pl-api": "^0.0.25",
|
||||
"pl-api": "^0.0.26",
|
||||
"postcss": "^8.4.29",
|
||||
"process": "^0.11.10",
|
||||
"punycode": "^2.1.1",
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { fetchRelationships } from 'pl-fe/actions/accounts';
|
||||
import { importFetchedAccount, importFetchedAccounts, importFetchedStatuses } from 'pl-fe/actions/importer';
|
||||
import { filterBadges, getTagDiff } from 'pl-fe/utils/badges';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { type Account as BaseAccount } from 'pl-api';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
|
@ -10,6 +9,8 @@ import { type Account, normalizeAccount } from 'pl-fe/normalizers';
|
|||
import { useAccountScrobble } from './useAccountScrobble';
|
||||
import { useRelationship } from './useRelationship';
|
||||
|
||||
import type { Account as BaseAccount } from 'pl-api';
|
||||
|
||||
interface UseAccountOpts {
|
||||
withRelationship?: boolean;
|
||||
withScrobble?: boolean;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { accountSchema, type Account as BaseAccount } from 'pl-api';
|
||||
import { useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
|
@ -10,6 +9,8 @@ import { type Account, normalizeAccount } from 'pl-fe/normalizers';
|
|||
import { useAccountScrobble } from './useAccountScrobble';
|
||||
import { useRelationship } from './useRelationship';
|
||||
|
||||
import type { Account as BaseAccount } from 'pl-api';
|
||||
|
||||
interface UseAccountLookupOpts {
|
||||
withRelationship?: boolean;
|
||||
withScrobble?: boolean;
|
||||
|
@ -26,7 +27,7 @@ const useAccountLookup = (acct: string | undefined, opts: UseAccountLookupOpts =
|
|||
Entities.ACCOUNTS,
|
||||
(account) => account.acct.toLowerCase() === acct?.toLowerCase(),
|
||||
() => client.accounts.lookupAccount(acct!),
|
||||
{ schema: accountSchema, enabled: !!acct, transform: normalizeAccount },
|
||||
{ enabled: !!acct, transform: normalizeAccount },
|
||||
);
|
||||
|
||||
const {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { type Relationship, relationshipSchema } from 'pl-api';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntity } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
||||
import type { Relationship } from 'pl-api';
|
||||
|
||||
interface UseRelationshipOpts {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
@ -18,7 +19,7 @@ const useRelationship = (accountId: string | undefined, opts: UseRelationshipOpt
|
|||
() => client.accounts.getRelationships([accountId!]),
|
||||
{
|
||||
enabled: enabled && !!accountId,
|
||||
schema: z.array(relationshipSchema).nonempty().transform(arr => arr[0]),
|
||||
schema: z.any().transform(arr => arr[0]),
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { type Relationship, relationshipSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useBatchedEntities } from 'pl-fe/entity-store/hooks/useBatchedEntities';
|
||||
import { useClient, useLoggedIn } from 'pl-fe/hooks';
|
||||
|
||||
import type { Relationship } from 'pl-api';
|
||||
|
||||
const useRelationships = (listKey: string[], accountIds: string[]) => {
|
||||
const client = useClient();
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
|
@ -14,7 +14,7 @@ const useRelationships = (listKey: string[], accountIds: string[]) => {
|
|||
[Entities.RELATIONSHIPS, ...listKey],
|
||||
accountIds,
|
||||
fetchRelationships,
|
||||
{ schema: relationshipSchema, enabled: isLoggedIn },
|
||||
{ enabled: isLoggedIn },
|
||||
);
|
||||
|
||||
return { relationships, ...result };
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { groupSchema, type Group as BaseGroup } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { normalizeGroup, type Group } from 'pl-fe/normalizers';
|
||||
|
||||
import type { Group as BaseGroup } from 'pl-api';
|
||||
|
||||
interface CreateGroupParams {
|
||||
display_name: string;
|
||||
note?: string;
|
||||
|
@ -21,7 +21,7 @@ const useCreateGroup = () => {
|
|||
const { createEntity, ...rest } = useCreateEntity<BaseGroup, Group, CreateGroupParams>(
|
||||
[Entities.GROUPS, 'search', ''],
|
||||
(params: CreateGroupParams) => client.experimental.groups.createGroup(params),
|
||||
{ schema: groupSchema, transform: normalizeGroup },
|
||||
{ transform: normalizeGroup },
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { groupMemberSchema, type Group, type GroupMember as GroupMember, type GroupRole } from 'pl-api';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
|
@ -6,13 +5,15 @@ import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
|||
import { useClient } from 'pl-fe/hooks';
|
||||
import { normalizeGroupMember } from 'pl-fe/normalizers';
|
||||
|
||||
import type { Group, GroupMember as GroupMember, GroupRole } from 'pl-api';
|
||||
|
||||
const useDemoteGroupMember = (group: Pick<Group, 'id'>, groupMember: Pick<GroupMember, 'id'>) => {
|
||||
const client = useClient();
|
||||
|
||||
const { createEntity } = useCreateEntity(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
({ account_ids, role }: { account_ids: string[]; role: GroupRole }) => client.experimental.groups.demoteGroupUsers(group.id, account_ids, role),
|
||||
{ schema: z.array(groupMemberSchema).transform((arr) => arr[0]), transform: normalizeGroupMember },
|
||||
{ schema: z.any().transform((arr) => arr[0]), transform: normalizeGroupMember },
|
||||
);
|
||||
|
||||
return createEntity;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { type Group as BaseGroup, groupSchema } from 'pl-api';
|
||||
import { useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
|
@ -9,6 +8,8 @@ import { normalizeGroup, type Group } from 'pl-fe/normalizers';
|
|||
|
||||
import { useGroupRelationship } from './useGroupRelationship';
|
||||
|
||||
import type { Group as BaseGroup } from 'pl-api';
|
||||
|
||||
const useGroup = (groupId: string, refetch = true) => {
|
||||
const client = useClient();
|
||||
const history = useHistory();
|
||||
|
@ -17,7 +18,6 @@ const useGroup = (groupId: string, refetch = true) => {
|
|||
[Entities.GROUPS, groupId],
|
||||
() => client.experimental.groups.getGroup(groupId),
|
||||
{
|
||||
schema: groupSchema,
|
||||
transform: normalizeGroup,
|
||||
refetch,
|
||||
enabled: !!groupId,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { statusSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntities } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
@ -11,7 +9,7 @@ const useGroupMedia = (groupId: string) => {
|
|||
return useEntities(
|
||||
[Entities.STATUSES, 'groupMedia', groupId],
|
||||
() => client.timelines.groupTimeline(groupId, { only_media: true }),
|
||||
{ schema: statusSchema, transform: normalizeStatus })
|
||||
{ transform: normalizeStatus })
|
||||
;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { groupMemberSchema, type GroupMember as BaseGroupMember, type GroupRoles } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntities } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { normalizeGroupMember, type GroupMember } from 'pl-fe/normalizers';
|
||||
|
||||
import type { GroupMember as BaseGroupMember, GroupRoles } from 'pl-api';
|
||||
|
||||
const useGroupMembers = (groupId: string, role: GroupRoles) => {
|
||||
const client = useClient();
|
||||
|
||||
const { entities, ...result } = useEntities<BaseGroupMember, GroupMember>(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupId, role],
|
||||
() => client.experimental.groups.getGroupMemberships(groupId, role),
|
||||
{ schema: groupMemberSchema, transform: normalizeGroupMember },
|
||||
{ transform: normalizeGroupMember },
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { accountSchema, GroupRoles } from 'pl-api';
|
||||
import { GroupRoles } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useDismissEntity, useEntities } from 'pl-fe/entity-store/hooks';
|
||||
|
@ -19,7 +19,6 @@ const useGroupMembershipRequests = (groupId: string) => {
|
|||
path,
|
||||
() => client.experimental.groups.getGroupMembershipRequests(groupId),
|
||||
{
|
||||
schema: accountSchema,
|
||||
transform: normalizeAccount,
|
||||
enabled: relationship?.role === GroupRoles.OWNER || relationship?.role === GroupRoles.ADMIN,
|
||||
},
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { type GroupRelationship, groupRelationshipSchema } from 'pl-api';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntity } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
||||
import type { GroupRelationship } from 'pl-api';
|
||||
|
||||
const useGroupRelationship = (groupId: string | undefined) => {
|
||||
const client = useClient();
|
||||
|
||||
|
@ -13,7 +14,7 @@ const useGroupRelationship = (groupId: string | undefined) => {
|
|||
() => client.experimental.groups.getGroupRelationships([groupId!]),
|
||||
{
|
||||
enabled: !!groupId,
|
||||
schema: z.array(groupRelationshipSchema).nonempty().transform(arr => arr[0]),
|
||||
schema: z.any().transform(arr => arr[0]),
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { type GroupRelationship, groupRelationshipSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useBatchedEntities } from 'pl-fe/entity-store/hooks/useBatchedEntities';
|
||||
import { useClient, useLoggedIn } from 'pl-fe/hooks';
|
||||
|
||||
import type { GroupRelationship } from 'pl-api';
|
||||
|
||||
const useGroupRelationships = (listKey: string[], groupIds: string[]) => {
|
||||
const client = useClient();
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
|
@ -15,7 +15,7 @@ const useGroupRelationships = (listKey: string[], groupIds: string[]) => {
|
|||
[Entities.RELATIONSHIPS, ...listKey],
|
||||
groupIds,
|
||||
fetchGroupRelationships,
|
||||
{ schema: groupRelationshipSchema, enabled: isLoggedIn },
|
||||
{ enabled: isLoggedIn },
|
||||
);
|
||||
|
||||
return { relationships, ...result };
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { groupSchema, type Group as BaseGroup } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntities } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
@ -8,6 +6,8 @@ import { normalizeGroup, type Group } from 'pl-fe/normalizers';
|
|||
|
||||
import { useGroupRelationships } from './useGroupRelationships';
|
||||
|
||||
import type { Group as BaseGroup } from 'pl-api';
|
||||
|
||||
const useGroups = () => {
|
||||
const client = useClient();
|
||||
const features = useFeatures();
|
||||
|
@ -15,7 +15,7 @@ const useGroups = () => {
|
|||
const { entities, ...result } = useEntities<BaseGroup, Group>(
|
||||
[Entities.GROUPS, 'search', ''],
|
||||
() => client.experimental.groups.getGroups(),
|
||||
{ enabled: features.groups, schema: groupSchema, transform: normalizeGroup },
|
||||
{ enabled: features.groups, transform: normalizeGroup },
|
||||
);
|
||||
const { relationships } = useGroupRelationships(
|
||||
['search', ''],
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { groupMemberSchema } from 'pl-api';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
|
@ -14,7 +13,7 @@ const usePromoteGroupMember = (group: Pick<Group, 'id'>, groupMember: Pick<Group
|
|||
const { createEntity } = useCreateEntity(
|
||||
[Entities.GROUP_MEMBERSHIPS, groupMember.id],
|
||||
({ account_ids, role }: { account_ids: string[]; role: GroupRole }) => client.experimental.groups.promoteGroupUsers(group.id, account_ids, role),
|
||||
{ schema: z.array(groupMemberSchema).transform((arr) => arr[0]), transform: normalizeGroupMember },
|
||||
{ schema: z.any().transform((arr) => arr[0]), transform: normalizeGroupMember },
|
||||
);
|
||||
|
||||
return createEntity;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { groupSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
@ -20,7 +18,7 @@ const useUpdateGroup = (groupId: string) => {
|
|||
const { createEntity, ...rest } = useCreateEntity(
|
||||
[Entities.GROUPS],
|
||||
(params: UpdateGroupParams) => client.experimental.groups.updateGroup(groupId, params),
|
||||
{ schema: groupSchema, transform: normalizeGroup },
|
||||
{ transform: normalizeGroup },
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
// Accounts
|
||||
export { useAccount } from './accounts/useAccount';
|
||||
export { useAccountLookup } from './accounts/useAccountLookup';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { bookmarkFolderSchema, type BookmarkFolder } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntities } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { useFeatures } from 'pl-fe/hooks/useFeatures';
|
||||
|
||||
import type { BookmarkFolder } from 'pl-api';
|
||||
|
||||
const useBookmarkFolders = () => {
|
||||
const client = useClient();
|
||||
const features = useFeatures();
|
||||
|
@ -12,7 +12,7 @@ const useBookmarkFolders = () => {
|
|||
const { entities, ...result } = useEntities<BookmarkFolder>(
|
||||
[Entities.BOOKMARK_FOLDERS],
|
||||
() => client.myAccount.getBookmarkFolders(),
|
||||
{ enabled: features.bookmarkFolders, schema: bookmarkFolderSchema },
|
||||
{ enabled: features.bookmarkFolders },
|
||||
);
|
||||
|
||||
const bookmarkFolders = entities;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { bookmarkFolderSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
@ -16,7 +14,6 @@ const useCreateBookmarkFolder = () => {
|
|||
[Entities.BOOKMARK_FOLDERS],
|
||||
(params: CreateBookmarkFolderParams) =>
|
||||
client.myAccount.createBookmarkFolder(params),
|
||||
{ schema: bookmarkFolderSchema },
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { bookmarkFolderSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useCreateEntity } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
|
@ -16,7 +14,6 @@ const useUpdateBookmarkFolder = (folderId: string) => {
|
|||
[Entities.BOOKMARK_FOLDERS],
|
||||
(params: UpdateBookmarkFolderParams) =>
|
||||
client.myAccount.updateBookmarkFolder(folderId, params),
|
||||
{ schema: bookmarkFolderSchema },
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
import { announcementSchema, type Announcement, type AnnouncementReaction, type FollowRelationshipUpdate, type Relationship, type StreamingEvent } from 'pl-api';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { updateConversations } from 'pl-fe/actions/conversations';
|
||||
|
@ -22,6 +20,7 @@ import { updateReactions } from '../announcements/useAnnouncements';
|
|||
|
||||
import { useTimelineStream } from './useTimelineStream';
|
||||
|
||||
import type { Announcement, AnnouncementReaction, FollowRelationshipUpdate, Relationship, StreamingEvent } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const updateAnnouncementReactions = ({ announcement_id: id, name }: AnnouncementReaction) => {
|
||||
|
@ -42,10 +41,10 @@ const updateAnnouncement = (announcement: Announcement) =>
|
|||
let updated = false;
|
||||
|
||||
const result = prevResult.map(value => value.id === announcement.id
|
||||
? (updated = true, announcementSchema.parse(announcement))
|
||||
? (updated = true, announcement)
|
||||
: value);
|
||||
|
||||
if (!updated) return [announcementSchema.parse(announcement), ...result];
|
||||
if (!updated) return [announcement, ...result];
|
||||
});
|
||||
|
||||
const deleteAnnouncement = (announcementId: string) =>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { trendsLinkSchema } from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntities } from 'pl-fe/entity-store/hooks';
|
||||
import { useClient, useFeatures } from 'pl-fe/hooks';
|
||||
|
||||
import type { TrendsLink } from 'pl-api';
|
||||
|
||||
const useTrendingLinks = () => {
|
||||
const client = useClient();
|
||||
const features = useFeatures();
|
||||
|
||||
const { entities, ...rest } = useEntities(
|
||||
const { entities, ...rest } = useEntities<TrendsLink>(
|
||||
[Entities.TRENDS_LINKS],
|
||||
() => client.trends.getTrendingLinks(),
|
||||
{ schema: trendsLinkSchema, enabled: features.trendingLinks },
|
||||
{ enabled: features.trendingLinks },
|
||||
);
|
||||
|
||||
return { trendingLinks: entities, ...rest };
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { type Poll } from 'pl-api';
|
||||
import React from 'react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { Provider } from 'react-redux';
|
||||
|
@ -9,6 +8,8 @@ import { mockStore, render, screen, rootState } from 'pl-fe/jest/test-helpers';
|
|||
|
||||
import PollFooter from './poll-footer';
|
||||
|
||||
import type { Poll } from 'pl-api';
|
||||
|
||||
let poll: Poll = {
|
||||
id: '1',
|
||||
options: [{
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { play, soundCache } from 'pl-fe/utils/sounds';
|
||||
|
||||
import type { Sounds } from 'pl-fe/utils/sounds';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { getFeatures, PLEROMA, TOKI, type Instance } from 'pl-api';
|
||||
|
||||
import type { RootState } from 'pl-fe/store';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { buildStatus } from 'pl-fe/jest/factory';
|
||||
|
||||
import {
|
||||
|
|
|
@ -8378,10 +8378,10 @@ pkg-types@^1.0.3:
|
|||
mlly "^1.2.0"
|
||||
pathe "^1.1.0"
|
||||
|
||||
pl-api@^0.0.25:
|
||||
version "0.0.25"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-0.0.25.tgz#d950d902c4bfe268d9de66611372dbd589939ca4"
|
||||
integrity sha512-gBS/5aN47w0ZcE7I0gBQMkngMwB9DSHvH40dM5XsZGPzUw7f3VECHM+tZyTlZI13rdVC9xJlVQTPX1h9KmGUOg==
|
||||
pl-api@^0.0.26:
|
||||
version "0.0.26"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-0.0.26.tgz#82b20fa0500a22efaae643782ac917c725eef4ff"
|
||||
integrity sha512-vbH11YmElHNpQZWM5G6nvgyQDp4q+G6IoMKqa7ek156UbuDBuZx/de2LYjlCJQ9nGtTzwNoOYRdtbLcshDKyiw==
|
||||
dependencies:
|
||||
blurhash "^2.0.5"
|
||||
http-link-header "^1.1.3"
|
||||
|
|
Loading…
Reference in a new issue