diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index ca506ca5c7..71df6cabc9 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -29,6 +29,8 @@ "accept_all": "", "accept_and_continue": "", "accept_change": "", + "accept_change_error_description": "", + "accept_change_error_title": "", "accept_invitation": "", "accept_or_reject_each_changes_individually": "", "accept_terms_and_conditions": "", @@ -53,6 +55,8 @@ "add_another_token": "", "add_comma_separated_emails_help": "", "add_comment": "", + "add_comment_error_message": "", + "add_comment_error_title": "", "add_company_details": "", "add_email_address": "", "add_email_to_claim_features": "", @@ -303,6 +307,8 @@ "delete_authentication_token_info": "", "delete_certificate": "", "delete_comment": "", + "delete_comment_error_message": "", + "delete_comment_error_title": "", "delete_comment_message": "", "delete_comment_thread": "", "delete_comment_thread_message": "", @@ -384,6 +390,8 @@ "easily_import_and_sync_your_references": "", "easily_manage_your_project_files_everywhere": "", "edit": "", + "edit_comment_error_message": "", + "edit_comment_error_title": "", "edit_dictionary": "", "edit_dictionary_empty": "", "edit_dictionary_remove": "", @@ -1181,6 +1189,8 @@ "rename": "", "rename_project": "", "reopen": "", + "reopen_comment_error_message": "", + "reopen_comment_error_title": "", "replace_figure": "", "replace_from_another_project": "", "replace_from_computer": "", @@ -1201,6 +1211,8 @@ "resize": "", "resolve": "", "resolve_comment": "", + "resolve_comment_error_message": "", + "resolve_comment_error_title": "", "resolved_comments": "", "restore": "", "restore_file": "", diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-add-comment.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-add-comment.tsx index 2cd55de12a..9d1c976f20 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-panel-add-comment.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-add-comment.tsx @@ -13,6 +13,8 @@ import { Button } from 'react-bootstrap' import { ReviewPanelEntry } from './review-panel-entry' import { ThreadId } from '../../../../../types/review-panel/review-panel' import { Decoration } from '@codemirror/view' +import { useModalsContext } from '@/features/ide-react/context/modals-context' +import { debugConsole } from '@/utils/debugging' export const ReviewPanelAddComment: FC<{ docId: string @@ -25,8 +27,8 @@ export const ReviewPanelAddComment: FC<{ const view = useCodeMirrorViewContext() const state = useCodeMirrorStateContext() const { addComment } = useThreadsActionsContext() - const [error, setError] = useState() const [submitting, setSubmitting] = useState(false) + const { showGenericMessageModal } = useModalsContext() const handleClose = useCallback(() => { view.dispatch({ @@ -35,22 +37,27 @@ export const ReviewPanelAddComment: FC<{ }, [view, value]) const submitForm = useCallback( - message => { + async message => { setSubmitting(true) const content = view.state.sliceDoc(from, to) - addComment(from, content, message) - .catch(setError) - .finally(() => setSubmitting(false)) - - view.dispatch({ - selection: EditorSelection.cursor(view.state.selection.main.anchor), - }) - - handleClose() + try { + await addComment(from, content, message) + handleClose() + view.dispatch({ + selection: EditorSelection.cursor(view.state.selection.main.anchor), + }) + } catch (err) { + debugConsole.error(err) + showGenericMessageModal( + t('add_comment_error_title'), + t('add_comment_error_message') + ) + } + setSubmitting(false) }, - [addComment, view, handleClose, from, to] + [addComment, view, handleClose, from, to, showGenericMessageModal, t] ) const { handleChange, handleKeyPress, content } = @@ -123,6 +130,7 @@ export const ReviewPanelAddComment: FC<{ t: value.spec.id as ThreadId, }} selectLineOnFocus={false} + disabled={submitting} >
- {error &&
{error.message}
}
) diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-change.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-change.tsx index 63dfdac714..799032a9cd 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-panel-change.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-change.tsx @@ -1,4 +1,4 @@ -import { memo } from 'react' +import { memo, useCallback, useState } from 'react' import { useRangesActionsContext } from '../context/ranges-context' import { Change, @@ -15,6 +15,7 @@ import { formatTimeBasedOnYear } from '@/features/utils/format-date' import { useChangesUsersContext } from '../context/changes-users-context' import { ReviewPanelChangeUser } from './review-panel-change-user' import { ReviewPanelEntry } from './review-panel-entry' +import { useModalsContext } from '@/features/ide-react/context/modals-context' export const ReviewPanelChange = memo<{ change: Change @@ -42,6 +43,27 @@ export const ReviewPanelChange = memo<{ const { acceptChanges, rejectChanges } = useRangesActionsContext() const permissions = usePermissionsContext() const changesUsers = useChangesUsersContext() + const { showGenericMessageModal } = useModalsContext() + + const [accepting, setAccepting] = useState(false) + + const acceptHandler = useCallback(async () => { + setAccepting(true) + try { + if (aggregate) { + await acceptChanges(change.id, aggregate.id) + } else { + await acceptChanges(change.id) + } + } catch (err) { + showGenericMessageModal( + t('accept_change_error_title'), + t('accept_change_error_description') + ) + } finally { + setAccepting(false) + } + }, [acceptChanges, aggregate, change.id, showGenericMessageModal, t]) if (!changesUsers) { // if users are not loaded yet, do not show "Unknown" user @@ -61,6 +83,7 @@ export const ReviewPanelChange = memo<{ position={change.op.p} docId={docId} hoverRanges={hoverRanges} + disabled={accepting} >
- @@ -48,7 +88,7 @@ export const ReviewPanelResolvedThread: FC<{ overlayProps={{ placement: 'bottom' }} description={t('delete')} > - diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-resolved-threads-menu.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-resolved-threads-menu.tsx index 5fcee53c37..16b38425b0 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-panel-resolved-threads-menu.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-resolved-threads-menu.tsx @@ -6,6 +6,7 @@ import useProjectRanges from '../hooks/use-project-ranges' import { useFileTreeData } from '@/shared/context/file-tree-data-context' import Icon from '@/shared/components/icon' import { Change, CommentOperation } from '../../../../../types/change' +import { ThreadId } from '../../../../../types/review-panel/review-panel' export const ReviewPanelResolvedThreadsMenu: FC = () => { const { t } = useTranslation() @@ -92,7 +93,7 @@ export const ReviewPanelResolvedThreadsMenu: FC = () => { return ( diff --git a/services/web/frontend/js/features/review-panel-new/context/ranges-context.tsx b/services/web/frontend/js/features/review-panel-new/context/ranges-context.tsx index 984b453176..44ccc8f1c3 100644 --- a/services/web/frontend/js/features/review-panel-new/context/ranges-context.tsx +++ b/services/web/frontend/js/features/review-panel-new/context/ranges-context.tsx @@ -16,6 +16,8 @@ import { import RangesTracker from '@overleaf/ranges-tracker' import { rejectChanges } from '@/features/source-editor/extensions/changes/reject-changes' import { useCodeMirrorViewContext } from '@/features/source-editor/components/codemirror-context' +import { postJSON } from '@/infrastructure/fetch-json' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' export type Ranges = { docId: string @@ -57,7 +59,7 @@ const RangesActionsContext = createContext(undefined) export const RangesProvider: FC = ({ children }) => { const view = useCodeMirrorViewContext() - + const { projectId } = useIdeReactContext() const [currentDoc] = useScopeValue( 'editor.sharejs_doc' ) @@ -115,8 +117,10 @@ export const RangesProvider: FC = ({ children }) => { const actions = useMemo( () => ({ - acceptChanges(...ids: string[]) { + async acceptChanges(...ids: string[]) { if (currentDoc?.ranges) { + const url = `/project/${projectId}/doc/${currentDoc.doc_id}/changes/accept` + await postJSON(url, { body: { change_ids: ids } }) currentDoc.ranges.removeChangeIds(ids) setRanges(buildRanges(currentDoc)) } @@ -127,7 +131,7 @@ export const RangesProvider: FC = ({ children }) => { } }, }), - [currentDoc, view] + [currentDoc, projectId, view] ) return ( diff --git a/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx b/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx index 212e9e0edb..81ddfdd85b 100644 --- a/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx +++ b/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx @@ -248,7 +248,6 @@ export const ThreadsProvider: FC = ({ children }) => { await postJSON(`/project/${projectId}/thread/${threadId}/messages`, { body: { content }, }) - // TODO: error_submitting_comment }, async editMessage( threadId: ThreadId, diff --git a/services/web/frontend/stylesheets/app/editor/review-panel-new.less b/services/web/frontend/stylesheets/app/editor/review-panel-new.less index 8216617854..c8ec25a36f 100644 --- a/services/web/frontend/stylesheets/app/editor/review-panel-new.less +++ b/services/web/frontend/stylesheets/app/editor/review-panel-new.less @@ -31,6 +31,11 @@ z-index: 1; } + .review-panel-entry.review-panel-entry-disabled { + opacity: 0.5; + pointer-events: none; + } + .review-panel-entry-indicator { display: none; } @@ -235,6 +240,11 @@ } } + .review-panel-resolved-disabled { + opacity: 0.5; + pointer-events: none; + } + .review-panel-resolved-comments-loading { text-align: center; } diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 47ada82c83..334ba4e20d 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -33,6 +33,8 @@ "accept_all": "Accept all", "accept_and_continue": "Accept and continue", "accept_change": "Accept change", + "accept_change_error_description": "There was an error accepting a track change. Please try again in a few moments.", + "accept_change_error_title": "Accept Change Error", "accept_invitation": "Accept invitation", "accept_or_reject_each_changes_individually": "Accept or reject each change individually", "accept_terms_and_conditions": "Accept terms and conditions", @@ -64,6 +66,8 @@ "add_another_token": "Add another token", "add_comma_separated_emails_help": "Separate multiple email addresses using the comma (,) character.", "add_comment": "Add comment", + "add_comment_error_message": "There was an error adding your comment. Please try again in a few moments.", + "add_comment_error_title": "Add Comment Error", "add_company_details": "Add Company Details", "add_email": "Add Email", "add_email_address": "Add email address", @@ -427,6 +431,8 @@ "delete_authentication_token_info": "You’re about to delete a Git authentication token. If you do, it can no longer be used to authenticate your identity when performing Git operations.", "delete_certificate": "Delete certificate", "delete_comment": "Delete comment", + "delete_comment_error_message": "There was an error deleting your comment. Please try again in a few moments.", + "delete_comment_error_title": "Delete Comment Error", "delete_comment_message": "You cannot undo this action.", "delete_comment_thread": "Delete comment thread", "delete_comment_thread_message": "This will delete the whole comment thread. You cannot undo this action.", @@ -526,6 +532,8 @@ "easily_manage_your_project_files_everywhere": "Easily manage your project files, everywhere", "easy_collaboration_for_students": "Easy collaboration for students. Supports longer or more complex projects.", "edit": "Edit", + "edit_comment_error_message": "There was an error editing your comment. Please try again in a few moments.", + "edit_comment_error_title": "Edit Comment Error", "edit_dictionary": "Edit Dictionary", "edit_dictionary_empty": "Your custom dictionary is empty.", "edit_dictionary_remove": "Remove from dictionary", @@ -1680,6 +1688,8 @@ "rename_project": "Rename Project", "renaming": "Renaming", "reopen": "Re-open", + "reopen_comment_error_message": "There was an error reopening your comment. Please try again in a few moments.", + "reopen_comment_error_title": "Reopen Comment Error", "replace_figure": "Replace figure", "replace_from_another_project": "Replace from another project", "replace_from_computer": "Replace from computer",