import { styled } from '@mui/material/styles';
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
import Promise from 'bluebird';
import { ClientContext, GraphQLClient, useMutation, useQuery } from 'graphql-hooks';
import { trim } from 'lodash';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import CachedIcon from '@mui/icons-material/Cached';
import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder';
import FolderIcon from '@mui/icons-material/Folder';
import { Button, LinearProgress, List, ListItemButton, ListItemIcon, ListItemText, TextField } from '@mui/material';
import { GITHUB_GRAPHQL_API } from '@/constants/auth';
import { useUserInfoObservable } from '@services/auth/hooks';
import { IGithubSearchNode, IGithubSearchRepoQuery } from './interfaces';
const RepoSearchContainer = styled('div')`
margin-top: 20px;
`;
const RepoSearchInput = styled(TextField)``;
const ReloadButton = styled(Button)`
white-space: nowrap;
width: 100%;
`;
const SEARCH_REPO_QUERY = `
query SearchRepo($queryString: String!, $login: String!) {
repositoryOwner(login: $login) {
id
}
search(query: $queryString, type: REPOSITORY, first: 10) {
repositoryCount
edges {
node {
... on Repository {
name
url
}
}
}
}
}
`;
const CREATE_REPO_MUTATION = `
mutation CreateRepository($repoName: String!, $homepageUrl: URI, $ownerId: ID!, $visibility: RepositoryVisibility!) {
createRepository (input: {
name: $repoName,
description: "A non-linear Wiki created using TidGi-Desktop.",
hasIssuesEnabled: true,
hasWikiEnabled: false,
homepageUrl: $homepageUrl,
ownerId: $ownerId,
visibility: $visibility
}) {
repository {
name
homepageUrl
}
}
}
`;
interface Props {
githubWikiUrl: string;
githubWikiUrlSetter: (value: string) => void;
isCreateMainWorkspace: boolean;
wikiFolderNameSetter?: (value: string) => void;
}
export default function SearchGithubRepo(props: Props): React.JSX.Element {
const userInfos = useUserInfoObservable();
const githubUsername = userInfos?.['github-userName'];
const accessToken = userInfos?.['github-token'];
const { t } = useTranslation();
const graphqlClient = useMemo(
() =>
new GraphQLClient({
url: GITHUB_GRAPHQL_API,
}),
[],
);
useEffect(() => {
graphqlClient.setHeader('Authorization', accessToken === undefined ? '' : `Bearer ${accessToken}`);
}, [accessToken, graphqlClient]);
if (githubUsername === '' || githubUsername === undefined || accessToken === '' || accessToken === undefined) {
return {t('AddWorkspace.WaitForLogin')};
} else {
return (
);
}
}
interface ITokens {
accessToken: string;
githubUsername: string;
}
function SearchGithubRepoResultList({
githubWikiUrl,
githubWikiUrlSetter,
wikiFolderNameSetter,
isCreateMainWorkspace,
githubUsername,
accessToken,
}: Props & ITokens): React.JSX.Element {
const { t } = useTranslation();
const onSelectRepo = useCallback(
(url: string, name: string) => {
githubWikiUrlSetter(url);
if (typeof wikiFolderNameSetter === 'function') {
wikiFolderNameSetter(name);
}
},
[githubWikiUrlSetter, wikiFolderNameSetter],
);
const [githubRepoSearchString, githubRepoSearchStringSetter] = useState('wiki');
const loadCount = 10;
// eslint-disable-next-line @typescript-eslint/unbound-method
const { loading, error, data, refetch } = useQuery(SEARCH_REPO_QUERY, {
variables: {
first: loadCount,
queryString: `user:${githubUsername} ${githubRepoSearchString}`,
login: githubUsername,
},
skipCache: true,
});
const refetchDebounced = useDebouncedCallback(refetch, [], 300);
// clear list on logout, which will cause accessToken change
useEffect(() => {
const timeoutHandle = setTimeout(async () => {
await refetchDebounced();
}, 100);
return () => {
clearTimeout(timeoutHandle);
};
}, [githubUsername, accessToken]);
// try refetch on error
const [retryInterval, retryIntervalSetter] = useState(100);
useEffect(() => {
if (error !== undefined && githubUsername.length > 0 && accessToken.length > 0) {
const timeoutHandle = setTimeout(async () => {
await refetchDebounced();
retryIntervalSetter(retryInterval * 10);
}, retryInterval);
return () => {
clearTimeout(timeoutHandle);
};
}
return undefined;
}, [error, githubUsername, accessToken, retryInterval]);
const [createRepository] = useMutation(CREATE_REPO_MUTATION);
const repositoryCount = data?.search.repositoryCount;
const repoList: IGithubSearchNode[] = useMemo(
() => (data !== undefined && (repositoryCount ?? 0) > 0 ? data.search.edges.map(({ node }) => node) : []),
[data, repositoryCount],
);
// auto select first one after first search
useEffect(() => {
if (githubWikiUrl.length === 0 && repoList.length > 0) {
onSelectRepo(repoList[0].url, repoList[0].name);
}
}, [repoList, githubWikiUrl, onSelectRepo]);
const [isCreatingRepo, isCreatingRepoSetter] = useState(false);
const githubUserID = data?.repositoryOwner.id;
const wikiUrlToCreate = `https://github.com/${githubUsername ?? '???'}/${githubRepoSearchString}`;
const isCreateNewRepo = trim(githubWikiUrl) === wikiUrlToCreate;
const githubPagesUrl = `https://${githubUsername ?? '???'}.github.io/${githubRepoSearchString}`;
let helperText = '';
if (error !== undefined) {
helperText = t('AddWorkspace.CanNotLoadList');
}
if (repositoryCount !== undefined && repositoryCount > loadCount) {
helperText = t('AddWorkspace.OmitMoreResult', { loadCount });
}
return (
) => {
githubRepoSearchStringSetter(event.target.value);
}}
label={t('AddWorkspace.SearchGithubRepoName')}
value={githubRepoSearchString}
helperText={helperText}
/>
{(loading || isCreatingRepo) && }
{repoList.map(({ name, url }) => (
{
onSelectRepo(url, name);
}}
selected={trim(githubWikiUrl) === trim(url)}
>
))}
{error === undefined && !loading && !isCreatingRepo && !repoList.some(({ url }) => trim(url) === wikiUrlToCreate) && githubRepoSearchString !== '' && (
{
isCreatingRepoSetter(true);
await createRepository({
variables: {
repoName: githubRepoSearchString,
homePageUrl: isCreateMainWorkspace ? githubPagesUrl : undefined,
ownerId: githubUserID,
visibility: isCreateMainWorkspace ? 'PUBLIC' : 'PRIVATE',
},
});
// wait for Github update their db
await Promise.delay(1000);
await refetchDebounced();
isCreatingRepoSetter(false);
githubWikiUrlSetter(wikiUrlToCreate);
}}
selected={isCreateNewRepo}
>
)}
{repoList.length === 0 && (
} onClick={() => void refetchDebounced()}>
{t('AddWorkspace.Reload')}
)}
);
}