mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 21:23:45 +01:00
Zotero: allow collaborators to refresh files imported from Zotero; show importer's name in the header
This commit is contained in:
@@ -992,6 +992,7 @@
|
||||
"imported_from_papers_at_date": "",
|
||||
"imported_from_the_output_of_another_project_at_date": "",
|
||||
"imported_from_zotero_at_date": "",
|
||||
"imported_from_zotero_at_date_by": "",
|
||||
"importing": "",
|
||||
"importing_and_merging_changes_in_github": "",
|
||||
"inactive_projects": "",
|
||||
@@ -2672,13 +2673,12 @@
|
||||
"zotero_dynamic_sync_description": "",
|
||||
"zotero_groups_loading_error": "",
|
||||
"zotero_groups_relink": "",
|
||||
"zotero_imported_by_collaborator": "",
|
||||
"zotero_imported_by_unknown": "",
|
||||
"zotero_integration": "",
|
||||
"zotero_is_premium": "",
|
||||
"zotero_reference_loading_error": "",
|
||||
"zotero_reference_loading_error_expired": "",
|
||||
"zotero_reference_loading_error_forbidden": "",
|
||||
"zotero_reference_loading_error_not_linked": "",
|
||||
"zotero_sync_description": "",
|
||||
"zotero_upgrade_prompt_content": "",
|
||||
"zotero_upgrade_prompt_title": ""
|
||||
|
||||
@@ -15,6 +15,13 @@ export type LinkedFileData = {
|
||||
source_project_id: string
|
||||
source_output_file_path: string
|
||||
}
|
||||
zotero: {
|
||||
provider: 'zotero'
|
||||
zoteroGroupId?: string
|
||||
importedAt: Date | string
|
||||
importedByUserId?: string
|
||||
importedByName?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type BinaryFile<T extends keyof LinkedFileData = keyof LinkedFileData> =
|
||||
|
||||
@@ -780,7 +780,8 @@
|
||||
"imported_from_external_provider_at_date": "Importiert aus <0>__shortenedUrlHTML__</0> am __formattedDate__ __relativeDate__",
|
||||
"imported_from_mendeley_at_date": "Importiert von Mendeley am __formattedDate__ __relativeDate__",
|
||||
"imported_from_the_output_of_another_project_at_date": "Importiert aus der Ausgabe von <0>einem anderen Projekt</0>: __sourceOutputFilePathHTML__, am __formattedDate__ __relativeDate__",
|
||||
"imported_from_zotero_at_date": "Importiert von Zotero at __formattedDate__ __relativeDate__",
|
||||
"imported_from_zotero_at_date": "Importiert aus Zotero am __formattedDate__ __relativeDate__",
|
||||
"imported_from_zotero_at_date_by": "Importiert aus Zotero am __formattedDate__ __relativeDate__ von __importedByName__",
|
||||
"importing": "Importieren",
|
||||
"importing_and_merging_changes_in_github": "Änderungen werden in GitHub importiert und zusammengeführt.",
|
||||
"in_order_to_have_a_secure_account_make_sure_your_password": "Um dein Konto abzusichern, stelle sicher, dass dein Passwort",
|
||||
@@ -1726,12 +1727,11 @@
|
||||
"zotero_account_linked_successfully": "Zotero-Konto erfolgreich verknüpft.",
|
||||
"zotero_groups_loading_error": "Beim Laden von Gruppen von Zotero ist ein Fehler aufgetreten",
|
||||
"zotero_groups_relink": "Beim Zugriff auf die Zotero-Daten ist ein Fehler aufgetreten. Dies wurde wahrscheinlich durch fehlende Berechtigungen verursacht. Bitte verknüpfe dein Konto neu und versuche es erneut.",
|
||||
"zotero_imported_by_collaborator": "Diese Datei wurde aus Zotero von deinem Mitautor importiert. Du kannst sie nicht aktualisieren.",
|
||||
"zotero_imported_by_unknown": "Diese Datei wurde aus Zotero von einem unbekannten Benutzer importiert. Du kannst sie nicht aktualisieren.",
|
||||
"zotero_integration": "Zotero-Integration",
|
||||
"zotero_is_premium": "Zotero-Integration ist eine Premiumfunktion",
|
||||
"zotero_reference_loading_error": "Fehler, Referenzen konnten nicht von Mendeley geladen werden",
|
||||
"zotero_reference_loading_error_expired": "Zotero-Token abgelaufen, bitte verknüpfe dein Konto neu",
|
||||
"zotero_reference_loading_error_forbidden": "Referenzen konnten nicht von Zotero geladen werden. Bitte verlinke dein Zotero-Konto erneut und versuche es nochmal",
|
||||
"zotero_reference_loading_error_not_linked": "Aktualisierung fehlgeschlagen: Das zum Importieren der Datei verwendete Konto ist nicht mehr mit Zotero verknüpft oder wurde entfernt.",
|
||||
"zotero_sync_description": "Mit der Zotero-Integration kannst du deine Referenzen von Zotero in deine __appName__ Projekte importieren."
|
||||
}
|
||||
|
||||
@@ -1262,6 +1262,7 @@
|
||||
"imported_from_papers_at_date": "Imported from Papers at __formattedDate__ __relativeDate__",
|
||||
"imported_from_the_output_of_another_project_at_date": "Imported from the output of <0>Another project</0>: __sourceOutputFilePathHTML__, at __formattedDate__ __relativeDate__",
|
||||
"imported_from_zotero_at_date": "Imported from Zotero at __formattedDate__ __relativeDate__",
|
||||
"imported_from_zotero_at_date_by": "Imported from Zotero at __formattedDate__ __relativeDate__ by __importedByName__",
|
||||
"importing": "Importing",
|
||||
"importing_and_merging_changes_in_github": "Importing and merging changes in GitHub",
|
||||
"in_order_to_have_a_secure_account_make_sure_your_password": "To help keep your account secure, make sure your new password:",
|
||||
@@ -3337,13 +3338,12 @@
|
||||
"zotero_dynamic_sync_description": "With the Zotero integration, you can import your references into __appName__. You can either import all your references at once or dynamically search your Zotero library directly from __appName__.",
|
||||
"zotero_groups_loading_error": "There was an error loading groups from Zotero",
|
||||
"zotero_groups_relink": "There was an error accessing your Zotero data. This was likely caused by lack of permissions. Please re-link your account and try again.",
|
||||
"zotero_imported_by_collaborator": "This file was imported from Zotero by your collaborator. You cannot refresh it.",
|
||||
"zotero_imported_by_unknown": "This file was imported from Zotero by an unknown user. You cannot refresh it.",
|
||||
"zotero_integration": "Zotero integration",
|
||||
"zotero_is_premium": "Zotero integration is a premium feature",
|
||||
"zotero_reference_loading_error": "Error, could not load references from Zotero",
|
||||
"zotero_reference_loading_error_expired": "Zotero token expired, please re-link your account",
|
||||
"zotero_reference_loading_error_forbidden": "Could not load references from Zotero, please re-link your account and try again",
|
||||
"zotero_reference_loading_error_not_linked": "Refresh failed: the account used to import the file is no longer linked to Zotero or has been removed.",
|
||||
"zotero_sync_description": "With the Zotero integration you can import your references from Zotero into your __appName__ projects.",
|
||||
"zotero_upgrade_prompt_content": "Link your Zotero account to search and add your references from Zotero directly in your project—they’ll automatically be added to your .bib file. Or import them as a file into your __appName__ projects.",
|
||||
"zotero_upgrade_prompt_title": "Cite from Zotero"
|
||||
|
||||
@@ -235,6 +235,7 @@
|
||||
"import_from_github": "Импорт с GitHub",
|
||||
"import_to_sharelatex": "Импортировать в __appName__",
|
||||
"imported_from_zotero_at_date": "Импортировано из Zotero в __formattedDate__ __relativeDate__",
|
||||
"imported_from_zotero_at_date_by": "Импортировано из Zotero в __formattedDate__ __relativeDate__ пользователем __importedByName__",
|
||||
"importing": "Импорт",
|
||||
"importing_and_merging_changes_in_github": "Импорт и слияние изменений в GitHub",
|
||||
"inactive_projects": "Неактивные проекты",
|
||||
@@ -595,10 +596,9 @@
|
||||
"zh-CN": "Китайский",
|
||||
"zotero_account_linked_successfully": "Ваш аккаунт Zotero успешно подключён.",
|
||||
"zotero_groups_loading_error": "При загрузке групп из Zotero произошла ошибка",
|
||||
"zotero_imported_by_collaborator": "Этот файл был импортирован из Zotero вашим соавтором. Вы не можете обновить файл.",
|
||||
"zotero_imported_by_unknown": "Этот файл был импортирован из Zotero неизвестным пользователем. Вы не можете обновить файл.",
|
||||
"zotero_reference_loading_error": "Ошибка: не удалось загрузить ссылки из Zotero",
|
||||
"zotero_reference_loading_error_expired": "Срок действия токена Zotero истёк, пожалуйста, повторно подключите аккаунт",
|
||||
"zotero_reference_loading_error_forbidden": "Не удалось загрузить ссылки из Zotero, пожалуйста, повторно подключите аккаунт и попробуйте снова",
|
||||
"zotero_reference_loading_error_not_linked": "Не удалось обновить файл: учётная запись, использованная для импорта файла, больше не связана с Zotero или была удалена.",
|
||||
"zotero_sync_description": "Используя интеграцию с Zotero вы можете импортировать свои ссылки из Zotero в проекты __appName__."
|
||||
}
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import logger from '@overleaf/logger'
|
||||
import { callbackify } from '@overleaf/promise-utils'
|
||||
import UserGetter from '../../../../app/src/Features/User/UserGetter.mjs'
|
||||
import LinkedFilesHandler from '../../../../app/src/Features/LinkedFiles/LinkedFilesHandler.mjs'
|
||||
import LinkedFilesErrors from '../../../../app/src/Features/LinkedFiles/LinkedFilesErrors.mjs'
|
||||
import ZoteroApiClient from './ZoteroApiClient.mjs'
|
||||
import { ZoteroForbiddenError, ZoteroAccountNotLinkedError } from './ZoteroApiClient.mjs'
|
||||
import LinkedFilesErrors from '../../../../app/src/Features/LinkedFiles/LinkedFilesErrors.mjs'
|
||||
import { Project } from '../../../../app/src/models/Project.mjs'
|
||||
|
||||
const {
|
||||
FeatureNotAvailableError,
|
||||
AccessDeniedError,
|
||||
RemoteServiceError,
|
||||
NotOriginalImporterError,
|
||||
} = LinkedFilesErrors
|
||||
|
||||
/**
|
||||
* Create a linked .bib file from Zotero (either My Library or a Group Library).
|
||||
*
|
||||
* linkedFileData shape:
|
||||
* { provider: 'zotero', zoteroGroupId?: string, importedAt: string }
|
||||
* {
|
||||
* provider: 'zotero'
|
||||
* zoteroGroupId?: string
|
||||
* importedAt: Date | string
|
||||
* importedByUserId?: string
|
||||
* importedByName?: string
|
||||
* }
|
||||
*
|
||||
* - If zoteroGroupId is present, export that group's library.
|
||||
* - Otherwise, export the user's personal library ("My Library").
|
||||
@@ -29,14 +33,15 @@ async function createLinkedFile(
|
||||
parentFolderId,
|
||||
userId
|
||||
) {
|
||||
logger.info(
|
||||
logger.debug(
|
||||
{ projectId, userId, groupId: linkedFileData.zoteroGroupId },
|
||||
'creating Zotero linked file'
|
||||
)
|
||||
|
||||
linkedFileData.importedByUserId = userId
|
||||
linkedFileData.importedByName = await _getUserName(userId)
|
||||
|
||||
const bibtex = await _getBibtex(userId, linkedFileData)
|
||||
const bibtex = await _getBibtex(linkedFileData)
|
||||
|
||||
const file = await LinkedFilesHandler.promises.importContent(
|
||||
projectId,
|
||||
@@ -59,29 +64,12 @@ async function refreshLinkedFile(
|
||||
parentFolderId,
|
||||
userId
|
||||
) {
|
||||
logger.info(
|
||||
logger.debug(
|
||||
{ projectId, userId, groupId: linkedFileData.zoteroGroupId },
|
||||
'refreshing Zotero linked file'
|
||||
)
|
||||
|
||||
if (linkedFileData.importedByUserId) {
|
||||
if (linkedFileData.importedByUserId !== userId) {
|
||||
throw new NotOriginalImporterError(
|
||||
'Only the user who created the Zotero-linked file can refresh this file'
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// No owner metadata (legacy files). Only project owner may refresh.
|
||||
const project = await Project.findById(projectId, 'owner_ref').lean()
|
||||
if (!project || String(project.owner_ref) !== String(userId)) {
|
||||
throw new NotOriginalImporterError(
|
||||
'Only the user who created the Zotero-linked file can refresh this file'
|
||||
)
|
||||
}
|
||||
linkedFileData.importedByUserId = userId
|
||||
}
|
||||
|
||||
const bibtex = await _getBibtex(userId, linkedFileData)
|
||||
const bibtex = await _getBibtex(linkedFileData)
|
||||
|
||||
const file = await LinkedFilesHandler.promises.importContent(
|
||||
projectId,
|
||||
@@ -94,7 +82,8 @@ async function refreshLinkedFile(
|
||||
return file._id
|
||||
}
|
||||
|
||||
async function _getBibtex(userId, linkedFileData) {
|
||||
async function _getBibtex(linkedFileData) {
|
||||
const userId = linkedFileData.importedByUserId
|
||||
try {
|
||||
if (linkedFileData.zoteroGroupId) {
|
||||
return await ZoteroApiClient.getGroupLibraryBibtex(
|
||||
@@ -118,14 +107,35 @@ async function _getBibtex(userId, linkedFileData) {
|
||||
function _sanitizeData(data) {
|
||||
return {
|
||||
provider: 'zotero',
|
||||
zoteroGroupId: data.zoteroGroupId || undefined,
|
||||
...(data.zoteroGroupId && {
|
||||
zoteroGroupId: data.zoteroGroupId,
|
||||
}),
|
||||
importedAt: data.importedAt,
|
||||
importedByUserId: data.importedByUserId || undefined,
|
||||
...(data.importedByUserId && {
|
||||
importedByUserId: data.importedByUserId,
|
||||
}),
|
||||
importedByName: data.importedByName || 'Unknown',
|
||||
}
|
||||
}
|
||||
|
||||
async function _getUserName(userId) {
|
||||
let user = null
|
||||
try {
|
||||
user = await UserGetter.promises.getUser(userId, {'email': 1, 'first_name': 1, 'last_name': 1})
|
||||
}
|
||||
catch (err) {
|
||||
logger.error({ userId, err }, 'failed to get user info')
|
||||
}
|
||||
if (!user) return 'Unknown'
|
||||
|
||||
const { email, first_name, last_name } = user
|
||||
const name = (first_name || last_name) ?
|
||||
[first_name, last_name].filter(n => n != null).join(' ') : email
|
||||
return name || 'Unknown'
|
||||
}
|
||||
|
||||
export default {
|
||||
createLinkedFile: callbackify(createLinkedFile),
|
||||
refreshLinkedFile: callbackify(refreshLinkedFile),
|
||||
promises: { createLinkedFile, refreshLinkedFile },
|
||||
promises: { createLinkedFile, refreshLinkedFile }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { formatTime, relativeDate } from '@/features/utils/format-date'
|
||||
import { LinkedFileIcon } from '@/features/file-view/components/file-view-icons'
|
||||
import type { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import { hasProvider } from '@/features/file-view/types/binary-file'
|
||||
import type { LinkedFile, LinkedFileData } from '@/features/file-view/types/binary-file'
|
||||
|
||||
type TPRFileViewInfoProps = {
|
||||
file: BinaryFile
|
||||
file: LinkedFile<keyof LinkedFileData>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15,31 +17,31 @@ type TPRFileViewInfoProps = {
|
||||
export function TPRFileViewInfo({ file }: TPRFileViewInfoProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (file.linkedFileData?.provider !== 'zotero') {
|
||||
return null
|
||||
}
|
||||
if (!hasProvider(file, 'zotero')) return null
|
||||
|
||||
const importedAt = (file.linkedFileData as any)?.importedAt || file.created
|
||||
const formattedDate = formatTime(importedAt)
|
||||
const relative = relativeDate(importedAt)
|
||||
|
||||
const groupId = (file.linkedFileData as any)?.zoteroGroupId
|
||||
const importedByUserId = (file.linkedFileData as any)?.importedByUserId
|
||||
const importedByName = (file.linkedFileData as any)?.importedByName || 'Unknown'
|
||||
|
||||
return (
|
||||
<p>
|
||||
<div>
|
||||
<span>
|
||||
<LinkedFileIcon />
|
||||
|
||||
{t('imported_from_zotero_at_date', {
|
||||
formattedDate,
|
||||
relativeDate: relative,
|
||||
})}
|
||||
</span>
|
||||
{groupId && (
|
||||
<span className="text-muted small"> (Group: {groupId})</span>
|
||||
)}
|
||||
</div>
|
||||
<LinkedFileIcon />
|
||||
|
||||
{(importedByUserId === getMeta('ol-user_id')) ? (
|
||||
t('imported_from_zotero_at_date', {
|
||||
formattedDate,
|
||||
relativeDate: relative,
|
||||
})
|
||||
) : (
|
||||
t('imported_from_zotero_at_date_by', {
|
||||
formattedDate,
|
||||
relativeDate: relative,
|
||||
importedByName,
|
||||
})
|
||||
)}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
+3
-45
@@ -1,55 +1,13 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import OLNotification from '@/shared/components/ol/ol-notification'
|
||||
import getMeta from '@/utils/meta'
|
||||
import type { LinkedFile, LinkedFileData } from '@/features/file-view/types/binary-file'
|
||||
|
||||
type TPRFileViewNotOriginalImporterProps = {
|
||||
file: BinaryFile
|
||||
type TPRFileViewInfoProps = {
|
||||
file: LinkedFile<keyof LinkedFileData>
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a warning if the current user is not the original importer of this Zotero file.
|
||||
* Registered via overleafModuleImports.tprFileViewNotOriginalImporter.
|
||||
*/
|
||||
export function TPRFileViewNotOriginalImporter({
|
||||
file,
|
||||
}: TPRFileViewNotOriginalImporterProps) {
|
||||
const provider = file.linkedFileData?.provider
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (provider !== 'zotero') {
|
||||
return null
|
||||
}
|
||||
|
||||
const currentUserId =
|
||||
(getMeta('ol-user') as any)?._id || (getMeta('ol-user_id') as string)
|
||||
const importedByUserId = (file.linkedFileData as any)?.importedByUserId
|
||||
|
||||
if (provider !== 'zotero') {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!importedByUserId) {
|
||||
return (
|
||||
<div className="file-view-error">
|
||||
<OLNotification
|
||||
type="warning"
|
||||
content={t('zotero_imported_by_unknown')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// TODO: fetch the collaborator's name and show it
|
||||
if (currentUserId && importedByUserId !== currentUserId) {
|
||||
return (
|
||||
<div className="file-view-error">
|
||||
<OLNotification
|
||||
type="warning"
|
||||
content={t('zotero_imported_by_collaborator')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
+9
-27
@@ -1,10 +1,10 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OLButton from '@/shared/components/ol/ol-button'
|
||||
import type { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import getMeta from '@/utils/meta'
|
||||
import type { LinkedFile, LinkedFileData } from '@/features/file-view/types/binary-file'
|
||||
import { hasProvider } from '@/features/file-view/types/binary-file'
|
||||
|
||||
type TPRFileViewRefreshButtonProps = {
|
||||
file: BinaryFile
|
||||
file: LinkedFile<keyof LinkedFileData>
|
||||
refreshFile: (isTPR: boolean | null) => void
|
||||
refreshing: boolean
|
||||
}
|
||||
@@ -20,35 +20,17 @@ export function TPRFileViewRefreshButton({
|
||||
refreshing,
|
||||
}: TPRFileViewRefreshButtonProps) {
|
||||
const { t } = useTranslation()
|
||||
const provider = (file.linkedFileData as Record<string, unknown>)?.provider
|
||||
const currentUserId =
|
||||
(getMeta('ol-user') as any)?._id || (getMeta('ol-user_id') as string)
|
||||
const importedByUserId = (file.linkedFileData as any)?.importedByUserId
|
||||
const isOriginalImporter =
|
||||
provider === 'zotero' &&
|
||||
currentUserId &&
|
||||
importedByUserId === currentUserId
|
||||
const zoteroIsProvider = hasProvider(file, 'zotero')
|
||||
const disabled = (zoteroIsProvider && !importedByUserId) ? true : false
|
||||
|
||||
if (provider !== 'zotero' || isOriginalImporter) {
|
||||
// Zotero or default refresh for originator only
|
||||
return (
|
||||
<OLButton
|
||||
variant="primary"
|
||||
onClick={() => refreshFile(provider === 'zotero' ? true : null)}
|
||||
disabled={refreshing}
|
||||
isLoading={refreshing}
|
||||
loadingLabel={t('refreshing')}
|
||||
>
|
||||
{t('refresh')}
|
||||
</OLButton>
|
||||
)
|
||||
}
|
||||
|
||||
// collaborator of Zotero file should not refresh
|
||||
return (
|
||||
<OLButton
|
||||
variant="primary"
|
||||
disabled
|
||||
onClick={() => refreshFile(zoteroIsProvider ? true : null)}
|
||||
disabled={refreshing || disabled}
|
||||
isLoading={refreshing}
|
||||
loadingLabel={t('refreshing')}
|
||||
>
|
||||
{t('refresh')}
|
||||
</OLButton>
|
||||
|
||||
+14
-20
@@ -1,9 +1,10 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OLNotification from '@/shared/components/ol/ol-notification'
|
||||
import type { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import type { LinkedFile, LinkedFileData } from '@/features/file-view/types/binary-file'
|
||||
import { hasProvider } from '@/features/file-view/types/binary-file'
|
||||
|
||||
type TPRFileViewRefreshErrorProps = {
|
||||
file: BinaryFile
|
||||
file: LinkedFile<keyof LinkedFileData>
|
||||
refreshError: string
|
||||
}
|
||||
|
||||
@@ -17,25 +18,18 @@ export function TPRFileViewRefreshError({
|
||||
}: TPRFileViewRefreshErrorProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (file.linkedFileData?.provider !== 'zotero') {
|
||||
// Not a Zotero file — fall back to default error display
|
||||
return (
|
||||
<div className="file-view-error">
|
||||
<OLNotification type="error" content={refreshError} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let message = refreshError
|
||||
if (refreshError === 'forbidden' || refreshError?.includes('403')) {
|
||||
message = t('zotero_reference_loading_error_forbidden')
|
||||
} else if (
|
||||
refreshError === 'expired' ||
|
||||
refreshError?.includes('token expired')
|
||||
) {
|
||||
message = t('zotero_reference_loading_error_expired')
|
||||
} else if (!message) {
|
||||
message = t('zotero_reference_loading_error')
|
||||
|
||||
if (hasProvider(file, 'zotero')) {
|
||||
if (!refreshError) {
|
||||
message = t('zotero_reference_loading_error')
|
||||
} else if (refreshError?.includes('not linked')) {
|
||||
message = t('zotero_reference_loading_error_not_linked')
|
||||
} else if (refreshError === 'forbidden' || refreshError?.includes('403')) {
|
||||
message = t('zotero_reference_loading_error_forbidden')
|
||||
} else if (refreshError === 'expired' || refreshError?.includes('token expired')) {
|
||||
message = t('zotero_reference_loading_error_expired')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user