mirror of
https://github.com/morten-olsen/refocus.dev.git
synced 2026-02-08 00:46:25 +01:00
feat: desktop version
This commit is contained in:
44
packages/widgets/src/code/edit.tsx
Normal file
44
packages/widgets/src/code/edit.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Button, CodeEditor, Form } 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 [code, setCode] = useState(value?.code || '');
|
||||
const [language, setLanguage] = useState(value?.language || '');
|
||||
const [name, setName] = useState(value?.name || '');
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
save({
|
||||
name,
|
||||
code,
|
||||
language,
|
||||
});
|
||||
}, [code, save, language, name]);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Form.Field label="Name">
|
||||
<Form.Input value={name} onChange={(e) => setName(e.target.value)} />
|
||||
</Form.Field>
|
||||
<Form.Field label="Language">
|
||||
<Form.Input
|
||||
value={language}
|
||||
onChange={(e) => setLanguage(e.target.value)}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field label="Code">
|
||||
<CodeEditor language={language} value={code} setValue={setCode} />
|
||||
</Form.Field>
|
||||
<Form.Buttons>
|
||||
<Button onClick={handleSave} title="Save" />
|
||||
</Form.Buttons>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export { Edit };
|
||||
17
packages/widgets/src/code/index.tsx
Normal file
17
packages/widgets/src/code/index.tsx
Normal 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: 'Code',
|
||||
description: 'Write a code file',
|
||||
icon: <IoLogoMarkdown />,
|
||||
id: 'text.code',
|
||||
schema,
|
||||
component: View,
|
||||
edit: Edit,
|
||||
};
|
||||
|
||||
export default widget;
|
||||
12
packages/widgets/src/code/schema.ts
Normal file
12
packages/widgets/src/code/schema.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Type, Static } from '@sinclair/typebox';
|
||||
|
||||
const schema = Type.Object({
|
||||
name: Type.String(),
|
||||
code: Type.String(),
|
||||
language: Type.String(),
|
||||
});
|
||||
|
||||
type Props = Static<typeof schema>;
|
||||
|
||||
export type { Props };
|
||||
export { schema };
|
||||
33
packages/widgets/src/code/view.tsx
Normal file
33
packages/widgets/src/code/view.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useName } from '@refocus/sdk';
|
||||
import { Props } from './schema';
|
||||
import { useEffect } from 'react';
|
||||
import { CodeEditor, View } from '@refocus/ui';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
const FullHeight = styled(View)`
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledCodeEditor = styled(CodeEditor)`
|
||||
height: 100%;
|
||||
`;
|
||||
const WidgetView: React.FC<Props> = ({ code, language, name }) => {
|
||||
const [, setName] = useName();
|
||||
|
||||
useEffect(() => {
|
||||
setName(name);
|
||||
}, [name, setName]);
|
||||
|
||||
return (
|
||||
<FullHeight $fc>
|
||||
<StyledCodeEditor
|
||||
readOnly
|
||||
language={language}
|
||||
value={code}
|
||||
setValue={() => {}}
|
||||
/>
|
||||
</FullHeight>
|
||||
);
|
||||
};
|
||||
|
||||
export { WidgetView as View };
|
||||
@@ -3,12 +3,14 @@ import { github } from './github';
|
||||
import { linear } from './linear';
|
||||
import { slack } from './slack';
|
||||
import markdown from './markdown';
|
||||
import code from './code';
|
||||
|
||||
const widgets = [
|
||||
...linear,
|
||||
...github,
|
||||
...slack,
|
||||
markdown,
|
||||
code,
|
||||
] satisfies Widget<any>[];
|
||||
|
||||
export { widgets };
|
||||
|
||||
@@ -25,7 +25,7 @@ const renderElement = (item: Renderable) => {
|
||||
</a>
|
||||
);
|
||||
case 'user':
|
||||
return <User id={item.user_id} />;
|
||||
return <User key={item.user_id} id={item.user_id} />;
|
||||
case 'emoji':
|
||||
return unicodeToEmoji(item.unicode);
|
||||
case 'rich_text_list':
|
||||
|
||||
@@ -5,7 +5,6 @@ import { render } from '../../block/render';
|
||||
import { useMemo } from 'react';
|
||||
import { WidgetProvider, WidgetView } from '@refocus/sdk';
|
||||
import { styled } from 'styled-components';
|
||||
import { User } from '../../block/elements/user';
|
||||
import { UserAvatar } from '../../block/elements/user-avatar';
|
||||
|
||||
type Message = Exclude<
|
||||
@@ -57,7 +56,7 @@ const Message: React.FC<Message> = ({
|
||||
{reaction.name}
|
||||
<View $fr>
|
||||
{reaction.users?.map((user) => (
|
||||
<UserAvatar id={user} />
|
||||
<UserAvatar key={user} id={user} />
|
||||
))}
|
||||
</View>
|
||||
</Typography>
|
||||
|
||||
@@ -9,7 +9,7 @@ import styled from 'styled-components';
|
||||
import { Props } from './schema';
|
||||
import { ConversationsHistoryResponse } from '@slack/web-api';
|
||||
import { useState } from 'react';
|
||||
import { Chat, Slack, Typography, View } from '@refocus/ui';
|
||||
import { Chat, Loader, Slack, Typography, View } from '@refocus/ui';
|
||||
import { User } from '../block/elements/user';
|
||||
import { Message } from './message/view';
|
||||
|
||||
@@ -42,21 +42,23 @@ const WidgetView = withSlack<Props>(({ conversationId, ts }) => {
|
||||
const [, setName] = useName();
|
||||
const addNotification = useAddWidgetNotification();
|
||||
const [message, setMessage] = useState('');
|
||||
const { fetch, data } = useSlackQuery(async (client, props: Props) => {
|
||||
if (props.ts) {
|
||||
const response = await client.send('conversations.replies', {
|
||||
channel: props.conversationId,
|
||||
ts: props.ts,
|
||||
});
|
||||
return response.messages! as MessageType[];
|
||||
} else {
|
||||
const response = await client.send('conversations.history', {
|
||||
channel: props.conversationId,
|
||||
limit: 5,
|
||||
});
|
||||
return response.messages! as MessageType[];
|
||||
}
|
||||
});
|
||||
const { fetch, data, loading } = useSlackQuery(
|
||||
async (client, props: Props) => {
|
||||
if (props.ts) {
|
||||
const response = await client.send('conversations.replies', {
|
||||
channel: props.conversationId,
|
||||
ts: props.ts,
|
||||
});
|
||||
return response.messages!.reverse() as MessageType[];
|
||||
} else {
|
||||
const response = await client.send('conversations.history', {
|
||||
channel: props.conversationId,
|
||||
limit: 5,
|
||||
});
|
||||
return response.messages! as MessageType[];
|
||||
}
|
||||
},
|
||||
);
|
||||
const info = useSlackQuery(async (client, props: Props) => {
|
||||
const response = await client.send('conversations.info', {
|
||||
channel: props.conversationId,
|
||||
@@ -65,16 +67,7 @@ const WidgetView = withSlack<Props>(({ conversationId, ts }) => {
|
||||
return response.channel!;
|
||||
});
|
||||
|
||||
const { fetch: post } = useSlackQuery(
|
||||
async (client, props: PostMessageOptions) => {
|
||||
client.send('chat.postMessage', {
|
||||
text: props.message,
|
||||
channel: conversationId,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
useAutoUpdate(
|
||||
const update = useAutoUpdate(
|
||||
{
|
||||
action: async () => {
|
||||
await info.fetch({ conversationId, ts });
|
||||
@@ -103,10 +96,22 @@ const WidgetView = withSlack<Props>(({ conversationId, ts }) => {
|
||||
[conversationId, ts],
|
||||
);
|
||||
|
||||
const { fetch: post } = useSlackQuery(
|
||||
async (client, props: PostMessageOptions) => {
|
||||
await client.send('chat.postMessage', {
|
||||
text: props.message,
|
||||
channel: conversationId,
|
||||
});
|
||||
setMessage('');
|
||||
await update();
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<Wrapper $p="sm" $fc $gap="sm">
|
||||
{loading && <Loader />}
|
||||
<MessageList $gap="md" $fc>
|
||||
{data?.map((message) => {
|
||||
{data?.map((message, index) => {
|
||||
if ('subtype' in message && message.subtype === 'channel_join') {
|
||||
return (
|
||||
<Typography key={message.ts}>
|
||||
@@ -116,7 +121,7 @@ const WidgetView = withSlack<Props>(({ conversationId, ts }) => {
|
||||
}
|
||||
return (
|
||||
<Message
|
||||
key={message.ts}
|
||||
key={message.ts || index}
|
||||
{...message}
|
||||
conversationId={conversationId}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user