feat: markdown, github file and UI improvements

This commit is contained in:
Morten Olsen
2023-06-19 16:33:42 +02:00
parent 4ec88717b0
commit 5c23cef034
27 changed files with 767 additions and 26 deletions

View File

@@ -0,0 +1,58 @@
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 [branch, setBranch] = useState(value?.branch || '');
const [path, setPath] = useState(value?.path || '');
const [highlight, setHighlight] = useState(value?.highlight || '');
const handleSave = useCallback(() => {
save({
owner,
repo,
branch,
path,
highlight,
});
}, [owner, repo, branch, path, highlight, 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="Branch"
value={branch}
onChange={(e) => setBranch(e.target.value)}
/>
<input
placeholder="Path"
value={path}
onChange={(e) => setPath(e.target.value)}
/>
<input
placeholder="Highlights"
value={highlight}
onChange={(e) => setHighlight(e.target.value)}
/>
<button onClick={handleSave}>Save</button>
</div>
);
};
export { Edit };

View File

@@ -0,0 +1,29 @@
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 File',
description: 'Display a file from a Github repository',
icon: <SiGithub />,
id: 'github.file',
parseUrl: (url) => {
if (url.hostname !== 'github.com') {
return;
}
const pathParts = url.pathname.split('/').filter(Boolean);
const [owner, repo, type, branch, ...filePathParts] = pathParts.slice(0);
const path = filePathParts.join('/');
if (type !== 'blob' || !branch || !path) {
return;
}
return { owner, repo, branch, path };
},
schema,
component: View,
edit: Edit,
};
export default widget;

View File

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

View File

@@ -0,0 +1,62 @@
import {
useAutoUpdate,
useGithubQuery,
useName,
withGithub,
} from '@refocus/sdk';
import { Props } from './schema';
import { CodeEditor, Github, View } from '@refocus/ui';
import { styled } from 'styled-components';
const FullHeight = styled(View)`
height: 100%;
`;
const StyledCodeEditor = styled(CodeEditor)`
height: 100%;
`;
const WidgetView = withGithub<Props>(
({ owner, repo, branch, path, highlight }) => {
const [, setName] = useName();
const { data, fetch } = useGithubQuery(async (client, params: Props) => {
const response = await client.rest.repos.getContent({
owner: params.owner,
repo: params.repo,
path: params.path,
});
setName(`${params.owner}/${params.repo}/${params.path}}`);
return response.data;
});
useAutoUpdate(
{
interval: 1000 * 60 * 5,
action: async () => fetch({ owner, repo, branch, path }),
},
[owner, repo, branch, path],
);
if (!data) {
return null;
}
if (!data || !('type' in data) || data.type !== 'file') {
return <div>Not a file</div>;
}
return (
<FullHeight $fc>
<StyledCodeEditor
highlight={highlight}
value={atob(data.content)}
setValue={() => {}}
readOnly
/>
</FullHeight>
);
},
Github.NotLoggedIn,
);
export { WidgetView as View };

View File

@@ -4,6 +4,7 @@ import pullRequest from './pull-request/index.widget';
import pullRequstComments from './pull-request-comments/index.widget';
import workflowRun from './workflow-run/index.widget';
import workflowRuns from './workflow-runs/index.widget';
import file from './file';
const github = [
githubProfileWidget,
@@ -11,6 +12,7 @@ const github = [
pullRequstComments,
workflowRun,
workflowRuns,
file,
] satisfies Widget<any>[];
export { github };

View File

@@ -2,7 +2,13 @@ import { Widget } from '@refocus/sdk';
import { github } from './github';
import { linear } from './linear';
import { slack } from './slack';
import markdown from './markdown';
const widgets = [...linear, ...github, ...slack] satisfies Widget<any>[];
const widgets = [
...linear,
...github,
...slack,
markdown,
] satisfies Widget<any>[];
export { widgets };

View File

@@ -10,10 +10,12 @@ const WidgetView = withLinear<LinearIssueProps>(({ id }) => {
const issue = await client.issue(id);
const assignee = await issue.assignee;
const creator = await issue.creator;
const state = await issue.state;
return {
issue,
assignee,
creator,
state,
};
});
@@ -34,6 +36,9 @@ const WidgetView = withLinear<LinearIssueProps>(({ id }) => {
onClick={() => window.open(data?.issue.url, '_blank')}
>
<View>
<Typography variant="tiny">
{data?.state?.name} - {data?.issue.priorityLabel}
</Typography>
<Typography variant="title">{data?.issue?.title}</Typography>
<Typography variant="tiny">
{data?.issue.description?.substring(0, 100)}

View File

@@ -0,0 +1,36 @@
import { CodeEditor, Typography } from '@refocus/ui';
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 [markdown, setMarkdown] = useState(value?.markdown || '');
const [name, setName] = useState(value?.name || '');
const handleSave = useCallback(() => {
save({
name,
markdown,
});
}, [markdown, save, name]);
return (
<div>
<Typography
as="input"
$u
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<CodeEditor language="markdown" value={markdown} setValue={setMarkdown} />
<button onClick={handleSave}>Save</button>
</div>
);
};
export { Edit };

View File

@@ -0,0 +1,17 @@
import { Widget } from '@refocus/sdk';
import { IoLogoMarkdown } from 'react-icons/io';
import { schema } from './schema';
import { Edit } from './edit';
import { View } from './view';
const widget: Widget<typeof schema> = {
name: 'Markdown',
description: 'A markdown note',
icon: <IoLogoMarkdown />,
id: 'text.markdown',
schema,
component: View,
edit: Edit,
};
export default widget;

View File

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

View File

@@ -0,0 +1,21 @@
import { useName } from '@refocus/sdk';
import ReactMarkdown from 'react-markdown';
import { Props } from './schema';
import { useEffect } from 'react';
import { View } from '@refocus/ui';
const WidgetView: React.FC<Props> = ({ markdown, name }) => {
const [, setName] = useName();
useEffect(() => {
setName(name);
}, [name, setName]);
return (
<View $px="md">
<ReactMarkdown>{markdown}</ReactMarkdown>
</View>
);
};
export { WidgetView as View };