Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions models/File.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { toggle } from 'mobx-restful';
import { blobOf, uniqueID } from 'web-utility';

import userStore from './User';

export interface SignedLink {
getLink: string;
putLink: string;
}

export class FileModel {
client = userStore.client;

@toggle('uploading')
async upload(file: string | Blob) {
if (typeof file === 'string') {
const name = file.split('/').pop()!;

file = new File([await blobOf(file)], name);
}
const { body } = await this.client.post<SignedLink>(
`file/signed-link/${file instanceof File ? file.name : uniqueID()}`,
);
await this.client.put(body!.putLink, file, { 'Content-Type': file.type });

return body!.getLink;
}
}

export default new FileModel();
43 changes: 41 additions & 2 deletions pages/dashboard/project/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { marked } from 'marked';
import { observer } from 'mobx-react';
import { ObservedComponent, reaction } from 'mobx-react-helper';
import { compose, JWTProps, jwtVerifier, RouteProps, router } from 'next-ssr-middleware';
import { FormEvent, KeyboardEventHandler } from 'react';
import { ChangeEvent, ClipboardEvent, DragEvent, FormEvent, KeyboardEventHandler } from 'react';
import { formToJSON, scrollTo, sleep } from 'web-utility';

import { PageHead } from '../../../components/PageHead';
import { EvaluationDisplay } from '../../../components/Project/EvaluationDisplay';
import { ScrollList } from '../../../components/ScrollList';
import { SessionBox } from '../../../components/User/SessionBox';
import { ConsultMessageModel, ProjectModel } from '../../../models/ProjectEvaluation';
import fileStore from '../../../models/File';
import { i18n, I18nContext } from '../../../models/Translation';

type ProjectEvaluationPageProps = JWTProps<User> & RouteProps<{ id: string }>;
Expand Down Expand Up @@ -76,6 +77,38 @@ export default class ProjectEvaluationPage extends ObservedComponent<
);
};

handleFiles = async (files: File[]) => {
for (const file of files) {
const URI = await fileStore.upload(file);
const content = file.type.startsWith('image/')
? `![${file.name}](${URI})`
: `[${file.name}](${URI})`;
await this.messageStore.updateOne({ content });
}
};

handleFileSelect = (event: ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || []);
if (files.length) {
this.handleFiles(files);
event.target.value = '';
}
};

handlePasteDrop = (event: ClipboardEvent<HTMLDivElement> | DragEvent<HTMLDivElement>) => {
const list =
event.type === 'paste'
? [...(event as ClipboardEvent).clipboardData.items]
: [...(event as DragEvent).dataTransfer.items];

const files = list.map(item => item.getAsFile()).filter((file): file is File => file !== null);

if (files.length) {
event.preventDefault();
this.handleFiles(files);
}
};

renderChatMessage = (
{ id, content, evaluation, prototypes, createdAt, createdBy }: ConsultMessage,
index = 0,
Expand Down Expand Up @@ -175,6 +208,10 @@ export default class ProjectEvaluationPage extends ObservedComponent<
className="sticky bottom-0 mx-1 mt-auto mb-1 flex items-end gap-2 p-1.5 sm:mx-0 sm:mb-0 sm:p-2"
onSubmit={this.handleMessageSubmit}
>
<Button component="label" className="min-w-0 p-2" title={t('upload_files') as string}>
📁
<input type="file" hidden multiple onChange={this.handleFileSelect} />
</Button>
<TextField
name="content"
placeholder={t('type_your_message')}
Expand All @@ -185,12 +222,14 @@ export default class ProjectEvaluationPage extends ObservedComponent<
size="small"
required
onKeyUp={this.handleQuickSubmit}
onPaste={this.handlePasteDrop}
onDrop={this.handlePasteDrop}
/>
<Button
type="submit"
variant="contained"
className="min-w-full px-2 whitespace-nowrap sm:min-w-0"
disabled={messageStore.uploading > 0}
disabled={messageStore.uploading > 0 || fileStore.uploading > 0}
>
{t('send')}
</Button>
Expand Down