mirror of
https://github.com/morten-olsen/refocus.dev.git
synced 2026-02-08 00:46:25 +01:00
fix: improved UI
This commit is contained in:
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
44
packages/widgets/src/github/workflow-run/edit.tsx
Normal file
44
packages/widgets/src/github/workflow-run/edit.tsx
Normal 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 };
|
||||
28
packages/widgets/src/github/workflow-run/index.widget.tsx
Normal file
28
packages/widgets/src/github/workflow-run/index.widget.tsx
Normal 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;
|
||||
12
packages/widgets/src/github/workflow-run/schema.ts
Normal file
12
packages/widgets/src/github/workflow-run/schema.ts
Normal 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 };
|
||||
53
packages/widgets/src/github/workflow-run/view.tsx
Normal file
53
packages/widgets/src/github/workflow-run/view.tsx
Normal 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 };
|
||||
37
packages/widgets/src/github/workflow-runs/edit.tsx
Normal file
37
packages/widgets/src/github/workflow-runs/edit.tsx
Normal 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 };
|
||||
28
packages/widgets/src/github/workflow-runs/index.widget.tsx
Normal file
28
packages/widgets/src/github/workflow-runs/index.widget.tsx
Normal 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;
|
||||
11
packages/widgets/src/github/workflow-runs/schema.ts
Normal file
11
packages/widgets/src/github/workflow-runs/schema.ts
Normal 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 };
|
||||
56
packages/widgets/src/github/workflow-runs/view.tsx
Normal file
56
packages/widgets/src/github/workflow-runs/view.tsx
Normal 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 };
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user