fix: improved UI

This commit is contained in:
Morten Olsen
2023-06-19 09:25:03 +02:00
parent 11299a31fa
commit 85b88822b4
28 changed files with 618 additions and 124 deletions

View File

@@ -2,11 +2,15 @@ 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';
import workflowRun from './workflow-run/index.widget';
import workflowRuns from './workflow-runs/index.widget';
const github = [
githubProfileWidget,
pullRequest,
pullRequstComments,
workflowRun,
workflowRuns,
] satisfies Widget<any>[];
export { github };

View File

@@ -3,6 +3,7 @@ import {
useAutoUpdate,
useGithubQuery,
withGithub,
useName,
} from '@refocus/sdk';
import { Chat, Github, List } from '@refocus/ui';
import { Props } from './schema';
@@ -16,12 +17,14 @@ type QueryData = {
const View = withGithub<Props>(({ owner, repo, pr }) => {
const addNotification = useAddWidgetNotification();
const [, setName] = useName();
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,
});
setName(`${params.owner}/${params.repo} #${params.pr}`);
return response.data.slice(0, 5);
});

View File

@@ -1,4 +1,9 @@
import { useAutoUpdate, useGithubQuery, withGithub } from '@refocus/sdk';
import {
useAutoUpdate,
useGithubQuery,
useName,
withGithub,
} from '@refocus/sdk';
import { Props } from './schema';
import { Github } from '@refocus/ui';
@@ -9,12 +14,14 @@ type QueryData = {
};
const View = withGithub<Props>(({ owner, repo, pr }) => {
const [, setName] = useName();
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,
});
setName(`${params.owner}/${params.repo} #${params.pr}`);
return response.data;
});

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 [id, setId] = useState(value?.id || '');
const handleSave = useCallback(() => {
save({
owner,
repo,
id: typeof id === 'string' ? parseInt(id, 10) : id,
});
}, [owner, repo, id, 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={id}
onChange={(e) => setId(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 Workflow Run',
description: 'Display information about a specific workflow run.',
icon: <SiGithub />,
id: 'github.workflow-run',
parseUrl: (url) => {
if (url.hostname !== 'github.com') {
return;
}
const pathParts = url.pathname.split('/').filter(Boolean);
const [owner, repo, type, subtype, id] = pathParts.slice(0);
if (type !== 'actions' || subtype !== 'runs' || !id) {
return;
}
return { owner, repo, id: parseInt(id, 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(),
id: Type.Number(),
});
type Props = Static<typeof schema>;
export type { Props };
export { schema };

View File

@@ -0,0 +1,53 @@
import {
useAutoUpdate,
useGithubQuery,
useName,
withGithub,
} from '@refocus/sdk';
import { Props } from './schema';
import { Github } from '@refocus/ui';
type QueryData = {
id: number;
owner: string;
repo: string;
};
const WidgetView = withGithub<Props>(({ owner, repo, id }) => {
const [, setName] = useName();
const { data, fetch } = useGithubQuery(async (client, params: QueryData) => {
const response = await client.rest.actions.getWorkflowRun({
owner: params.owner,
repo: params.repo,
run_id: params.id,
});
setName(`${response.data.repository.full_name} ${response.data.name}`);
return response.data;
});
useAutoUpdate(
{
interval: 1000 * 60 * 5,
action: async () => fetch({ owner, repo, id }),
},
[owner, repo, id],
);
if (!data) {
return null;
}
return (
<Github.WorkflowRun
workflowRun={data}
onPress={() =>
window.open(
`https://github.com/${owner}/${repo}/actions/runs/${id}`,
'_blank',
)
}
/>
);
}, Github.NotLoggedIn);
export { WidgetView as View };

View File

@@ -0,0 +1,37 @@
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 handleSave = useCallback(() => {
save({
owner,
repo,
});
}, [owner, repo, 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)}
/>
<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 Workflow Runs',
description: 'Display the last 5 workflow runs',
icon: <SiGithub />,
id: 'github.workflow-runs',
parseUrl: (url) => {
if (url.hostname !== 'github.com') {
return;
}
const pathParts = url.pathname.split('/').filter(Boolean);
const [owner, repo, type, subtype] = pathParts.slice(0);
if (type !== 'actions' || subtype) {
return;
}
return { owner, repo };
},
schema,
component: View,
edit: Edit,
};
export default widget;

View File

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

View File

@@ -0,0 +1,56 @@
import {
useAutoUpdate,
useGithubQuery,
useName,
withGithub,
} from '@refocus/sdk';
import { Props } from './schema';
import { Github, List } from '@refocus/ui';
type QueryData = {
owner: string;
repo: string;
};
const WidgetView = withGithub<Props>(({ owner, repo }) => {
const [, setName] = useName();
const { data, fetch } = useGithubQuery(async (client, params: QueryData) => {
const response = await client.rest.actions.listWorkflowRunsForRepo({
owner: params.owner,
repo: params.repo,
});
setName(`${params.owner}/${params.repo} workflow runs`);
return response.data.workflow_runs.slice(0, 5);
});
console.log(data);
useAutoUpdate(
{
interval: 1000 * 60 * 5,
action: async () => fetch({ owner, repo }),
},
[owner, repo],
);
if (!data) {
return null;
}
return (
<List>
{data?.map((run) => (
<Github.WorkflowRun
workflowRun={run}
onPress={() =>
window.open(
`https://github.com/${owner}/${repo}/actions/runs/${run.id}`,
'_blank',
)
}
/>
))}
</List>
);
}, Github.NotLoggedIn);
export { WidgetView as View };

View File

@@ -2,6 +2,7 @@ import {
useAddWidgetNotification,
useAutoUpdate,
useLinearQuery,
useName,
withLinear,
} from '@refocus/sdk';
import { Chat, Linear, List, View } from '@refocus/ui';
@@ -13,9 +14,11 @@ type LinearIssueProps = {
const WidgetView = withLinear<LinearIssueProps>(({ id }) => {
const addNotification = useAddWidgetNotification();
const [, setName] = useName();
const { data, fetch } = useLinearQuery(async (client) => {
const issue = await client.issue(id);
const comments = await issue.comments();
setName(id);
return comments.nodes;
});

View File

@@ -1,9 +1,11 @@
import {
useAddWidgetNotification,
useAutoUpdate,
useName,
useSlackQuery,
withSlack,
} from '@refocus/sdk';
import styled from 'styled-components';
import { Props } from './schema';
import { Message } from './message/view';
import { useState } from 'react';
@@ -13,7 +15,24 @@ type PostMessageOptions = {
message: string;
};
const MessageList = styled(View)`
transform: scaleY(-1);
flex: 1;
overflow-y: auto;
flex-grow: 1;
flex-shrink: 1;
& > * {
transform: scaleY(-1);
}
`;
const Wrapper = styled(View)`
max-height: 100%;
overflow: hidden;
`;
const WidgetView = withSlack<Props>(({ conversationId }) => {
const [, setName] = useName();
const addNotification = useAddWidgetNotification();
const [message, setMessage] = useState('');
const { fetch, data } = useSlackQuery(async (client, props: Props) => {
@@ -27,6 +46,7 @@ const WidgetView = withSlack<Props>(({ conversationId }) => {
const response = await client.send('conversations.info', {
channel: props.conversationId,
});
setName(response.channel!.name || 'Direct message');
return response.channel!;
});
@@ -39,7 +59,7 @@ const WidgetView = withSlack<Props>(({ conversationId }) => {
},
);
const update = useAutoUpdate(
useAutoUpdate(
{
action: async () => {
await info.fetch({ conversationId });
@@ -68,17 +88,8 @@ const WidgetView = withSlack<Props>(({ conversationId }) => {
);
return (
<View $p="md">
<button onClick={update}>Update</button>
<Typography variant="header">
{info.data?.name || 'Direct message'}
</Typography>
<Chat.Compose
value={message}
onValueChange={setMessage}
onSend={() => post({ message })}
/>
<List>
<Wrapper $p="sm" $fc $gap="sm">
<MessageList $gap="md" $fc>
{data?.map((message) => (
<Message
key={message.ts}
@@ -86,8 +97,13 @@ const WidgetView = withSlack<Props>(({ conversationId }) => {
userId={message.user}
/>
))}
</List>
</View>
</MessageList>
<Chat.Compose
value={message}
onValueChange={setMessage}
onSend={() => post({ message })}
/>
</Wrapper>
);
}, Slack.NotLoggedIn);