This commit is contained in:
Morten Olsen
2023-06-16 11:10:50 +02:00
commit bc0d501d98
163 changed files with 16458 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import { Widget } from '@refocus/sdk';
import githubProfileWidget from './profile/index.widget';
import pullRequest from './pull-request/index.widget';
import pullRequstComments from './pull-request-comments/index.widget';
const github = [
githubProfileWidget,
pullRequest,
pullRequstComments,
] satisfies Widget<any>[];
export { github };

View File

@@ -0,0 +1,28 @@
import { useCallback, useState } from 'react';
import { Props } from './schema';
type EditorProps = {
value?: Props;
save: (data: Props) => void;
};
const Editor: React.FC<EditorProps> = ({ value, save }) => {
const [data, setData] = useState<Props>(value || { username: '' });
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setData((data) => ({ ...data, [e.target.name]: e.target.value }));
}, []);
const handleSave = useCallback(() => {
save(data);
}, [data, save]);
return (
<div>
<input name="username" value={data.username} onChange={handleChange} />
<button onClick={handleSave}>Save</button>
</div>
);
};
export { Editor };

View File

@@ -0,0 +1,30 @@
import { useGithubQuery, withGithub } from '@refocus/sdk';
import { Github } from '@refocus/ui';
import { useEffect } from 'react';
type GithubProfileProps = {
username: string;
};
type QueryData = {
username: string;
};
const GithubProfile = withGithub<GithubProfileProps>(({ username }) => {
const user = useGithubQuery(async (client, params: QueryData) => {
const nextUser = await client.rest.users.getByUsername({
username: params.username,
});
return nextUser.data;
});
useEffect(() => {
user.fetch({ username });
}, [username]);
if (!user.data) return null;
return <Github.Profile profile={user.data} />;
}, Github.NotLoggedIn);
export { GithubProfile };

View File

@@ -0,0 +1,14 @@
import { GithubProfile } from '.';
import { Widget } from '@refocus/sdk';
import { schema } from './schema';
import { Editor } from './editor';
const githubProfileWidget: Widget<typeof schema> = {
name: 'Github Profile',
id: 'github.profile',
schema,
component: GithubProfile,
edit: Editor,
};
export default githubProfileWidget;

View File

@@ -0,0 +1,10 @@
import { Type, Static } from '@sinclair/typebox';
const schema = Type.Object({
username: Type.String(),
});
type Props = Static<typeof schema>;
export type { Props };
export { schema };

View File

@@ -0,0 +1,44 @@
import { useCallback, useState } from 'react';
import { Props } from './schema';
type EditorProps = {
value?: Props;
save: (data: Props) => void;
};
const Edit: React.FC<EditorProps> = ({ value, save }) => {
const [owner, setOwner] = useState(value?.owner || '');
const [repo, setRepo] = useState(value?.repo || '');
const [pr, setPr] = useState(value?.pr || '');
const handleSave = useCallback(() => {
save({
owner,
repo,
pr: typeof pr === 'string' ? parseInt(pr, 10) : pr,
});
}, [owner, repo, pr, save]);
return (
<div>
<input
placeholder="Owner"
value={owner}
onChange={(e) => setOwner(e.target.value)}
/>
<input
placeholder="Repo"
value={repo}
onChange={(e) => setRepo(e.target.value)}
/>
<input
placeholder="PR"
value={pr}
onChange={(e) => setPr(e.target.value)}
/>
<button onClick={handleSave}>Save</button>
</div>
);
};
export { Edit };

View File

@@ -0,0 +1,28 @@
import { Widget } from '@refocus/sdk';
import { SiGithub } from 'react-icons/si';
import { schema } from './schema';
import { Edit } from './edit';
import { View } from './view';
const widget: Widget<typeof schema> = {
name: 'Github Pull Request Comments',
id: 'github.pull-request-comments',
icon: <SiGithub />,
description: 'Display the 5 latest comments on a Github pull request',
parseUrl: (url) => {
if (url.hostname !== 'github.com') {
return;
}
const pathParts = url.pathname.split('/').filter(Boolean);
const [owner, repo, type, pr] = pathParts.slice(0);
if (type !== 'pull' || !pr) {
return;
}
return { owner, repo, pr: parseInt(pr, 10) };
},
schema,
component: View,
edit: Edit,
};
export default widget;

View File

@@ -0,0 +1,12 @@
import { Type, Static } from '@sinclair/typebox';
const schema = Type.Object({
owner: Type.String(),
repo: Type.String(),
pr: Type.Number(),
});
type Props = Static<typeof schema>;
export type { Props };
export { schema };

View File

@@ -0,0 +1,74 @@
import {
useAddWidgetNotification,
useAutoUpdate,
useGithubQuery,
withGithub,
} from '@refocus/sdk';
import { Chat, Github, List } from '@refocus/ui';
import { Props } from './schema';
import { View as PullRequest } from '../pull-request/view';
type QueryData = {
owner: string;
repo: string;
pr: number;
};
const View = withGithub<Props>(({ owner, repo, pr }) => {
const addNotification = useAddWidgetNotification();
const { data, fetch } = useGithubQuery(async (client, params: QueryData) => {
const response = await client.rest.pulls.listReviewComments({
owner: params.owner,
repo: params.repo,
pull_number: params.pr,
});
return response.data.slice(0, 5);
});
useAutoUpdate(
{
interval: 1000 * 60 * 5,
action: async () => fetch({ owner, repo, pr }),
callback: (next, prev) => {
if (prev && next) {
const previousIds = prev.map((comment) => comment.id);
const newComments = next.filter(
(comment) => !previousIds.includes(comment.id),
);
for (const comment of newComments) {
addNotification({
title: `New comments on PR #${pr} in ${owner}/${repo}`,
message: comment.body,
});
}
}
},
},
[owner, repo, pr, addNotification],
);
if (!data) {
return null;
}
return (
<List>
<PullRequest owner={owner} repo={repo} pr={pr} />
{data.map((comment) => (
<Chat.Message
message={{
sender: {
name: comment.user.login,
avatar: comment.user.avatar_url,
},
timestamp: new Date(comment.created_at),
text: comment.body,
}}
key={comment.id}
/>
))}
</List>
);
}, Github.NotLoggedIn);
export { View };

View File

@@ -0,0 +1,44 @@
import { useCallback, useState } from 'react';
import { Props } from './schema';
type EditorProps = {
value?: Props;
save: (data: Props) => void;
};
const Edit: React.FC<EditorProps> = ({ value, save }) => {
const [owner, setOwner] = useState(value?.owner || '');
const [repo, setRepo] = useState(value?.repo || '');
const [pr, setPr] = useState(value?.pr || '');
const handleSave = useCallback(() => {
save({
owner,
repo,
pr: typeof pr === 'string' ? parseInt(pr, 10) : pr,
});
}, [owner, repo, pr, save]);
return (
<div>
<input
placeholder="Owner"
value={owner}
onChange={(e) => setOwner(e.target.value)}
/>
<input
placeholder="Repo"
value={repo}
onChange={(e) => setRepo(e.target.value)}
/>
<input
placeholder="PR"
value={pr}
onChange={(e) => setPr(e.target.value)}
/>
<button onClick={handleSave}>Save</button>
</div>
);
};
export { Edit };

View File

@@ -0,0 +1,28 @@
import { Widget } from '@refocus/sdk';
import { SiGithub } from 'react-icons/si';
import { schema } from './schema';
import { Edit } from './edit';
import { View } from './view';
const widget: Widget<typeof schema> = {
name: 'Github Pull Request',
description: 'Display an info card for a Github pull request',
icon: <SiGithub />,
id: 'github.pull-request',
parseUrl: (url) => {
if (url.hostname !== 'github.com') {
return;
}
const pathParts = url.pathname.split('/').filter(Boolean);
const [owner, repo, type, pr] = pathParts.slice(0);
if (type !== 'pull' || !pr) {
return;
}
return { owner, repo, pr: parseInt(pr, 10) };
},
schema,
component: View,
edit: Edit,
};
export default widget;

View File

@@ -0,0 +1,12 @@
import { Type, Static } from '@sinclair/typebox';
const schema = Type.Object({
owner: Type.String(),
repo: Type.String(),
pr: Type.Number(),
});
type Props = Static<typeof schema>;
export type { Props };
export { schema };

View File

@@ -0,0 +1,36 @@
import { useAutoUpdate, useGithubQuery, withGithub } from '@refocus/sdk';
import { Props } from './schema';
import { Github } from '@refocus/ui';
type QueryData = {
owner: string;
repo: string;
pr: number;
};
const View = withGithub<Props>(({ owner, repo, pr }) => {
const { data, fetch } = useGithubQuery(async (client, params: QueryData) => {
const response = await client.rest.pulls.get({
owner: params.owner,
repo: params.repo,
pull_number: params.pr,
});
return response.data;
});
useAutoUpdate(
{
interval: 1000 * 60 * 5,
action: async () => fetch({ owner, repo, pr }),
},
[owner, repo, pr],
);
if (!data) {
return null;
}
return <Github.PullRequest pullRequest={data} />;
}, Github.NotLoggedIn);
export { View };