Skip to content

Commit be078bb

Browse files
authored
Merge branch 'dev' into fix/followup_pr120
2 parents b5b2317 + c3b99f8 commit be078bb

File tree

27 files changed

+442
-290
lines changed

27 files changed

+442
-290
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ API_GENCOMMENT=/** @generated THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FI
33

44
REACT_APP_API_URL=https://api.github.com/graphql
55
# Используется для кодогенерации, Apollo Client плагина
6-
REACT_APP_ACCESS_TOKEN=284b8e0b1b4882b0b4a1fd6321f3136fcd309eac
6+
REACT_APP_ACCESS_TOKEN=9221b1b5ba4b0de72eed80b09602ed7b4c73c222
77

88
# Firebase section
99
REACT_APP_FIREBASE_apiKey=AIzaSyABlBQc-tjCRKWwBj8jTTrMiT2M2UKiJpk

src/features/repo-list/hooks.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { useState } from "react";
2+
import { useDebounce } from "shared/hooks";
3+
import { alert } from "shared/helpers";
4+
import * as Queries from "./queries.gen";
5+
import * as Params from "./params";
6+
7+
export const PAGE_SIZE = 30;
8+
9+
/**
10+
* @hook star/unstar логика
11+
* TODO: add debounced loading/disabled logic
12+
* TODO: add errors catching
13+
*/
14+
export const useStarring = (variables?: Queries.ReposQueryVariables) => {
15+
// FIXME: simplify?
16+
const [addStar] = Queries.useAddStarMutation();
17+
const [removeStar] = Queries.useRemoveStarMutation();
18+
// FIXME: temp, impl better later
19+
const [loadingId, setLoadingId] = useState<string | null>(null);
20+
const debouncedLoadingId = useDebounce(loadingId);
21+
22+
// FIXME: more strict
23+
const handle = async (repoId?: string | null, viewerHasStarred?: boolean) => {
24+
const actionType = viewerHasStarred ? "unstar" : "star";
25+
if (!repoId) {
26+
alert.error(`Failed to ${actionType} repo, try later`);
27+
return;
28+
}
29+
setLoadingId(repoId);
30+
// request
31+
const action = viewerHasStarred ? removeStar : addStar;
32+
await action({
33+
variables: { starrableId: repoId },
34+
refetchQueries: [{ variables, query: Queries.ReposDocument }],
35+
});
36+
// fulfilled
37+
alert.success(`Successfully ${actionType}red!`);
38+
setLoadingId(null);
39+
};
40+
41+
return { handle, loadingId, debouncedLoadingId };
42+
};
43+
44+
/**
45+
* @hook Работа с фильтрацией по affilations, пагинацией
46+
*/
47+
export const useFilters = () => {
48+
const { tab, setTab, tabEnum } = Params.useTabParam();
49+
const { after, before, setCursor } = Params.useCursorParam();
50+
51+
/**
52+
* Обработчик выбора вкладки
53+
* @remark Реактивно сбрасываем пагинацию, при смене вкладки
54+
*/
55+
const handleTabClick: typeof setTab = (type) => {
56+
setTab(type);
57+
setCursor({});
58+
};
59+
60+
/**
61+
* Обработчик пагинации
62+
* @remark Явно определяем вкладку, чтобы она точно была задана (для соответствия ссылок features/origin)
63+
*/
64+
const handlePaginationClick: typeof setCursor = (pageInfo) => {
65+
setTab(tab);
66+
setCursor(pageInfo);
67+
};
68+
69+
return {
70+
config: {
71+
tab,
72+
ownerAffiliations: [tabEnum],
73+
after,
74+
before,
75+
/**
76+
* @variant (!before, !after) => Первый вход, фетчим первые {PAGE_SIZE}
77+
* @variant (after, !before) => След. страница, фетчим след. первые {PAGE_SIZE}
78+
* @variant (!after, before) => Пред. страница, фетчим пред. последние {PAGE_SIZE}
79+
* @variant (after, before) => (невозможна из-за реализации)
80+
*/
81+
first: (!before && PAGE_SIZE) || undefined,
82+
last: (before && PAGE_SIZE) || undefined,
83+
},
84+
handleTabClick,
85+
handlePaginationClick,
86+
};
87+
};

src/features/repo-list/index.tsx

Lines changed: 18 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,70 +2,32 @@ import React from "react";
22
import { Repo, Tabs, SimplePagination, Card } from "shared/components";
33
import { str, dom } from "shared/helpers";
44
import { useReposQuery } from "./queries.gen";
5-
import * as Params from "./params";
5+
import { useFilters, PAGE_SIZE, useStarring } from "./hooks";
6+
import { tabsMap } from "./params";
67
import "./index.scss";
78

89
type Props = {
910
username: string;
1011
};
1112

12-
const PAGE_SIZE = 30;
13-
1413
/**
15-
* @hook Работа с фильтрацией по affilations, пагинацией
14+
* @feature Список репозиториев пользователя
15+
* FIXME: rename to UserRepoList? (coz - user as dep)
1616
*/
17-
const useFilters = () => {
18-
const { tab, setTab, tabEnum } = Params.useTabParam();
19-
const { after, before, setCursor } = Params.useCursorParam();
20-
21-
/**
22-
* Обработчик выбора вкладки
23-
* @remark Реактивно сбрасываем пагинацию, при смене вкладки
24-
*/
25-
const handleTabClick: typeof setTab = (type) => {
26-
setTab(type);
27-
setCursor({});
28-
};
29-
30-
/**
31-
* Обработчик пагинации
32-
* @remark Явно определяем вкладку, чтобы она точно была задана (для соответствия ссылок features/origin)
33-
*/
34-
const handlePaginationClick: typeof setCursor = (pageInfo) => {
35-
setTab(tab);
36-
setCursor(pageInfo);
37-
};
38-
39-
return {
40-
config: {
41-
tab,
42-
ownerAffiliations: [tabEnum],
43-
after,
44-
before,
45-
/**
46-
* @variant (!before, !after) => Первый вход, фетчим первые {PAGE_SIZE}
47-
* @variant (after, !before) => След. страница, фетчим след. первые {PAGE_SIZE}
48-
* @variant (!after, before) => Пред. страница, фетчим пред. последние {PAGE_SIZE}
49-
* @variant (after, before) => (невозможна из-за реализации)
50-
*/
51-
first: (!before && PAGE_SIZE) || undefined,
52-
last: (before && PAGE_SIZE) || undefined,
53-
},
54-
handleTabClick,
55-
handlePaginationClick,
56-
};
57-
};
58-
// FIXME: rename to UserRepoList? (coz - user as dep)
5917
const RepoList = ({ username }: Props) => {
6018
const { handleTabClick, handlePaginationClick, config } = useFilters();
61-
const { data, loading } = useReposQuery({ variables: { login: username, ...config } });
19+
const { data, loading, variables } = useReposQuery({
20+
variables: { login: username, ...config },
21+
});
22+
// TODO: transmit id and viewerHasStarred of nodes to handler func
23+
const starring = useStarring(variables);
6224
const { pageInfo, totalCount = 0, nodes } = data?.user?.repositories || {};
6325
const length = nodes?.length;
6426

6527
return (
6628
<div className="repo-list">
6729
<Tabs className="repo-list__tabs">
68-
{Object.keys(Params.tabsMap).map((type) => (
30+
{Object.keys(tabsMap).map((type) => (
6931
<Tabs.Item
7032
key={type}
7133
name={str.capitalize(type)}
@@ -81,7 +43,14 @@ const RepoList = ({ username }: Props) => {
8143
{/* NOTE: А то все {PAGE_SIZE} плейсхолдеров слишком много */}
8244
{loading && <Card.SkeletonGroup amount={10} />}
8345
{length !== 0 ? (
84-
data?.user?.repositories.nodes?.map((node) => <Repo key={node?.id} {...node} />)
46+
data?.user?.repositories.nodes?.map((node) => (
47+
<Repo
48+
onStarring={() => starring.handle(node?.id, node?.viewerHasStarred)}
49+
key={node?.id}
50+
data={node}
51+
loading={starring.debouncedLoadingId === node?.id}
52+
/>
53+
))
8554
) : (
8655
<h2 className="repo-list__placeholder">
8756
{username} doesn’t have any repositories yet.

src/features/repo-list/queries.gen.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ export type ReposQueryVariables = Types.Exact<{
1515

1616
export type ReposQuery = { readonly user?: Types.Maybe<{ readonly id: string, readonly repositories: { readonly totalCount: number, readonly pageInfo: { readonly endCursor?: Types.Maybe<string>, readonly startCursor?: Types.Maybe<string>, readonly hasNextPage: boolean, readonly hasPreviousPage: boolean }, readonly nodes?: Types.Maybe<ReadonlyArray<Types.Maybe<{ readonly id: string, readonly name: string, readonly updatedAt: any, readonly viewerHasStarred: boolean, readonly primaryLanguage?: Types.Maybe<{ readonly color?: Types.Maybe<string>, readonly name: string }>, readonly owner: { readonly login: string } | { readonly login: string } }>>> } }> };
1717

18+
export type AddStarMutationVariables = Types.Exact<{
19+
starrableId: Types.Scalars['ID'];
20+
}>;
21+
22+
23+
export type AddStarMutation = { readonly addStar?: Types.Maybe<{ readonly starrable?: Types.Maybe<{ readonly id: string } | { readonly id: string } | { readonly id: string }> }> };
24+
25+
export type RemoveStarMutationVariables = Types.Exact<{
26+
starrableId: Types.Scalars['ID'];
27+
}>;
28+
29+
30+
export type RemoveStarMutation = { readonly removeStar?: Types.Maybe<{ readonly starrable?: Types.Maybe<{ readonly id: string } | { readonly id: string } | { readonly id: string }> }> };
31+
1832

1933
export const ReposDocument = gql`
2034
query Repos($login: String!, $ownerAffiliations: [RepositoryAffiliation], $after: String, $before: String, $first: Int, $last: Int) {
@@ -75,4 +89,72 @@ export function useReposLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<Repo
7589
}
7690
export type ReposQueryHookResult = ReturnType<typeof useReposQuery>;
7791
export type ReposLazyQueryHookResult = ReturnType<typeof useReposLazyQuery>;
78-
export type ReposQueryResult = Apollo.QueryResult<ReposQuery, ReposQueryVariables>;
92+
export type ReposQueryResult = Apollo.QueryResult<ReposQuery, ReposQueryVariables>;
93+
export const AddStarDocument = gql`
94+
mutation AddStar($starrableId: ID!) {
95+
addStar(input: {starrableId: $starrableId}) {
96+
starrable {
97+
id
98+
}
99+
}
100+
}
101+
`;
102+
export type AddStarMutationFn = Apollo.MutationFunction<AddStarMutation, AddStarMutationVariables>;
103+
104+
/**
105+
* __useAddStarMutation__
106+
*
107+
* To run a mutation, you first call `useAddStarMutation` within a React component and pass it any options that fit your needs.
108+
* When your component renders, `useAddStarMutation` returns a tuple that includes:
109+
* - A mutate function that you can call at any time to execute the mutation
110+
* - An object with fields that represent the current status of the mutation's execution
111+
*
112+
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
113+
*
114+
* @example
115+
* const [addStarMutation, { data, loading, error }] = useAddStarMutation({
116+
* variables: {
117+
* starrableId: // value for 'starrableId'
118+
* },
119+
* });
120+
*/
121+
export function useAddStarMutation(baseOptions?: Apollo.MutationHookOptions<AddStarMutation, AddStarMutationVariables>) {
122+
return Apollo.useMutation<AddStarMutation, AddStarMutationVariables>(AddStarDocument, baseOptions);
123+
}
124+
export type AddStarMutationHookResult = ReturnType<typeof useAddStarMutation>;
125+
export type AddStarMutationResult = Apollo.MutationResult<AddStarMutation>;
126+
export type AddStarMutationOptions = Apollo.BaseMutationOptions<AddStarMutation, AddStarMutationVariables>;
127+
export const RemoveStarDocument = gql`
128+
mutation RemoveStar($starrableId: ID!) {
129+
removeStar(input: {starrableId: $starrableId}) {
130+
starrable {
131+
id
132+
}
133+
}
134+
}
135+
`;
136+
export type RemoveStarMutationFn = Apollo.MutationFunction<RemoveStarMutation, RemoveStarMutationVariables>;
137+
138+
/**
139+
* __useRemoveStarMutation__
140+
*
141+
* To run a mutation, you first call `useRemoveStarMutation` within a React component and pass it any options that fit your needs.
142+
* When your component renders, `useRemoveStarMutation` returns a tuple that includes:
143+
* - A mutate function that you can call at any time to execute the mutation
144+
* - An object with fields that represent the current status of the mutation's execution
145+
*
146+
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
147+
*
148+
* @example
149+
* const [removeStarMutation, { data, loading, error }] = useRemoveStarMutation({
150+
* variables: {
151+
* starrableId: // value for 'starrableId'
152+
* },
153+
* });
154+
*/
155+
export function useRemoveStarMutation(baseOptions?: Apollo.MutationHookOptions<RemoveStarMutation, RemoveStarMutationVariables>) {
156+
return Apollo.useMutation<RemoveStarMutation, RemoveStarMutationVariables>(RemoveStarDocument, baseOptions);
157+
}
158+
export type RemoveStarMutationHookResult = ReturnType<typeof useRemoveStarMutation>;
159+
export type RemoveStarMutationResult = Apollo.MutationResult<RemoveStarMutation>;
160+
export type RemoveStarMutationOptions = Apollo.BaseMutationOptions<RemoveStarMutation, RemoveStarMutationVariables>;

src/features/repo-list/queries.gql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,21 @@ query Repos ($login: String!, $ownerAffiliations: [RepositoryAffiliation], $afte
2525
}
2626
}
2727
}
28+
29+
# FIXME: simplify
30+
31+
mutation AddStar($starrableId: ID!) {
32+
addStar(input: {starrableId: $starrableId }) {
33+
starrable {
34+
id
35+
}
36+
}
37+
}
38+
39+
mutation RemoveStar($starrableId: ID!) {
40+
removeStar(input: {starrableId: $starrableId }) {
41+
starrable {
42+
id
43+
}
44+
}
45+
}

src/features/search/results/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ const SearchResults = () => {
8383
{loading && <Card.SkeletonGroup amount={PAGE_SIZE} />}
8484
{data?.search.nodes?.map((node) => (
8585
<ResultItem key={node?.id} className={(node as any).__typename}>
86-
{isRepoSearch && <Repo {...node} format="owner-repo" />}
86+
{isRepoSearch && <Repo data={node} format="owner-repo" />}
8787
{/* !!! FIXME: simplify */}
8888
{isUserSearch &&
8989
((node as any)?.__typename === "Organization" ? (
90-
<Org {...node} />
90+
<Org data={node} />
9191
) : (
92-
<User {...node} />
92+
<User data={node} />
9393
))}
9494
</ResultItem>
9595
))}

0 commit comments

Comments
 (0)