mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-24 05:07:06 +08:00
fix(core): some comment editor ux enhancements (#13126)
fix AF-2726, AF-2729 #### PR Dependency Tree * **PR #13126** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added drag-and-drop support for file attachments in the comment editor. * Improved user feedback with notifications and toasts when downloading attachments. * **Bug Fixes** * Enhanced error handling and reporting for attachment downloads. * **Improvements** * Optimized file download process for same-origin resources to improve performance. * Updated default comment filter to show all comments, not just those for the current mode. * **Documentation** * Updated English localization to provide clearer instructions when no comments are present. <!-- end of auto-generated comment: release notes by coderabbit.ai --> #### PR Dependency Tree * **PR #13126** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal)
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { IconButton, notify } from '@affine/component';
|
import { IconButton, notify, toast } from '@affine/component';
|
||||||
import { LitDocEditor, type PageEditor } from '@affine/core/blocksuite/editors';
|
import { LitDocEditor, type PageEditor } from '@affine/core/blocksuite/editors';
|
||||||
import { SnapshotHelper } from '@affine/core/modules/comment/services/snapshot-helper';
|
import { SnapshotHelper } from '@affine/core/modules/comment/services/snapshot-helper';
|
||||||
import type { CommentAttachment } from '@affine/core/modules/comment/types';
|
import type { CommentAttachment } from '@affine/core/modules/comment/types';
|
||||||
import { PeekViewService } from '@affine/core/modules/peek-view';
|
import { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
|
import { downloadResourceWithUrl } from '@affine/core/utils/resource';
|
||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { getAttachmentFileIconRC } from '@blocksuite/affine/components/icons';
|
import { getAttachmentFileIconRC } from '@blocksuite/affine/components/icons';
|
||||||
import { type RichText, selectTextModel } from '@blocksuite/affine/rich-text';
|
import { type RichText, selectTextModel } from '@blocksuite/affine/rich-text';
|
||||||
@@ -80,16 +81,6 @@ export interface CommentEditorRef {
|
|||||||
focus: () => void;
|
focus: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const download = (url: string, name: string) => {
|
|
||||||
const element = document.createElement('a');
|
|
||||||
element.setAttribute('download', name);
|
|
||||||
element.setAttribute('href', url);
|
|
||||||
element.style.display = 'none';
|
|
||||||
document.body.append(element);
|
|
||||||
element.click();
|
|
||||||
element.remove();
|
|
||||||
};
|
|
||||||
|
|
||||||
// todo: get rid of circular data changes
|
// todo: get rid of circular data changes
|
||||||
const useSnapshotDoc = (
|
const useSnapshotDoc = (
|
||||||
defaultSnapshotOrDoc: DocSnapshot | Store,
|
defaultSnapshotOrDoc: DocSnapshot | Store,
|
||||||
@@ -313,6 +304,28 @@ export const CommentEditor = forwardRef<CommentEditorRef, CommentEditorProps>(
|
|||||||
[addAttachments]
|
[addAttachments]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleDragOver = useCallback(
|
||||||
|
(e: React.DragEvent) => {
|
||||||
|
if (readonly) return;
|
||||||
|
// Prevent default to allow drop
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
[readonly]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDrop = useCallback(
|
||||||
|
(e: React.DragEvent) => {
|
||||||
|
if (readonly) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const files = Array.from(e.dataTransfer?.files ?? []);
|
||||||
|
if (files.length) {
|
||||||
|
addAttachments(files);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[addAttachments, readonly]
|
||||||
|
);
|
||||||
|
|
||||||
const openFilePicker = useAsyncCallback(async () => {
|
const openFilePicker = useAsyncCallback(async () => {
|
||||||
if (isUploadDisabled) return;
|
if (isUploadDisabled) return;
|
||||||
const files = await openFilesWith('Any');
|
const files = await openFilesWith('Any');
|
||||||
@@ -382,8 +395,6 @@ export const CommentEditor = forwardRef<CommentEditorRef, CommentEditorProps>(
|
|||||||
if (!attachments) return;
|
if (!attachments) return;
|
||||||
const att = attachments[index];
|
const att = attachments[index];
|
||||||
if (!att) return;
|
if (!att) return;
|
||||||
const url = att.url || att.localUrl;
|
|
||||||
if (!url) return;
|
|
||||||
if (isImageAttachment(att)) {
|
if (isImageAttachment(att)) {
|
||||||
// translate attachment index to image index
|
// translate attachment index to image index
|
||||||
const imageAttachments = attachments.filter(isImageAttachment);
|
const imageAttachments = attachments.filter(isImageAttachment);
|
||||||
@@ -391,13 +402,19 @@ export const CommentEditor = forwardRef<CommentEditorRef, CommentEditorProps>(
|
|||||||
if (imageIndex >= 0) {
|
if (imageIndex >= 0) {
|
||||||
handleImagePreview(imageIndex);
|
handleImagePreview(imageIndex);
|
||||||
}
|
}
|
||||||
} else if (att.url || att.localUrl) {
|
} else if (att.url) {
|
||||||
// todo: open attachment preview. for now, just download it
|
// todo: open attachment preview. for now, just download it
|
||||||
download(url, att.filename ?? att.file?.name ?? 'attachment');
|
downloadResourceWithUrl(
|
||||||
notify({
|
att.url,
|
||||||
title: 'Downloading attachment',
|
att.filename ?? att.file?.name ?? 'attachment'
|
||||||
message: 'The attachment is being downloaded to your computer.',
|
).catch(e => {
|
||||||
|
console.error('Failed to download attachment', e);
|
||||||
|
notify.error({
|
||||||
|
title: 'Failed to download attachment',
|
||||||
|
message: e.message,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
toast('The attachment is being downloaded to your computer.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[attachments, handleImagePreview]
|
[attachments, handleImagePreview]
|
||||||
@@ -538,6 +555,8 @@ export const CommentEditor = forwardRef<CommentEditorRef, CommentEditorProps>(
|
|||||||
onClick={readonly ? undefined : handleClickEditor}
|
onClick={readonly ? undefined : handleClickEditor}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onPaste={handlePaste}
|
onPaste={handlePaste}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
data-readonly={!!readonly}
|
data-readonly={!!readonly}
|
||||||
className={clsx(styles.container, 'comment-editor-viewport')}
|
className={clsx(styles.container, 'comment-editor-viewport')}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ const CommentList = ({ entity }: { entity: DocCommentEntity }) => {
|
|||||||
const [filterState, setFilterState] = useState<CommentFilterState>({
|
const [filterState, setFilterState] = useState<CommentFilterState>({
|
||||||
showResolvedComments: false,
|
showResolvedComments: false,
|
||||||
onlyMyReplies: false,
|
onlyMyReplies: false,
|
||||||
onlyCurrentMode: true,
|
onlyCurrentMode: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFilterChange = useCallback(
|
const onFilterChange = useCallback(
|
||||||
|
|||||||
@@ -33,7 +33,22 @@ export async function downloadBlob(blob: Blob, filename: string) {
|
|||||||
URL.revokeObjectURL(blobUrl);
|
URL.revokeObjectURL(blobUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function downloadFile(url: string, filename: string) {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.target = '_blank';
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
|
||||||
export async function downloadResourceWithUrl(url: string, filename: string) {
|
export async function downloadResourceWithUrl(url: string, filename: string) {
|
||||||
|
// 1. if url is not same origin, fetch it to blob and download it
|
||||||
|
// 2. otherwise, download it directly
|
||||||
|
const sameOrigin = new URL(url).origin === window.location.origin;
|
||||||
|
if (sameOrigin) {
|
||||||
|
downloadFile(url, filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// given input url may not have correct mime type
|
// given input url may not have correct mime type
|
||||||
const blob = await resourceUrlToBlob(url);
|
const blob = await resourceUrlToBlob(url);
|
||||||
if (!blob) return;
|
if (!blob) return;
|
||||||
|
|||||||
@@ -8239,7 +8239,7 @@ export function useAFFiNEI18N(): {
|
|||||||
*/
|
*/
|
||||||
["com.affine.comment.comments"](): string;
|
["com.affine.comment.comments"](): string;
|
||||||
/**
|
/**
|
||||||
* `No comments yet`
|
* `No comments yet, select content to add comment to`
|
||||||
*/
|
*/
|
||||||
["com.affine.comment.no-comments"](): string;
|
["com.affine.comment.no-comments"](): string;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2067,7 +2067,7 @@
|
|||||||
"com.affine.migration-all-docs-notification.error": "Migration failed: {{errorMessage}}",
|
"com.affine.migration-all-docs-notification.error": "Migration failed: {{errorMessage}}",
|
||||||
"com.affine.migration-all-docs-notification.button": "Migrate data",
|
"com.affine.migration-all-docs-notification.button": "Migrate data",
|
||||||
"com.affine.comment.comments": "Comments",
|
"com.affine.comment.comments": "Comments",
|
||||||
"com.affine.comment.no-comments": "No comments yet",
|
"com.affine.comment.no-comments": "No comments yet, select content to add comment to",
|
||||||
"com.affine.comment.delete.confirm.title": "Delete the thread?",
|
"com.affine.comment.delete.confirm.title": "Delete the thread?",
|
||||||
"com.affine.comment.delete.confirm.description": "All comments will also be deleted, and this action cannot be undone.",
|
"com.affine.comment.delete.confirm.description": "All comments will also be deleted, and this action cannot be undone.",
|
||||||
"com.affine.comment.reply.delete.confirm.title": "Delete this reply?",
|
"com.affine.comment.reply.delete.confirm.title": "Delete this reply?",
|
||||||
|
|||||||
Reference in New Issue
Block a user