mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #15592 from overleaf/td-ide-page-more-modals
React IDE page: add out-of-sync modal and project deleted modal GitOrigin-RevId: 6081a40c3009e8ebc5b3f9752c4059af3d52966d
This commit is contained in:
parent
ecf7befe88
commit
c3afce73c1
13 changed files with 257 additions and 25 deletions
|
@ -466,6 +466,7 @@
|
|||
"helps_us_tailor_your_experience": "",
|
||||
"hide_configuration": "",
|
||||
"hide_document_preamble": "",
|
||||
"hide_local_file_contents": "",
|
||||
"hide_outline": "",
|
||||
"history": "",
|
||||
"history_add_label": "",
|
||||
|
@ -790,6 +791,8 @@
|
|||
"organize_projects": "",
|
||||
"other_logs_and_files": "",
|
||||
"other_output_files": "",
|
||||
"out_of_sync": "",
|
||||
"out_of_sync_detail": "",
|
||||
"output_file": "",
|
||||
"overall_theme": "",
|
||||
"overleaf": "",
|
||||
|
@ -843,6 +846,7 @@
|
|||
"please_select_a_project": "",
|
||||
"please_select_an_output_file": "",
|
||||
"please_set_main_file": "",
|
||||
"please_wait": "",
|
||||
"plus_additional_collaborators_document_history_track_changes_and_more": "",
|
||||
"plus_more": "",
|
||||
"plus_upgraded_accounts_receive": "",
|
||||
|
@ -875,6 +879,8 @@
|
|||
"project_not_linked_to_github": "",
|
||||
"project_ownership_transfer_confirmation_1": "",
|
||||
"project_ownership_transfer_confirmation_2": "",
|
||||
"project_renamed_or_deleted": "",
|
||||
"project_renamed_or_deleted_detail": "",
|
||||
"project_synced_with_git_repo_at": "",
|
||||
"project_synchronisation": "",
|
||||
"project_timed_out_enable_stop_on_first_error": "",
|
||||
|
@ -936,6 +942,7 @@
|
|||
"reject": "",
|
||||
"reject_all": "",
|
||||
"relink_your_account": "",
|
||||
"reload_editor": "",
|
||||
"remote_service_error": "",
|
||||
"remove": "",
|
||||
"remove_collaborator": "",
|
||||
|
@ -1068,6 +1075,7 @@
|
|||
"show_in_code": "",
|
||||
"show_in_pdf": "",
|
||||
"show_less": "",
|
||||
"show_local_file_contents": "",
|
||||
"show_outline": "",
|
||||
"show_x_more": "",
|
||||
"show_x_more_projects": "",
|
||||
|
@ -1362,6 +1370,7 @@
|
|||
"welcome_to_sl": "",
|
||||
"were_in_the_process_of_reducing_compile_timeout_which_may_affect_this_project": "",
|
||||
"were_in_the_process_of_reducing_compile_timeout_which_may_affect_your_project": "",
|
||||
"were_performing_maintenance": "",
|
||||
"weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_this_project": "",
|
||||
"weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_your_project": "",
|
||||
"what_does_this_mean": "",
|
||||
|
|
|
@ -21,7 +21,7 @@ export function Alerts() {
|
|||
return (
|
||||
<div className="global-alerts">
|
||||
{connectionState.forceDisconnected ? (
|
||||
<Alert bsStyle="warning" className="small">
|
||||
<Alert bsStyle="danger" className="small">
|
||||
<strong>{t('disconnected')}</strong>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
|
|
@ -10,6 +10,7 @@ import EditorNavigationToolbar from '@/features/ide-react/components/editor-navi
|
|||
import ChatPane from '@/features/chat/components/chat-pane'
|
||||
import { useLayoutEventTracking } from '@/features/ide-react/hooks/use-layout-event-tracking'
|
||||
import useSocketListeners from '@/features/ide-react/hooks/use-socket-listeners'
|
||||
import { useModalsContext } from '@/features/ide-react/context/modals-context'
|
||||
import { useOpenFile } from '@/features/ide-react/hooks/use-open-file'
|
||||
|
||||
// This is filled with placeholder content while the real content is migrated
|
||||
|
@ -25,7 +26,19 @@ export default function IdePage() {
|
|||
useOpenFile()
|
||||
|
||||
const [leftColumnDefaultSize, setLeftColumnDefaultSize] = useState(20)
|
||||
const { registerUserActivity } = useConnectionContext()
|
||||
const { connectionState, registerUserActivity } = useConnectionContext()
|
||||
const { showLockEditorMessageModal } = useModalsContext()
|
||||
|
||||
// Show modal when editor is forcefully disconnected
|
||||
useEffect(() => {
|
||||
if (connectionState.forceDisconnected) {
|
||||
showLockEditorMessageModal(connectionState.forcedDisconnectDelay)
|
||||
}
|
||||
}, [
|
||||
connectionState.forceDisconnected,
|
||||
connectionState.forcedDisconnectDelay,
|
||||
showLockEditorMessageModal,
|
||||
])
|
||||
|
||||
// Inform the connection manager when the user is active
|
||||
const listener = useCallback(
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Modal } from 'react-bootstrap'
|
||||
import AccessibleModal from '@/shared/components/accessible-modal'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export type LockEditorMessageModalProps = {
|
||||
delay: number // In seconds
|
||||
show: boolean
|
||||
}
|
||||
|
||||
function LockEditorMessageModal({ delay, show }: LockEditorMessageModalProps) {
|
||||
const { t } = useTranslation()
|
||||
const [secondsUntilRefresh, setSecondsUntilRefresh] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
setSecondsUntilRefresh(delay)
|
||||
|
||||
const timer = window.setInterval(() => {
|
||||
setSecondsUntilRefresh(seconds => Math.max(0, seconds - 1))
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
window.clearInterval(timer)
|
||||
}
|
||||
}
|
||||
}, [show, delay])
|
||||
|
||||
return (
|
||||
<AccessibleModal
|
||||
show={show}
|
||||
// It's not possible to hide this modal, but it's a required prop
|
||||
onHide={() => {}}
|
||||
className="lock-editor-modal"
|
||||
backdrop={false}
|
||||
keyboard={false}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title>{t('please_wait')}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{t('were_performing_maintenance', { seconds: secondsUntilRefresh })}
|
||||
</Modal.Body>
|
||||
</AccessibleModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default LockEditorMessageModal
|
|
@ -0,0 +1,81 @@
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import AccessibleModal from '@/shared/components/accessible-modal'
|
||||
import { useState } from 'react'
|
||||
import { useLocation } from '@/shared/hooks/use-location'
|
||||
|
||||
export type OutOfSyncModalProps = {
|
||||
editorContent: string
|
||||
show: boolean
|
||||
onHide: () => void
|
||||
}
|
||||
|
||||
function OutOfSyncModal({ editorContent, show, onHide }: OutOfSyncModalProps) {
|
||||
const { t } = useTranslation()
|
||||
const location = useLocation()
|
||||
const [editorContentShown, setEditorContentShown] = useState(false)
|
||||
const editorContentRows = (editorContent.match(/\n/g)?.length || 0) + 1
|
||||
|
||||
// Reload the page to avoid staying in an inconsistent state.
|
||||
// https://github.com/overleaf/issues/issues/3694
|
||||
function done() {
|
||||
onHide()
|
||||
location.reload()
|
||||
}
|
||||
|
||||
return (
|
||||
<AccessibleModal
|
||||
show={show}
|
||||
onHide={done}
|
||||
className="out-of-sync-modal"
|
||||
backdrop={false}
|
||||
keyboard={false}
|
||||
>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>{t('out_of_sync')}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body className="modal-body-share">
|
||||
<Trans
|
||||
i18nKey="out_of_sync_detail"
|
||||
components={[
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<br />,
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="/learn/Kb/Editor_out_of_sync_problems"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Body>
|
||||
<Button
|
||||
bsStyle="info"
|
||||
onClick={() => setEditorContentShown(shown => !shown)}
|
||||
>
|
||||
{editorContentShown
|
||||
? t('hide_local_file_contents')
|
||||
: t('show_local_file_contents')}
|
||||
</Button>
|
||||
{editorContentShown ? (
|
||||
<div className="text-preview">
|
||||
<textarea
|
||||
className="scroll-container"
|
||||
readOnly
|
||||
rows={editorContentRows}
|
||||
value={editorContent}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button bsStyle="info" onClick={done}>
|
||||
{t('reload_editor')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default OutOfSyncModal
|
|
@ -24,6 +24,7 @@ const initialState: ConnectionState = {
|
|||
inactiveDisconnect: false,
|
||||
lastConnectionAttempt: 0,
|
||||
reconnectAt: null,
|
||||
forcedDisconnectDelay: 0,
|
||||
error: '',
|
||||
}
|
||||
|
||||
|
@ -181,9 +182,10 @@ export class ConnectionManager extends Emitter<Events> {
|
|||
this.changeState({
|
||||
...this.state,
|
||||
forceDisconnected: true,
|
||||
forcedDisconnectDelay: delay,
|
||||
error,
|
||||
})
|
||||
setTimeout(() => this.disconnect(), delay * 1000)
|
||||
setTimeout(() => this.disconnect(), 1000)
|
||||
}
|
||||
|
||||
private onJoinProjectResponse({
|
||||
|
|
|
@ -13,6 +13,7 @@ export type ConnectionState = {
|
|||
forceDisconnected: boolean
|
||||
inactiveDisconnect: boolean
|
||||
reconnectAt: number | null
|
||||
forcedDisconnectDelay: number
|
||||
lastConnectionAttempt: number
|
||||
error: '' | ConnectionError
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { ConnectionState } from '../connection/types/connection-state'
|
|||
import { ConnectionManager } from '@/features/ide-react/connection/connection-manager'
|
||||
import { Socket } from '@/features/ide-react/connection/types/socket'
|
||||
import { secondsUntil } from '@/features/ide-react/connection/utils'
|
||||
import { useLocation } from '@/shared/hooks/use-location'
|
||||
|
||||
type ConnectionContextValue = {
|
||||
socket: Socket
|
||||
|
@ -28,6 +29,8 @@ const ConnectionContext = createContext<ConnectionContextValue | undefined>(
|
|||
)
|
||||
|
||||
export const ConnectionProvider: FC = ({ children }) => {
|
||||
const location = useLocation()
|
||||
|
||||
const [connectionManager] = useState(() => new ConnectionManager())
|
||||
const [connectionState, setConnectionState] = useState(
|
||||
connectionManager.state
|
||||
|
@ -69,6 +72,24 @@ export const ConnectionProvider: FC = ({ children }) => {
|
|||
connectionManager.disconnect()
|
||||
}, [connectionManager])
|
||||
|
||||
// Reload the page on force disconnect. Doing this in React-land means that we
|
||||
// can use useLocation(), which provides mockable location methods
|
||||
useEffect(() => {
|
||||
if (connectionState.forceDisconnected) {
|
||||
const timer = window.setTimeout(
|
||||
() => location.reload(),
|
||||
connectionState.forcedDisconnectDelay * 1000
|
||||
)
|
||||
return () => {
|
||||
window.clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
}, [
|
||||
connectionState.forceDisconnected,
|
||||
connectionState.forcedDisconnectDelay,
|
||||
location,
|
||||
])
|
||||
|
||||
const value = useMemo<ConnectionContextValue>(
|
||||
() => ({
|
||||
socket: connectionManager.socket,
|
||||
|
|
|
@ -115,7 +115,8 @@ export const EditorManagerProvider: FC = ({ children }) => {
|
|||
const { reportError, eventEmitter, eventLog } = useIdeReactContext()
|
||||
const { socket, disconnect } = useConnectionContext()
|
||||
const { view, setView } = useLayoutContext()
|
||||
const { showGenericMessageModal, genericModalVisible } = useModalsContext()
|
||||
const { showGenericMessageModal, genericModalVisible, showOutOfSyncModal } =
|
||||
useModalsContext()
|
||||
|
||||
const [showSymbolPalette, setShowSymbolPalette] = useScopeValue<boolean>(
|
||||
'editor.showSymbolPalette'
|
||||
|
@ -550,13 +551,7 @@ export const EditorManagerProvider: FC = ({ children }) => {
|
|||
|
||||
// Tell the user about the error state.
|
||||
setIsInErrorState(true)
|
||||
|
||||
// TODO: MIGRATION: Show out-of-sync modal
|
||||
// this.ide.showOutOfSyncModal(
|
||||
// 'Out of sync',
|
||||
// "Sorry, this file has gone out of sync and we need to do a full refresh. <br> <a target='_blank' rel='noopener noreferrer' href='/learn/Kb/Editor_out_of_sync_problems'>Please see this help guide for more information</a>",
|
||||
// editorContent
|
||||
// )
|
||||
showOutOfSyncModal(editorContent || '')
|
||||
|
||||
// Do not forceReopen the document.
|
||||
return
|
||||
|
@ -581,6 +576,7 @@ export const EditorManagerProvider: FC = ({ children }) => {
|
|||
reportError,
|
||||
setIsInErrorState,
|
||||
showGenericMessageModal,
|
||||
showOutOfSyncModal,
|
||||
t,
|
||||
])
|
||||
|
||||
|
|
|
@ -104,9 +104,9 @@ export const IdeReactProvider: FC = ({ children }) => {
|
|||
user_id: getMeta('ol-user_id'),
|
||||
project_id: projectId,
|
||||
// @ts-ignore
|
||||
client_id: socket.socket.sessionid,
|
||||
client_id: socket.socket?.sessionid,
|
||||
// @ts-ignore
|
||||
transport: socket.socket.transport,
|
||||
transport: socket.socket?.transport?.name,
|
||||
client_now: new Date(),
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import useSocketListener from '@/features/ide-react/hooks/use-socket-listener'
|
|||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { FileTreeFindResult } from '@/features/ide-react/types/file-tree'
|
||||
import { Project } from '../../../../../types/project'
|
||||
import { useModalsContext } from '@/features/ide-react/context/modals-context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type DocumentMetadata = {
|
||||
labels: string[]
|
||||
|
@ -45,12 +47,14 @@ const MetadataContext = createContext<MetadataContextValue | undefined>(
|
|||
)
|
||||
|
||||
export const MetadataProvider: FC = ({ children }) => {
|
||||
const { t } = useTranslation()
|
||||
const ide = useIdeContext()
|
||||
const { eventEmitter, projectId } = useIdeReactContext()
|
||||
const { socket } = useConnectionContext()
|
||||
const { onlineUsersCount } = useOnlineUsersContext()
|
||||
const { permissionsLevel } = useEditorContext()
|
||||
const { currentDocument } = useEditorManagerContext()
|
||||
const { showGenericMessageModal } = useModalsContext()
|
||||
|
||||
const [documents, setDocuments] = useState<DocumentsMetadata>({})
|
||||
|
||||
|
@ -180,17 +184,10 @@ export const MetadataProvider: FC = ({ children }) => {
|
|||
useEffect(() => {
|
||||
const handleProjectJoined = ({ project }: { project: Project }) => {
|
||||
if (project.deletedByExternalDataSource) {
|
||||
// TODO: MIGRATION: Show generic message modal here
|
||||
/*
|
||||
ide.showGenericMessageModal(
|
||||
'Project Renamed or Deleted',
|
||||
`\
|
||||
This project has either been renamed or deleted by an external data source such as Dropbox.
|
||||
We don't want to delete your data on Overleaf, so this project still contains your history and collaborators.
|
||||
If the project has been renamed please look in your project list for a new project under the new name.\
|
||||
`
|
||||
showGenericMessageModal(
|
||||
t('project_renamed_or_deleted'),
|
||||
t('project_renamed_or_deleted_detail')
|
||||
)
|
||||
*/
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
if (permissionsLevel !== 'readOnly') {
|
||||
|
@ -204,7 +201,13 @@ If the project has been renamed please look in your project list for a new proje
|
|||
return () => {
|
||||
eventEmitter.off('project:joined', handleProjectJoined)
|
||||
}
|
||||
}, [eventEmitter, loadProjectMetaFromServer, permissionsLevel])
|
||||
}, [
|
||||
eventEmitter,
|
||||
loadProjectMetaFromServer,
|
||||
permissionsLevel,
|
||||
showGenericMessageModal,
|
||||
t,
|
||||
])
|
||||
|
||||
const value = useMemo<MetadataContextValue>(
|
||||
() => ({
|
||||
|
|
|
@ -9,6 +9,10 @@ import {
|
|||
import GenericMessageModal, {
|
||||
GenericMessageModalOwnProps,
|
||||
} from '@/features/ide-react/components/modals/generic-message-modal'
|
||||
import OutOfSyncModal, {
|
||||
OutOfSyncModalProps,
|
||||
} from '@/features/ide-react/components/modals/out-of-sync-modal'
|
||||
import LockEditorMessageModal from '@/features/ide-react/components/modals/lock-editor-message-modal'
|
||||
|
||||
type ModalsContextValue = {
|
||||
genericModalVisible: boolean
|
||||
|
@ -16,6 +20,10 @@ type ModalsContextValue = {
|
|||
title: GenericMessageModalOwnProps['title'],
|
||||
message: GenericMessageModalOwnProps['message']
|
||||
) => void
|
||||
showOutOfSyncModal: (
|
||||
editorContent: OutOfSyncModalProps['editorContent']
|
||||
) => void
|
||||
showLockEditorMessageModal: (delay: number) => void
|
||||
}
|
||||
|
||||
const ModalsContext = createContext<ModalsContextValue | undefined>(undefined)
|
||||
|
@ -25,6 +33,18 @@ export const ModalsContextProvider: FC = ({ children }) => {
|
|||
const [genericMessageModalData, setGenericMessageModalData] =
|
||||
useState<GenericMessageModalOwnProps>({ title: '', message: '' })
|
||||
|
||||
const [shouldShowOutOfSyncModal, setShouldShowOutOfSyncModal] =
|
||||
useState(false)
|
||||
const [outOfSyncModalData, setOutOfSyncModalData] = useState({
|
||||
editorContent: '',
|
||||
})
|
||||
|
||||
const [shouldShowLockEditorModal, setShouldShowLockEditorModal] =
|
||||
useState(false)
|
||||
const [lockEditorModalData, setLockEditorModalData] = useState({
|
||||
delay: 0,
|
||||
})
|
||||
|
||||
const handleHideGenericModal = useCallback(() => {
|
||||
setShowGenericModal(false)
|
||||
}, [])
|
||||
|
@ -40,12 +60,33 @@ export const ModalsContextProvider: FC = ({ children }) => {
|
|||
[]
|
||||
)
|
||||
|
||||
const handleHideOutOfSyncModal = useCallback(() => {
|
||||
setShouldShowOutOfSyncModal(false)
|
||||
}, [])
|
||||
|
||||
const showOutOfSyncModal = useCallback((editorContent: string) => {
|
||||
setOutOfSyncModalData({ editorContent })
|
||||
setShouldShowOutOfSyncModal(true)
|
||||
}, [])
|
||||
|
||||
const showLockEditorMessageModal = useCallback((delay: number) => {
|
||||
setLockEditorModalData({ delay })
|
||||
setShouldShowLockEditorModal(true)
|
||||
}, [])
|
||||
|
||||
const value = useMemo<ModalsContextValue>(
|
||||
() => ({
|
||||
showGenericMessageModal,
|
||||
genericModalVisible: showGenericModal,
|
||||
showOutOfSyncModal,
|
||||
showLockEditorMessageModal,
|
||||
}),
|
||||
[showGenericMessageModal, showGenericModal]
|
||||
[
|
||||
showGenericMessageModal,
|
||||
showGenericModal,
|
||||
showOutOfSyncModal,
|
||||
showLockEditorMessageModal,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -56,6 +97,15 @@ export const ModalsContextProvider: FC = ({ children }) => {
|
|||
onHide={handleHideGenericModal}
|
||||
{...genericMessageModalData}
|
||||
/>
|
||||
<OutOfSyncModal
|
||||
{...outOfSyncModalData}
|
||||
show={shouldShowOutOfSyncModal}
|
||||
onHide={handleHideOutOfSyncModal}
|
||||
/>
|
||||
<LockEditorMessageModal
|
||||
{...lockEditorModalData}
|
||||
show={shouldShowLockEditorModal}
|
||||
/>
|
||||
</ModalsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -740,6 +740,7 @@
|
|||
"helps_us_tailor_your_experience": "This helps us tailor your Overleaf experience.",
|
||||
"hide_configuration": "Hide configuration",
|
||||
"hide_document_preamble": "Hide document preamble",
|
||||
"hide_local_file_contents": "Hide Local File Contents",
|
||||
"hide_outline": "Hide File outline",
|
||||
"history": "History",
|
||||
"history_add_label": "Add label",
|
||||
|
@ -1227,6 +1228,8 @@
|
|||
"other_output_files": "Download other output files",
|
||||
"other_sessions": "Other Sessions",
|
||||
"our_values": "Our values",
|
||||
"out_of_sync": "Out of sync",
|
||||
"out_of_sync_detail": "Sorry, this file has gone out of sync and we need to do a full refresh.<0 /><1>Please see this help guide for more information</1>",
|
||||
"output_file": "Output file",
|
||||
"over": "over",
|
||||
"over_x_templates_easy_getting_started": "There are thousands of __templates__ in our template gallery, so it’s really easy to get started, whether you’re writing a journal article, thesis, CV or something else.",
|
||||
|
@ -1309,6 +1312,7 @@
|
|||
"please_select_an_output_file": "Please Select an Output File",
|
||||
"please_set_a_password": "Please set a password",
|
||||
"please_set_main_file": "Please choose the main file for this project in the project menu. ",
|
||||
"please_wait": "Please wait",
|
||||
"plus_additional_collaborators_document_history_track_changes_and_more": "(plus additional collaborators, document history, track changes, and more).",
|
||||
"plus_more": "plus more",
|
||||
"plus_upgraded_accounts_receive": "Plus with an upgraded account you get",
|
||||
|
@ -1360,6 +1364,8 @@
|
|||
"project_owner_plus_10": "Project author + 10",
|
||||
"project_ownership_transfer_confirmation_1": "Are you sure you want to make <0>__user__</0> the owner of <1>__project__</1>?",
|
||||
"project_ownership_transfer_confirmation_2": "This action cannot be undone. The new owner will be notified and will be able to change project access settings (including removing your own access).",
|
||||
"project_renamed_or_deleted": "Project Renamed or Deleted",
|
||||
"project_renamed_or_deleted_detail": "This project has either been renamed or deleted by an external data source such as Dropbox. We don’t want to delete your data on Overleaf, so this project still contains your history and collaborators. If the project has been renamed please look in your project list for a new project under the new name.",
|
||||
"project_synced_with_git_repo_at": "This project is synced with the GitHub repository at",
|
||||
"project_synchronisation": "Project Synchronisation",
|
||||
"project_timed_out_enable_stop_on_first_error": "<0>Enable “Stop on first error”</0> to help you find and fix errors right away.",
|
||||
|
@ -1625,6 +1631,7 @@
|
|||
"show_in_code": "Show in code",
|
||||
"show_in_pdf": "Show in PDF",
|
||||
"show_less": "show less",
|
||||
"show_local_file_contents": "Show Local File Contents",
|
||||
"show_outline": "Show File outline",
|
||||
"show_x_more": "Show __x__ more",
|
||||
"show_x_more_projects": "Show __x__ more projects",
|
||||
|
@ -2027,6 +2034,7 @@
|
|||
"welcome_to_sl": "Welcome to __appName__!",
|
||||
"were_in_the_process_of_reducing_compile_timeout_which_may_affect_this_project": "We’re in the process of <0>reducing the compile timeout limit</0> on our free plan, which may affect this project in future.",
|
||||
"were_in_the_process_of_reducing_compile_timeout_which_may_affect_your_project": "We’re in the process of <0>reducing the compile timeout limit</0> on our free plan, which may affect your project in future.",
|
||||
"were_performing_maintenance": "We’re performing maintenance on Overleaf and you need to wait a moment. Sorry for any inconvenience. The editor will refresh automatically in __seconds__ seconds.",
|
||||
"weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_this_project": "We’ve recently <0>reduced the compile timeout limit</0> on our free plan, which may have affected this project.",
|
||||
"weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_your_project": "We’ve recently <0>reduced the compile timeout limit</0> on our free plan, which may have affected your project.",
|
||||
"what_does_this_mean": "What does this mean?",
|
||||
|
|
Loading…
Reference in a new issue