Merge branch 'develop' into feature/management

# Conflicts:
#	frontend.macos.code-workspace
#	packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/index.js
#	packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/index.js
#	yarn.lock
This commit is contained in:
Dmitry Sychugov 2023-11-09 19:13:20 +05:00
commit 3170833b98
374 changed files with 9006 additions and 4205 deletions

201
.vscode/tasks.json vendored
View File

@ -1,63 +1,186 @@
{
"version": "2.0.0",
"problemMatcher": [],
"presentation": {
"focus": true,
"close": true
},
"tasks": [
{
"label": "Frontend | Start",
"label": "Backend | build EE",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/build.backend.docker.py",
"type": "shell",
"command": "cd ${workspaceFolder} ; yarn start"
},
{
"label": "Backend | Build",
"type": "shell",
"command": "${workspaceFolder}/build/build.backend.docker.sh",
"windows": {
"command": "${workspaceFolder}\\build\\build.bat"
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Backend | Start",
"label": "Backend | build CE",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/build.backend.docker.py -c",
"type": "shell",
"command": "${workspaceFolder}/build/start/start.backend.docker.sh",
"windows": {
"command": "${workspaceFolder}\\build\\start\\start.bat"
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Backend | Restart",
"label": "Backend | build SAAS",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/build.backend.docker.py -s",
"type": "shell",
"command": "${workspaceFolder}/build/start/restart.backend.docker.sh",
"windows": {
"command": "${workspaceFolder}\\build\\start\\restart.bat"
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Backend | Stop",
"label": "Backend | build +DNS",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/build.backend.docker.py -d",
"type": "shell",
"command": "${workspaceFolder}/build/start/stop.backend.docker.sh",
"windows": {
"command": "${workspaceFolder}\\build\\start\\stop.bat"
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
}
],
"inputs": [
{
"id": "start-param",
"description": "Command",
"default": "Start",
"type": "pickString",
"options": ["Start", "Restart", "Stop"]
},
{
"id": "local-docker",
"description": "Manipulation",
"default": "Local",
"type": "pickString",
"options": ["Local", "Docker"]
"label": "Backend | clear",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/clear.backend.docker.py",
"type": "shell",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Backend | stop",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/start/stop.backend.docker.py",
"type": "shell",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Backend | restart",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/start/restart.backend.docker.py",
"type": "shell",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Backend | start",
"command": "cd ${workspaceFolder}/../ ; ${command:python.interpreterPath} buildtools/start/start.backend.docker.py",
"type": "shell",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Frontend | install",
"type": "shell",
"command": "cd ${workspaceFolder} ; yarn install",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Frontend | build",
"type": "shell",
"command": "cd ${workspaceFolder} ; yarn build",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Frontend | start",
"type": "shell",
"command": "cd ${workspaceFolder} ; yarn start",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
},
{
"label": "Frontend | start-prod",
"type": "shell",
"command": "cd ${workspaceFolder} ; yarn start-prod",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new",
"focus": true,
"close": false
}
}
]
}

View File

@ -6,15 +6,15 @@
},
{
"name": "🚀 @docspace/client",
"path": "packages\\client"
"path": "packages/client"
},
{
"name": "🔑 @docspace/login",
"path": "packages\\login"
"path": "packages/login"
},
{
"name": "📄 @docspace/editor",
"path": "packages\\editor"
"path": "packages/editor"
},
{
"name": "🗂 @docspace/management",
@ -22,11 +22,11 @@
},
{
"name": "📦 @docspace/common",
"path": "packages\\common"
"path": "packages/common"
},
{
"name": "📦 @docspace/components",
"path": "packages\\components"
"path": "packages/components"
}
],
"settings": {
@ -45,13 +45,94 @@
],
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
},
"VsCodeTaskButtons.tasks": [
{
"label": "Server",
"tasks": [
{
"label": "Docker : Build-EE",
"task": "Backend | build EE",
"tooltip": "🛠️ Start the \"backend docker build EE\" task"
},
{
"label": "Docker : Build-CE",
"task": "Backend | build CE",
"tooltip": "🛠️ Start the \"backend docker build CE\" task"
},
{
"label": "Docker : Build-EE+DNS",
"task": "Backend | build +DNS",
"tooltip": "🛠️ Start the \"backend docker build EE+dnsmasq\" task"
},
{
"label": "Docker : Build-SAAS",
"task": "Backend | build SAAS",
"tooltip": "🛠️ Start the \"backend docker build SAAS\" task"
},
{
"label": "Docker : Clear",
"task": "Backend | clear",
"tooltip": "🛠️ Start the \"backend docker clear\" task"
},
{
"label": "Docker : Stop",
"task": "Backend | stop",
"tooltip": "🛠️ Start the \"backend docker stop\" task"
},
{
"label": "Docker : Start",
"task": "Backend | start",
"tooltip": "🛠️ Start the \"backend docker start\" task"
},
{
"label": "Docker : Restart",
"task": "Backend | restart",
"tooltip": "🛠️ Start the \"backend docker restart\" task"
}
],
"tooltip": "🛠️ Server tasks"
},
{
"label": "Client",
"tasks": [
{
"label": "Install",
"task": "Frontend | install",
"tooltip": "🛠️ Start the \"frontend install packages\" task"
},
{
"label": "Build",
"task": "Frontend | build",
"tooltip": "🛠️ Start the \"frontend build\" task"
},
{
"label": "Start",
"task": "Frontend | start",
"tooltip": "🛠️ Start the \"frontend start\" task"
},
{
"label": "Start prod",
"task": "Frontend | start-prod",
"tooltip": "🛠️ Start the \"frontend start production\" task"
}
],
"tooltip": "🛠️ Client tasks"
}
]
},
"extensions": {
"recommendations": [
"folke.vscode-monorepo-workspace",
"orta.vscode-jest",
"firsttris.vscode-jest-runner"
"firsttris.vscode-jest-runner",
"spencerwmiles.vscode-task-buttons",
"esbenp.prettier-vscode",
"formulahendry.auto-close-tag",
"formulahendry.auto-complete-tag",
"formulahendry.auto-rename-tag",
"mrmlnc.vscode-duplicate",
"ms-python.python"
]
}
}

View File

@ -1,58 +0,0 @@
{
"folders": [
{
"name": "🌐 root",
"path": "."
},
{
"name": "🚀 @docspace/client",
"path": "packages/client"
},
{
"name": "🔑 @docspace/login",
"path": "packages/login"
},
{
"name": "📄 @docspace/editor",
"path": "packages/editor"
},
{
"name": "🗂 @docspace/management",
"path": "packages/management"
},
{
"name": "📦 @docspace/common",
"path": "packages/common"
},
{
"name": "📦 @docspace/components",
"path": "packages/components"
}
],
"settings": {
"window.zoomLevel": 0,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"liveServer.settings.multiRootWorkspaceName": "✨ docspace",
"cSpell.words": ["docspace", "browserslist", "debuginfo", "doceditor"],
"jest.autoRun": {},
"jest.disabledWorkspaceFolders": [
"🌐 root",
"🔑 @docspace/login",
"🚀 @docspace/client",
"📄 @docspace/editor",
"📦 @docspace/common",
"🗂 @docspace/management"
],
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
},
"extensions": {
"recommendations": [
"folke.vscode-monorepo-workspace",
"orta.vscode-jest",
"firsttris.vscode-jest-runner"
]
}
}

View File

@ -23,7 +23,6 @@
"storybook-serve": "yarn workspace @docspace/components run storybook-serve",
"test": "yarn workspace @docspace/components test",
"wipe": "shx rm -rf node_modules yarn.lock packages/**/node_modules",
"debug-info": "auto-changelog --unreleased-only --template debuginfo --output public/debuginfo.md",
"licenses-audit": "yarn licenses audit --output-csv=licenses.csv --config=licenses.config.js --summary"
},
"old-scripts": {
@ -35,7 +34,6 @@
"e2e.test:translation": "yarn workspaces foreach -vptiR --from '{@docspace/client,@docspace/login}' run test:translation:model"
},
"devDependencies": {
"auto-changelog": "file:./packages/auto-changelog-2.3.1.tgz",
"he": "^1.2.0",
"shx": "^0.3.4",
"terser": "^5.16.6"

Binary file not shown.

View File

@ -51,9 +51,10 @@
"react-avatar-editor": "^13.0.0",
"react-colorful": "^5.6.1",
"react-hotkeys-hook": "^3.4.7",
"react-markdown": "^7.1.2",
"react-markdown": "^9.0.0",
"react-smartbanner": "^5.1.4",
"react-string-format": "^0.1.4",
"remark-gfm": "^4.0.0",
"windows-iana": "^5.1.0"
},
"devDependencies": {

View File

@ -29,7 +29,6 @@
"AuditSubheader": "يتيح لك القسم الفرعي تصفح قائمة أحدث التغييرات (الإنشاء والتعديل والحذف وما إلى ذلك) التي أجراها المستخدمون على الكيانات (الغرف والفرص والملفات وما إلى ذلك) داخل DocSpace الخاص بك.",
"AuditTrailNav": "سجل تدقيق",
"AutoBackup": "النسخ الاحتياطي التلقائي",
"AutoBackupDescription": "استخدم هذا الخيار للنسخ الاحتياطي التلقائي لبيانات DocSpace.",
"AutoBackupHelp": "يُستخدم خيار <strong> النسخ الاحتياطي التلقائي </ strong> لأتمتة عملية النسخ الاحتياطي لبيانات DocSpace لتتمكن من استعادتها لاحقًا إلى خادم محلي.",
"AutoBackupHelpNote": "اختر تخزين البيانات وفترة النسخ الاحتياطي التلقائي والحد الأقصى لعدد النسخ المحفوظة. <br/> <strong> ملاحظة: </ strong> قبل أن تتمكن من حفظ بيانات النسخ الاحتياطي في حساب طرف ثالث (DropBox أو Box.com أو OneDrive أو Google Drive) ، ستحتاج إلى ربط هذا الحساب بالمجلد العام {{organizationName}}.",
"AutoSavePeriod": "فترة الحفظ التلقائي",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Alt bölmə sizə DocSpace-dəki (otaqlara, imkanlara, fayllara və s.) istifadəçilər tərəfindən edilən ən son dəyişikliklərin (yaradılması, dəyişdirilməsi, silinməsi və s.) siyahısını nəzərdən keçirməyə imkan verir.",
"AuditTrailNav": "Audit izi",
"AutoBackup": "Avtomatik ehtiyat nüsxəsi",
"AutoBackupDescription": "Portal məlumatlarının avtomatik yedəklənməsi üçün bu seçimdən istifadə edin.",
"AutoBackupHelp": "<strong>Avtomatik ehtiyat nüsxə</strong> seçimi daha sonra yerli serverə bərpa edə bilmək üçün DocSpace məlumatlarının ehtiyat nüsxəsini çıxarmaq prosesini avtomatlaşdırmaq məqsədilə istifadə olunur.",
"AutoBackupHelpNote": "Məlumat yaddaşını, avtomatik yedəkləmə müddətini və saxlanılan nüsxələrin maksimum sayını seçin. <br/><strong>Qeyd:</strong> ehtiyat nüsxəsini üçüncü tərəf hesabında (DropBox, Box.com, OneDrive və ya Google Drive) saxlamazdan əvvəl bu hesabı {{organizationName}} Ümumi qovluğuna qoşmalısınız.",
"AutoSavePeriod": "Avtomatik yadda saxlama müddəti",
@ -86,7 +85,6 @@
"DNSSettingsTooltipStandalone": "Siz istədiyiniz ləqəbi 'Portal Ünvanı' sahəsinə daxil edə, və ya 'İstifadəçi domen adı' qutusunu yoxlaya və aşağı sahədə ONLYOFFICE üçün öz domen adınızı təyin edə bilərsiniz. Etdiyiniz dəyişiklikləri tətbiq etmək üçün, bölmənin aşağı tərəfində 'Qeyd et' düyməsini basın.",
"DocumentService": "Sənəd Xidməti",
"DocumentServiceLocationHeader": "Sənəd Xidməti Yeri",
"DocumentServiceLocationHeaderHelp": "Document Service sənədin redaktəsini həyata keçirməyə və sənəd faylını müvafiq OfficeOpen XML formatına çevirməyə imkan verən server xidmətidir.",
"DocumentServiceLocationHeaderInfo": "Sənəd xidmətinin yeri quraşdırılmış sənəd xidmətləri ilə serverin ünvanını müəyyən edir. ",
"DocumentServiceLocationUrlApi": "Sənəd Redaktə Xidmətinin Ünvanı",
"DocumentServiceLocationUrlInternal": "İcma Serverindən sorğular üçün DocSpace ünvanı",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Подразделът Ви позволява да преглеждате списъка с най-новите промени (създаване, модифициране, изтриване и т.н.), направени от потребителите на обектите (стаи, възможности, файлове и т.н.) във вашия DocSpace.",
"AuditTrailNav": "Одитна пътека",
"AutoBackup": "Автоматично архивиране",
"AutoBackupDescription": "Използвайте тази опция за автоматично архивиране на данните за портала.",
"AutoBackupHelp": "Опцията <strong>Автоматично архивиране</strong> се използва за автоматизиране на процеса на архивиране на данни за DocSpace, за да може да се възстанови по-късно на локален сървър.",
"AutoBackupHelpNote": "Изберете хранилището на данни, автоматичния период на резервно копие и максималния брой запазени копия.<br/><strong>Забележка:</strong> преди да можете да запазите резервните данни в профил на трета страна (DropBox, Box.com, OneDrive или Google Диск), ще трябва да свържете този профил към папката 'Общи документи' {{organizationName}}.",
"AutoSavePeriod": "Период на автоматично запазване",
@ -86,7 +85,6 @@
"DNSSettingsTooltipStandalone": "Поставете отметка в полето „Име на домейн по избор“ и посочете името на собствения си домейн за ONLYOFFICE пространството в полето по-долу. За да влязат в сила зададените от вас параметри, щракнете върху бутона „Запазване“ в долната част на секцията.",
"DocumentService": "Служба за документи",
"DocumentServiceLocationHeader": "Местоположение на услугата за документи",
"DocumentServiceLocationHeaderHelp": "Document Service е услуга на сървъра, която позволява извършването на редактиране на документа и позволява конвертиране на документа в съответния формат OfficeOpen XML.",
"DocumentServiceLocationHeaderInfo": "Местоположението на услугата документ указва адреса на сървъра с инсталираните услуги за документи. ",
"DocumentServiceLocationUrlApi": "Адрес на услугата за редактиране на документи",
"DocumentServiceLocationUrlInternal": "Адрес на услугата за документи за заявки от DocSpace",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Tato podsekce umožňuje procházet seznam nejnovějších změn (vytvoření, úpravy, odstranění atd.), které uživatelé provedli v entitách (místnostech, příležitostech, souborech atd.) v rámci vašeho prostoru DocSpace.",
"AuditTrailNav": "Audit Trail",
"AutoBackup": "Automatická záloha",
"AutoBackupDescription": "Použijte tuto možnost pro automatické zálohování prostorových dat.",
"AutoBackupHelp": "Možnost <strong>Automatického zálohování</strong> se používá pro automatizaci procesu zálohování dat DocSpace, aby je bylo možné později obnovit z místního serveru.",
"AutoBackupHelpNote": "Vyberte si místo uložení, periodu automatického zálohování a maximální počet uložených kopií.<br/><strong>Poznámka:</strong>před tím, než uložíte svojí zálohu na účet třetí strany (DropBox, Box.com, OneDrive nebo Google Drive), musíte připojit tento účet ke složce Běžné Dokumenty {{organizationName}}.",
"AutoSavePeriod": "Doba automatického ukládání",

View File

@ -2,5 +2,11 @@
"EmptyScreenDescription": "Bitte überprüfen Sie Ihre Internetverbindung und laden Sie die Seite neu oder versuchen Sie es später.",
"GalleryEmptyScreenDescription": "Wählen Sie eine beliebige Formularvorlage aus, um die Details zu sehen",
"GalleryEmptyScreenHeader": "Formularvorlagen konnten nicht geladen werden",
"TemplateInfo": "Informationen zur Vorlage"
"TemplateInfo": "Informationen zur Vorlage",
"Categories": "Kategorien",
"ViewAllTemplates": "Alle Vorlagen ansehen",
"EmptyFormGalleryScreenDescription": "Es konnten keine Ergebnisse gefunden werden, die Ihrer Anfrage entsprechen",
"Free": "Kostenlos",
"SuggestChanges": "Änderungen vorschlagen"
}

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Subsecțiunea vă permite să răsfoiți lista celor mai recente modificări (creare, modificare, ştergere etc.) efectuate de utilizatori asupra elementelor (săli, oportunităţi, fişiere etc.) din cadrul DocSpace.",
"AuditTrailNav": "Audit-Trail",
"AutoBackup": "Automatische Sicherung",
"AutoBackupDescription": "Nutzen Sie diese Option für die automatische Sicherung der DocSpace-Daten.",
"AutoBackupHelp": "Mit der Option <strong>Automatische Sicherung</strong> wird der Prozess der Datensicherung von DocSpace automatisiert, um sie später auf einem lokalen Server wiederherstellen zu können.",
"AutoBackupHelpNote": "Wählen Sie den Datenspeicher, den automatischen Sicherungszeitraum und die maximale Anzahl der gespeicherten Kopien aus. <br/><strong>Hinweis:</strong> Bevor Sie die Sicherungsdaten auf einem Drittanbieter-Konto (DropBox, Box.com, OneDrive oder Google Drive) speichern können, müssen Sie zuerst dieses Konto mit dem Ordner {{organizationName}} 'Gemeinsame Dokumente' verbinden.",
"AutoSavePeriod": "Zeitraum für automatische Speicherung",
@ -92,7 +91,6 @@
"DNSSettingsTooltipStandalone": "Markieren Sie das Kontrollkästchen 'Benutzerdefinierter Domain-Name' und geben Sie Ihren eigenen Domain-Namen für den ONLYOFFICE-Space im Feld unten ein. Um die Änderungen zu übernehmen, klicken Sie auf den Button 'Speichern' im unteren Bereich.",
"DocumentService": "Dokumentenservice",
"DocumentServiceLocationHeader": "Adressen des Dokumentendienstes",
"DocumentServiceLocationHeaderHelp": " Der Dokumentendienst ist der Serverdienst, der die Dokumentbearbeitung ermöglicht und die Konvertierung der Dokumentdatei in das entsprechende OfficeOpen XML-Format ermöglicht.",
"DocumentServiceLocationHeaderInfo": "Adressen des Dokumentendienstes gibt die Serveradresse mit den dazu installierten Dokumentendienstes. ",
"DocumentServiceLocationUrlApi": "die Adresse des Dokumentenbearbeitungsservices",
"DocumentServiceLocationUrlInternal": "Sie Adresse des Dokumenten Services für Anforderungen vom DocSpace",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Η υποενότητα σας επιτρέπει να περιηγηθείτε στη λίστα με τις τελευταίες αλλαγές (δημιουργία, τροποποίηση, διαγραφή κ.λπ.) που έχουν γίνει από χρήστες στις οντότητες (δωμάτια, ευκαιρίες, αρχεία κ.λπ.) εντός του DocSpace σας.",
"AuditTrailNav": "Παρακολούθηση ελέγχου",
"AutoBackup": "Αυτόματα αντίγραφα ασφαλείας",
"AutoBackupDescription": "Χρησιμοποιήστε αυτήν την επιλογή για να δημιουργήσετε αυτόματα αντίγραφα ασφαλείας των δεδομένων της DocSpace.",
"AutoBackupHelp": "Η επιλογή <strong>Αυτόματα αντίγραφα ασφαλείας</strong> χρησιμοποιείται για την αυτοματοποίηση της διαδικασίας δημιουργίας αντιγράφων ασφαλείας των δεδομένων της DocSpace, ώστε να είναι δυνατή η επαναφορά τους αργότερα σε έναν τοπικό διακομιστή.",
"AutoBackupHelpNote": "Επιλέξτε την αποθήκευση δεδομένων, την περίοδο αυτόματης δημιουργίας αντιγράφων ασφαλείας και τον μέγιστο αριθμό αποθηκευμένων αντιγράφων.<br/><strong>Σημείωση:</strong> Πριν μπορέσετε να αποθηκεύσετε τα δεδομένα αντιγράφων ασφαλείας σε λογαριασμό τρίτου μέρους (DropBox, Box.com, OneDrive ή Google Drive), θα πρέπει να συνδέσετε αυτόν τον λογαριασμό με τον Κοινό φάκελο του οργανισμού {{organizationName}}.",
"AutoSavePeriod": "Περίοδος αυτόματης αποθήκευσης",

View File

@ -1,11 +1,15 @@
{
"EmptyScreenDescription": "Please check your Internet connection and refresh the page or try later.",
"GalleryEmptyScreenDescription": "Select any form template to see the details",
"GalleryEmptyScreenHeader": "Failed to load form templates",
"TemplateInfo": "Template info",
"Categories": "Categories",
"ViewAllTemplates": "View all templates",
"EmptyFormGalleryScreenDescription": "No results matching your query could be found",
"Free": "Free",
"SuggestChanges": "Suggest changes",
"SelectForm": "Select Form",
"SubmitToGalleryBlockBody": "Submit your templates to share them with the ONLYOFFICE community.",
"SubmitToGalleryBlockHeader": "ONLYOFFICE Form Gallery",
"SubmitToGalleryDialogGuideInfo": "Learn how to create perfect forms and increase your chance to get approval in our <1>guide</1>.",
"SubmitToGalleryDialogMainInfo": "Submit your form to the public gallery to let others use it in their work. Once the form passes moderation, you will be notified and rewarded for your contribution.",
"TemplateInfo": "Template info"
"SubmitToGalleryDialogMainInfo": "Submit your form to the public gallery to let others use it in their work. Once the form passes moderation, you will be notified and rewarded for your contribution."
}

View File

@ -28,6 +28,7 @@
"FeedUpdateUser": "has been assigned role {{role}}",
"FileExtension": "File extension",
"FilesEmptyScreenText": "See file and folder details here",
"GalleryEmptyScreenText": "See form template details here",
"HistoryEmptyScreenText": "Activity history will be shown here",
"InfoBanner": "The list of invited users includes the owner and/or admins of this DocSpace with full access to all rooms. The owner and/or administrator cannot be assigned other access rights. Once added to the room, they will be notified of all changes.",
"ItemsSelected": "Items selected",

View File

@ -33,7 +33,7 @@
"AuditSubheader": "The subsection allows you to browse through the list of the latest changes (creation, modification, deletion etc.) made by users to the entities (rooms, opportunities, files etc.) within your DocSpace.",
"AuditTrailNav": "Audit Trail",
"AutoBackup": "Automatic backup",
"AutoBackupDescription": "Use this option for automatic backup of the DocSpace data.",
"AutoBackupDescription": "The Automatic backup option is used to automate the DocSpace data backup process to be able to restore it later to a local server.",
"AutoBackupHelp": "The <strong>Automatic backup</strong> option is used to automate the DocSpace data backup process to be able to restore it later to a local server.",
"AutoBackupHelpNote": "Choose the data storage, automatic backup period and maximal number of saved copies.<br/><strong>Note:</strong> before you can save the backup data to a third-party account (DropBox, Box.com, OneDrive or Google Drive), you will need to connect this account to {{organizationName}} Common folder.",
"AutoSavePeriod": "Autosave period",
@ -99,7 +99,7 @@
"DNSSettingsTooltipStandalone": "Check the 'Custom domain name box' and specify your own domain name for the ONLYOFFICE space in the field below. To make the parameters you set take effect click the 'Save button' at the bottom of the section.",
"DocumentService": "Document Service",
"DocumentServiceLocationHeader": "Document Service Location",
"DocumentServiceLocationHeaderHelp": "Document Service is the server service which allows to perform the document editing and allows to convert the document file into the appropriate OfficeOpen XML format.",
"DocumentServiceLocationHeaderHelp": "Document Service is the server service which allows to perform the document editing and allows to convert the document file into the appropriate OfficeOpen XML format. Document service location specifies the address of the server with the document services installed.",
"DocumentServiceLocationHeaderInfo": "Document service location specifies the address of the server with the document services installed. ",
"DocumentServiceLocationUrlApi": "Document Editing Service Address",
"DocumentServiceLocationUrlInternal": "Document Service address for requests from the DocSpace",

View File

@ -2,5 +2,11 @@
"EmptyScreenDescription": "Por favor, compruebe su conexión a Internet y actualice la página o inténtelo más tarde.",
"GalleryEmptyScreenDescription": "Seleccione cualquier plantilla de formulario para ver los detalles",
"GalleryEmptyScreenHeader": "Error al cargar las plantillas de formulario",
"TemplateInfo": "Información sobre la plantilla"
"TemplateInfo": "Información sobre la plantilla",
"Categories": "Categorías",
"ViewAllTemplates": "Ver todas las plantillas",
"EmptyFormGalleryScreenDescription": "No se han encontrado resultados que coincidan con la búsqueda",
"Free": "Gratis",
"SuggestChanges": "Sugerir cambios"
}

View File

@ -29,7 +29,6 @@
"AuditSubheader": "La subsección le permite navegar por la lista de los últimos cambios (creación, modificación, eliminación, etc.) realizados por los usuarios en las entidades (salas, oportunidades, archivos, etc.) dentro de su DocSpace.",
"AuditTrailNav": "Rastro de auditoría",
"AutoBackup": "Copia de seguridad automática",
"AutoBackupDescription": "Use esta opción para la copia de seguridad automática de los datos de DocSpace.",
"AutoBackupHelp": "La opción <strong>Copia de seguridad automática</strong> se usa para automatizar el proceso de creación de copia de seguridad de DocSpace para poder restaurarlo más tarde en un servidor local.",
"AutoBackupHelpNote": "Elija el almacenamiento para los datos, periodo de creación de copia y el número máximo de copias guardadas.<br/><strong>Nota:</strong> antes de guardar la copia de seguridad en una cuenta de terceros (Dropbox, Box.com, OneDrive o Google Drive), usted necesitará conectar esta cuenta a la carpeta Documentos Comunes de {{organizationName}}.",
"AutoSavePeriod": "Periodo de autoguardado",
@ -87,7 +86,6 @@
"DNSSettingsTooltipStandalone": "Marque la casilla 'Nombre de dominio personalizado' y especifique su propio nombre del espacio ONLYOFFICE en el campo debajo. Para aplicar los cambios hechos, use el botón 'Guardar' en la parte inferior de la sección.",
"DocumentService": "Servicio de documentos",
"DocumentServiceLocationHeader": "Ubicación de servicio de documentos",
"DocumentServiceLocationHeaderHelp": "Document Service es el servicio del servidor que permite realizar ediciones a los documentos y convertir los archivos de documentos a los formatos OfficeOpen XML apropiados.",
"DocumentServiceLocationHeaderInfo": "Ubicación de servicio de documentos especifica la dirección del servidor con servicios de documentos instalados. ",
"DocumentServiceLocationUrlApi": "Dirección de servicio de edición de documentos",
"DocumentServiceLocationUrlInternal": "Dirección del Servicio de Documentos para realizar peticiones desde el DocSpace",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Tässä aliosiossa voit selata luetteloa viimeisimmistä muutoksista (luonti, muokkaus, poisto jne.), joita käyttäjät ovat tehneet DocSpacen entiteetehin (huoneet, mahdollisuudet, tiedostot jne).",
"AuditTrailNav": "Tapahtumien seuranta",
"AutoBackup": "Automaattinen varmuuskopiointi",
"AutoBackupDescription": "Käytä tätä vaihtoehtoa DocSpacen tietojen automaattiseen varmuuskopiointiin.",
"AutoBackupHelp": "<strong>Automaattisen varmuuskopioinnin</strong> vaihtoehtoa käytetään tallentamaan DocSpace-tietoja automaattisesti, jotta ne voidaan palauttaa myöhemmin paikalliseen palvelimeen.",
"AutoBackupHelpNote": "Valitse talletustapa, automaattisen varmuuskopioinnin jakso ja maksimimäärä tallennettuja kopioita.<br/><strong>Huom:</strong> ennenkuin voit tallentaa varmuuskopion kolmannen osapuolen tilille (DropBox, Box.com, OneDrive tai Google Drive), tulee sinun yhdistää tämä tili {{organizationName}} Yleisten asiakirjojen kansioon.",
"AutoSavePeriod": "Automaattisen tallennuksen aika",

View File

@ -2,5 +2,11 @@
"EmptyScreenDescription": "Veuillez vérifier votre connexion Internet et actualisez la page ou réessayez ultérieurement.",
"GalleryEmptyScreenDescription": "Sélectionnez un modèle de formulaire pour voir les détails",
"GalleryEmptyScreenHeader": "Chargement des modèles de formulaire a échoué",
"TemplateInfo": "Informations sur le modèle"
"TemplateInfo": "Informations sur le modèle",
"Categories": "Catégories",
"ViewAllTemplates": "Afficher tous les formulaires",
"EmptyFormGalleryScreenDescription": "Aucun résultat correspondant à votre requête n'a pu être trouvé",
"Free": "Gratuit",
"SuggestChanges": "Proposer des modifications"
}

View File

@ -29,7 +29,6 @@
"AuditSubheader": "La sous-section vous permet de parcourir la liste des derniers changements (création, modification, suppression, etc.) apportés par les utilisateurs aux entités (salles, opportunités, fichiers, etc.) de votre DocSpace.",
"AuditTrailNav": "Piste d'audit ",
"AutoBackup": "Sauvegarde automatique",
"AutoBackupDescription": "Utilisez cette option pour la sauvegarde automatique des données de DocSpace.",
"AutoBackupHelp": "L'option <strong>Sauvegarde automatique</strong> est utilisée pour automatiser le processus de sauvegarde des données de DocSpace en vue de les restaurer plus tard sur un serveur local.",
"AutoBackupHelpNote": "Sélectionnez le stockage de données, période de sauvegarde automatique et nombre maximal de copies sauvegardées.<br/><strong>Remarque :</strong> avant de pouvoir enregistrer des données de sauvegarde sur un compte tiers (Dropbox, Box.com, OneDrive ou Google Drive), vous avez besoin de connecter ce compte au dossier Documents communs {{organizationName}}.",
"AutoSavePeriod": "Période de sauvegarde automatique",
@ -87,7 +86,6 @@
"DNSSettingsTooltipStandalone": "Cochez la case 'Nom de domaine personnalisé' et spécifiez votre propre nom de domaine pour l'espace ONLYOFFICE dans le champ ci-dessous. Pour appliquer les modifications effectuées utilisez le bouton 'Enregistrer' en bas de la section.",
"DocumentService": "Service de documents",
"DocumentServiceLocationHeader": "Emplacement du service de documents",
"DocumentServiceLocationHeaderHelp": "Document Service est le service serveur qui permet d'effectuer l'édition du document et permet de convertir le fichier document au format OfficeOpen XML approprié.",
"DocumentServiceLocationHeaderInfo": "L'emplacement du service de documents spécifie l'adresse du serveur avec les services de documents installés. ",
"DocumentServiceLocationUrlApi": "Adresse du service de modification de documents",
"DocumentServiceLocationUrlInternal": "Adresse du service de documents pour les demandes provenant du DocSpace",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Ենթաբաժինը թույլ է տալիս թերթել վերջին փոփոխությունների ցանկը (ստեղծում, փոփոխում, ջնջում և այլն), որոնք կատարվել են օգտատերերի կողմից ձեր DocSpace-ի սուբյեկտներում (սենյակներ, հնարավորություններ, ֆայլեր և այլն):",
"AuditTrailNav": "Ստուգման հետք",
"AutoBackup": "Ինքնաշխատ պահուստավորում",
"AutoBackupDescription": "Օգտագործեք այս ընտրանքը DocSpace-ի տվյալների ինքնաշխատ պահուստավորման համար:",
"AutoBackupHelp": "<strong>Ավտոմատ պահուստավորում</strong>ընտրանքն օգտագործվում է DocSpace-ի տվյալների կրկնօրինակման գործընթացը ավտոմատացնելու համար, որպեսզի այն հետագայում հնարավոր լինի վերականգնել տեղական սերվերում:",
"AutoBackupHelpNote": "Ընտրեք տվյալների պահեստը, ավտոմատ պահուստավորման ժամանակահատված և պահպանված պատճենների առավելագույն քանակը։<br/><ուժեղ>նշում:</ուժեղ> նախքան պահուստային տվյալները երրորդ կողմի հաշվում պահելը (DropBox, Box.com, OneDrive or Google Drive), Դուք պետք է միացնեք այս հաշիվը {{organizationName}} ընդհանուր պանակին:",
"AutoSavePeriod": "Ավտոմատ պահպանման ժամկետը",

View File

@ -2,5 +2,11 @@
"EmptyScreenDescription": "Ti preghiamo di controllare la tua connessione Internet e aggiorna la pagina o riprova più tardi.",
"GalleryEmptyScreenDescription": "Seleziona qualsiasi modello di modulo per vedere i dettagli",
"GalleryEmptyScreenHeader": "Impossibile caricare i modelli di modulo",
"TemplateInfo": "Informazioni sul modello"
"TemplateInfo": "Informazioni sul modello",
"Categories": "Categorie",
"ViewAllTemplates": "Visualizzare tutti i modelli",
"EmptyFormGalleryScreenDescription": "Non è stato possibile trovare alcun risultato corrispondente alla tua ricerca",
"Free": "Gratis",
"SuggestChanges": "Suggerire modifiche"
}

View File

@ -29,7 +29,6 @@
"AuditSubheader": "La sottosezione ti permette di navigare attraverso lelenco delle ultime modifiche (creazione, modifica, cancellazione ecc.) apportate dagli utenti alle entità (stanze, opportunità, file ecc.) allinterno del tuo DocSpace.",
"AuditTrailNav": "Registro di controllo",
"AutoBackup": "Backup automatico",
"AutoBackupDescription": "Usa questa opzione per un backup automatico dei dati di DocSpace.",
"AutoBackupHelp": "L'opzione <strong>Backup automatico</strong> viene utilizzata per automatizzare il processo di backup dei dati di DocSpace per poterlo ripristinare successivamente su un server locale.",
"AutoBackupHelpNote": "Scegli l'archivio dei dati, il periodo di backup automatico e il numero massimo di copie salvate.<br/><strong>Nota:</strong> prima di poter salvare i dati di backup su un account di terze parti (DropBox, Box.com, OneDrive o Google Drive), dovrai collegare questo account alla cartella Documenti comuni {{organizationName}}.",
"AutoSavePeriod": "Periodo di salvataggio automatico",
@ -92,7 +91,6 @@
"DNSSettingsTooltipStandalone": "Spunta la casella 'Nome dominio personalizzato' e specifica il proprio nome a dominio per il spazio ONLYOFFICE nel campo di sotto. Per applicare i parametri impostati clicca su 'Salva' nella parte inferiore della sezione",
"DocumentService": "Servizio documenti",
"DocumentServiceLocationHeader": "Locazione del servizio documenti",
"DocumentServiceLocationHeaderHelp": "Document Service è il servizio server che consente di eseguire l'editing dei documenti ed inoltre di convertire il file di documento nel formato XML appropriato di OfficeOpen.",
"DocumentServiceLocationHeaderInfo": "La locazione del servizio documenti determina l'indirizzo del server con i servizi documenti installati. ",
"DocumentServiceLocationUrlApi": "Indirizzo del servizio di modifica documenti",
"DocumentServiceLocationUrlInternal": "Document Service Address per le richieste dal DocSpace",

View File

@ -2,5 +2,11 @@
"EmptyScreenDescription": "インターネット接続をご確認のうえ、ページを更新するか、後でお試しください",
"GalleryEmptyScreenDescription": "フォームテンプレートを選択すると、詳細が表示されます",
"GalleryEmptyScreenHeader": "フォームテンプレートの読み込みに失敗しました",
"TemplateInfo": "テンプレート情報"
"TemplateInfo": "テンプレート情報",
"Categories": "カテゴリー",
"ViewAllTemplates": "テンプレート一覧を見る",
"EmptyFormGalleryScreenDescription": "検索条件に一致する結果は見つかりませんでした。",
"Free": "無料",
"SuggestChanges": "変更点のご提案"
}

View File

@ -29,7 +29,6 @@
"AuditSubheader": "サブセクションでは、DocSpace内のエンティティルーム、機会、ファイルなどに対してユーザーが行った最新の変更作成、変更、削除などのリストを閲覧することができます。",
"AuditTrailNav": "監査証跡",
"AutoBackup": "自動バックアップ",
"AutoBackupDescription": "DocSpaceデータを自動的にバックアップするためにこの操作を使用してください。",
"AutoBackupHelp": "<strong>自動バックアップ</strong>オプションは、DocSpaceデータのバックアッププロセスを自動化し、後でローカルサーバーに復元できるようにするために使用します。",
"AutoBackupHelpNote": "データのためのストレージ、自動バックアップ期間、保存されるコピーの最大数を選択してください。<br/> <strong>ご注意してください:</strong>バックアップを保存する前に、第三者サービスのアカウントDropBox、Box.com、OneDriveまたはGoogle Driveを{{organizationName}}「共通文書」フォルダーに接続する必要です。",
"AutoSavePeriod": "自動保存期間",
@ -86,7 +85,6 @@
"DNSSettingsTooltipStandalone": "'カスタムドメイン名'ボックスを確認して、下のフィールドにONLYOFFICEポータル用に独自ドメイン名を入力してください。 変更を適用するように、セクションの下部に'保存'ボタンを押してください。",
"DocumentService": "文書サービス",
"DocumentServiceLocationHeader": "文書サービス位置",
"DocumentServiceLocationHeaderHelp": "文書サービスは文書編集したり、適切なOfficeOpen XML形式に変換したりすることを可能にするサーバーサービスです。",
"DocumentServiceLocationHeaderInfo": "文書サービス位置は文書サービスがインストルしたサーバアドレスを指定しています。",
"DocumentServiceLocationUrlApi": "文書編集サービスアドレス",
"DocumentServiceLocationUrlInternal": "コミュニティ・サーバーからの要求に対するのための文書サービスアドレス",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "하위 섹션에서는 사용자가 DocSpace 내의 항목(방, 기회, 파일 등)에 대해 수행한 최신 변경 사항(생성, 수정, 삭제 등) 목록을 검색할 수 있습니다.",
"AuditTrailNav": "감사 추적",
"AutoBackup": "자동 백업",
"AutoBackupDescription": "DocSpace 데이터 자동 백업에 대해 이 옵션을 사용하세요.",
"AutoBackupHelp": "<strong>자동 백업</strong> 옵션은 DocSpace 데이터 백업 프로세스를 자동화하여 후에 로컬 서버로 복원할 수 있도록 하는 데 사용됩니다.",
"AutoBackupHelpNote": "데이터 저장소, 자동 백업 기간, 최대 저장 복사본 수를 선택하세요.<br/><strong>참고:</strong> 백업 데이터를 타사 계정(DropBox, Box.com, OneDrive, Google Drive)에 저장하기 전에 이 계정을 {{organizationName}} 공유 폴더에 연결해야 합니다.",
"AutoSavePeriod": "자동 저장 기간",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "ພາກສ່ວນຍ່ອຍອະນຸຍາດໃຫ້ທ່ານສາມາດທ່ອງໄປຫາບັນຊີລາຍຊື່ຂອງການປ່ຽນແປງຫຼ້າສຸດ (ການສ້າງ, ການແກ້ໄຂ, ການລຶບແລະອື່ນໆ) ທີ່ເຮັດໂດຍຜູ້ໃຊ້ກັບຫນ່ວຍງານ (ຫ້ອງ, ໂອກາດ, ໄຟລ໌ແລະອື່ນໆ) ພາຍໃນ DocSpace ຂອງ ທ່ານ.",
"AuditTrailNav": "ປະຫວັດກວດສອບ",
"AutoBackup": "ອັດຕະໂນມັດ ສໍາຮອງຂໍ້ມູນ",
"AutoBackupDescription": "ໃຊ້ຕົວເລືອກນີ້ສໍາລັບການສໍາຮອງຂໍ້ມູນອັດຕະໂນມັດຂອງຂໍ້ມູນປະຕູ.",
"AutoBackupHelp": "ຕົວເລືອກ <strong>ການສຳຮອງຂໍ້ມູນອັດຕະໂນມັດ</strong> ຖືກນໍາໃຊ້ເພື່ອອັດຕະໂນມັດຂະບວນການສໍາຮອງຂໍ້ມູນປະຕູເພື່ອໃຫ້ສາມາດກູ້ມັນຄືນມາໃນເຊີບເວີໃນເຄື່ອງໃນພາຍຫຼັງ.",
"AutoBackupHelpNote": "ເລືອກບ່ອນເກັບຂໍ້ມູນ, ໄລຍະເວລາສຳຮອງອັດຕະໂນມັດ ແລະຈຳນວນສູງສຸດຂອງສຳເນົາທີ່ບັນທຶກໄວ້ .<br/> <strong>ໝາຍເຫດ:</strong> ກ່ອນທີ່ທ່ານຈະສາມາດບັນທຶກຂໍ້ມູນສຳຮອງໄວ້ໃນບັນຊີພາກສ່ວນທີສາມໄດ້ ( DropBox , Box.com , OneDrive ຫຼື Google Drive), ທ່ານຈະຕ້ອງເຊື່ອມຕໍ່ບັນຊີນີ້ກັບ {{ organizationName }} ໂຟນເດີທົ່ວໄປ.",
"AutoSavePeriod": "ບັນທຶກອັດຕະໂນມັດ ໄລ​ຍະ​ເວ​ລາ",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Apakšsadaļa ļauj pārlūkot jaunāko izmaiņu sarakstu (izveide, modificēšana, dzēšana utt.), ko lietotāji veikuši jūsu DocSpace esošajās entītijās (telpas, iespējas, faili utt.).",
"AuditTrailNav": "Audita žurnāls",
"AutoBackup": "Automātiskā rezerves kopēšana",
"AutoBackupDescription": "Izmantojiet šo iespēju automātiskai DocSpace datu rezerves kopēšanai.",
"AutoBackupHelp": "<strong>Automātiskās datu rezerves kopēšanas</strong> iespēja tiek izmantota, lai automatizētu DocSpace datu rezerves kopēšanas procesu un varētu tos vēlāk noglabāt lokālajā serverī.",
"AutoBackupHelpNote": "Izvēlieties datu uzglabāšanu, automātiskās dublēšanas periodu un maksimālo saglabāto kopiju skaitu.<br/><strong>Piezīme:</strong> Pirms jūs varēsit noglabāt rezerves datus trešās puses kontā (Dropbox, Box.com, OneDrive vai Google Drive), jums būs jāpieslēdzas šim kontam {{organizationName}} Kopīgie dokumenti mapē.",
"AutoSavePeriod": "Automātiskās saglabāšanas periods",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Met deze subsectie kunt u bladeren door de lijst met de laatste wijzigingen (aanmaak, wijziging, verwijdering enz.) die gebruikers hebben aangebracht in de entiteiten (ruimtes, kansen, bestanden enz.) binnen uw DocSpace.",
"AuditTrailNav": "Audit Trail",
"AutoBackup": "Automatische back-up",
"AutoBackupDescription": "Gebruik deze optie voor automatische back-up van de ruimtegegevens.",
"AutoBackupHelp": "De optie <strong>Automatische back-up</strong> wordt gebruikt om het back-upproces van DocSpace-gegevens te automatiseren om deze later op een lokale server te kunnen herstellen.",
"AutoBackupHelpNote": "Kies de dataopslag, automatische back-upperiode en het maximale aantal opgeslagen kopieën.<br/><strong>Opmerking:</strong> voordat u de back-upgegevens kunt opslaan op een account van een derde partij (DropBox, Box.com, OneDrive of Google Drive), moet u dit account verbinden met de map {{organizationName}} Gemeenschappelijke documenten.",
"AutoSavePeriod": "Autosave periode",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Podsekcja umożliwia przeglądanie listy najnowszych zmian (utworzeń, modyfikacji, usunięć itp.) wprowadzonych przez użytkowników w poszczególnych elementach (pokojach, możliwościach, plikach itp.) w DocSpace.",
"AuditTrailNav": "Szlak audytu",
"AutoBackup": "Automatyczna kopia zapasowa",
"AutoBackupDescription": "Użyj tej opcji do automatycznego tworzenia kopii zapasowych danych portalu.",
"AutoBackupHelp": "Opcja <strong>Automatyczna kopia zapasowa</strong> służy do automatyzacji procesu tworzenia kopii zapasowych danych DocSpace, aby móc ją później przywrócić na lokalnym serwerze.",
"AutoBackupHelpNote": "Wybierz pamięć danych, okres automatycznego tworzenia kopii zapasowych i maksymalną liczbę zapisanych kopii.<br/><strong>Uwaga:</strong> przed zapisaniem danych kopii zapasowej na konto osób trzecich (DropBox, Box.com, OneDrive lub Dysk Google) , musisz połączyć to konto z folderem Wspólne dokumenty {{organizationName}}.",
"AutoSavePeriod": "Częstotliwość autozapisywania",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "A subseção permite navegar pela lista das últimas alterações (criação, modificação, exclusão etc.) feitas pelos usuários nas entidades (salas, oportunidades, arquivos etc.) em seu DocSpace.",
"AuditTrailNav": "Trilha de auditoria",
"AutoBackup": "Backup automático",
"AutoBackupDescription": "Use esta opção para backup automático dos dados do DocSpace.",
"AutoBackupHelp": "A opção <strong>Backup automático</strong> é usada para automatizar o processo de backup de dados DocSpace para ser possível restaurá-lo mais tarde em um servidor local.",
"AutoBackupHelpNote": "Escolha o armazenamento de dados, período de backup automático e número máximo de cópias salvas.<br/><strong>Nota:</strong> antes que você possa salvar os dados de backup para uma conta de terceiros (Dropbox, Box.com, OneDrive ou Google Drive), você precisará conectar essa conta para o módulo de Comum {{organizationName}}.",
"AutoSavePeriod": "Período de salvamento automático",
@ -87,7 +86,6 @@
"DNSSettingsTooltipStandalone": "Marque a caixa 'Personalizar nome do domínio' e especifique seu próprio nome do domínio para o espaço ONLYOFFICE no campo abaixo. Para que os parâmetros que você definiu tenham efeito, clique no botão 'Salvar' na parte inferior da seção.",
"DocumentService": "Serviço de documento",
"DocumentServiceLocationHeader": "Localização de serviço de documento",
"DocumentServiceLocationHeaderHelp": "Document Service é o serviço servidor que permite realizar a edição de documentos e permite converter o arquivo de documentos para o formato OfficeOpen XML apropriado.",
"DocumentServiceLocationHeaderInfo": "Localização de serviço de documento especifica o endereço do servidor com os serviços de documentos instalados. ",
"DocumentServiceLocationUrlApi": "Endereço de serviço de edição de documento",
"DocumentServiceLocationUrlInternal": "Endereço de serviço de documentos para solicitações do DocSpace",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "A subsecção permite-lhe navegar através da lista das últimas alterações (criação, modificação, eliminação, etc.) feitas pelos utilizadores às entidades (salas, oportunidades, ficheiros, etc.) dentro do seu DocSpace.",
"AuditTrailNav": "Auditar o Rastro",
"AutoBackup": "Cópia de segurança automática",
"AutoBackupDescription": "Utilize esta opção para fazer uma cópia de segurança automática dos dados do DocSpace.",
"AutoBackupHelp": "A opção de <strong>Cópia de Segurança Automática</strong> é utilizada para automatizar a criação da cópia de segurança para que possa posteriormente restaurá-la num servidor local.",
"AutoBackupHelpNote": "Escolha o armazenamento de dados, o período de criação de cópias de segurança e o número máximo de cópias guardadas.<br/><strong>Nota:</strong> antes de poder guardar para uma conta de terceiros (DropBox, Box.com, OneDrive ou Google Drive), irá precisar de ligar a conta à Pasta Comum da {{organizationName}}.",
"AutoSavePeriod": "Período para Guardar Automaticamente",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Subsecțiunea vă permite să răsfoiți lista celor mai recente modificări (creare, modificare, ştergere etc.) efectuate de utilizatori asupra elementelor (săli, oportunităţi, fişiere etc.) din cadrul DocSpace.",
"AuditTrailNav": "Lanţ de audit",
"AutoBackup": "Copiere de rezervă automată",
"AutoBackupDescription": "Utilizați această opțiune pentru a crea o copie de rezervă a datelor din spațiu DocSpacei.",
"AutoBackupHelp": "Opțiunea <strong>Copiere de rezervă automată</strong> permite automatizarea procesului de copiere a datelor din spațiu DocSpace pentru a fi restaurate ulterior pe un server local.",
"AutoBackupHelpNote": "Alegeți varianta de stocare a datelor, intervalul de timp pentru executarea copiei de rezervă și numărul maxim de copii.<br/><strong>Mențiune:</strong> înainte de a crea o copie de rezervă a datelor cu un cont de stocare de la terți (DropBox, Box.com, OneDrive sau Google Drive), trebuie să vă conectați cu acest cont la folderul Comun al {{organizationName}}.",
"AutoSavePeriod": "Interval de timp pentru salvarea automată",

View File

@ -1,11 +1,14 @@
{
"EmptyScreenDescription": "Проверьте подключение к Интернету и обновите страницу или повторите попытку позже.",
"GalleryEmptyScreenDescription": "Выберите шаблон формы, чтобы просмотреть подробности",
"GalleryEmptyScreenHeader": "Не удалось загрузить шаблоны форм",
"TemplateInfo": "Информация шаблона",
"Categories": "Категории",
"ViewAllTemplates": "Все шаблоны",
"Free": "Бесплатно",
"SuggestChanges": "Предложить изменения",
"SelectForm": "Выбрать форму",
"SubmitToGalleryBlockBody": "Присылайте свои шаблоны, чтобы поделиться ими с сообществом ONLYOFFICE.",
"SubmitToGalleryBlockHeader": "Галерея Форм ONLYOFFICE",
"SubmitToGalleryDialogGuideInfo": "Узнайте как создавать идеальные формы и увеличить свои шансы получить одобрение из нашего <1>руководства</1>.",
"SubmitToGalleryDialogMainInfo": "Отправьте свою форму в общедоступную галерею, чтобы другие могли использовать ее в своей работе. Как только форма пройдет модерацию, вы будете уведомлены и вознаграждены за свой вклад.",
"TemplateInfo": "Информация шаблона"
"SubmitToGalleryDialogMainInfo": "Отправьте свою форму в общедоступную галерею, чтобы другие могли использовать ее в своей работе. Как только форма пройдет модерацию, вы будете уведомлены и вознаграждены за свой вклад."
}

View File

@ -26,6 +26,7 @@
"FeedUpdateUser": "назначена роль {{role}}",
"FileExtension": "Расширение файла",
"FilesEmptyScreenText": "Здесь будут представлены свойства файлов и папок",
"GalleryEmptyScreenText": "Здесь будут представлены подробности о шаблонах форм",
"HistoryEmptyScreenText": "Здесь будет отображаться история активности",
"ItemsSelected": "Выбрано элементов",
"LastModifiedBy": "Автор последнего корректива",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Раздел позволяет просматривать список последних изменений (создание, модификация, удаление и т.д.), внесенных пользователями в сущности (возможности, файлы и т.д.) на вашем портале.",
"AuditTrailNav": "Журнал аудита",
"AutoBackup": "Автоматическое резервное копирование",
"AutoBackupDescription": "Используйте эту опцию для автоматического выполнения резервного копирования данных портала.",
"AutoBackupHelp": "Опция <strong>Автоматическое резервное копирование</strong> данных используется для автоматизации процесса создания резервных копий данных для последующего их восстановления на локальном сервере.",
"AutoBackupHelpNote": "Выберите хранилище для данных, период автоматического сохранения и максимальное число хранимых копий.<br/><strong>Обратите внимание:</strong> прежде чем Вы сможете сохранять резервные копии в стороннем аккаунте (Dropbox, Box.com, OneDrive или Google Drive), потребуется подключить его к папке 'Общие' {{organizationName}}.",
"AutoSavePeriod": "Период автоматического сохранения",
@ -92,7 +91,6 @@
"DNSSettingsTooltipStandalone": "Установите флажок 'Пользовательское доменное имя' и укажите собственное доменное имя для пространства ONLYOFFICE в поле ниже. Чтобы применить внесенные изменения, используйте кнопку 'Сохранить' внизу раздела.",
"DocumentService": "Служба документов",
"DocumentServiceLocationHeader": "Расположение службы документов",
"DocumentServiceLocationHeaderHelp": "Служба документов - это серверная служба, которая позволяет осуществлять редактирование документов и позволяет конвертировать файл документа в соответствующий формат OfficeOpen XML.",
"DocumentServiceLocationHeaderInfo": "Расположение службы документов определяет адрес сервера с установленными службами документов. ",
"DocumentServiceLocationUrlApi": "Адрес службы редактирования документов",
"DocumentServiceLocationUrlInternal": "Адрес Службы документов для запросов от DocSpace",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Podsekcia vám umožňuje prezerať zoznam posledných zmien (vytvorenie, úprava, odstránenie atď.), ktoré vykonali používatelia v objektoch (miestnosti, príležitosti, súbory atď.) vo vašom DocSpace.",
"AuditTrailNav": "Auditné záznamy",
"AutoBackup": "Automatická záloha",
"AutoBackupDescription": "Túto možnosť použite na automatické zálohovanie údajov DocSpace.",
"AutoBackupHelp": "Možnosť <strong>Automatické zálohovanie</strong> sa používa na automatizáciu procesu zálohovania dát v DocSpace, aby bolo možné ho neskôr obnoviť na lokálnom serveri.",
"AutoBackupHelpNote": "Vyberte si miesto uloženia dát, časový interval zálohovania a maximálny počet uložených kópií.<br/><strong>Upozornenie:</strong> skôr ako môžete uložiť zálohované údaje na účet tretích strán (DropBox, Box.com, OneDrive alebo Google Drive), budete musieť tento účet pripojiť k priečinku Všeobecné dokumenty {{organizationName}}.",
"AutoSavePeriod": "Časový interval automatického ukladania",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Podrazdelek vam omogoča brskanje po seznamu zadnjih sprememb (ustvarjanje, spreminjanje, brisanje itd.), ki so jih uporabniki naredili za entitete (sobe, priložnosti, datoteke itd.) v vašem prostoru DocSpace.",
"AuditTrailNav": "Revizijska pot",
"AutoBackup": "Samodejno varnostno kopiranje",
"AutoBackupDescription": "To možnost uporabite za samodejno varnostno kopiranje podatkov prostora.",
"AutoBackupHelp": "Možnost <strong>Samodejno varnostno kopiranje</strong> se uporablja za avtomatizacijo varnostnega kopiranja podatkov DocSpace, da bi jih lahko pozneje obnovili na lokalnem strežniku.",
"AutoBackupHelpNote": "Izberite shranjevanje podatkov, obdobje samodejnega varnostnega kopiranja in največje število shranjenih kopij.<br/><strong>Opomba:</strong> preden lahko shranite varnostno kopijo podatkov na računu drugih ponudnikov (DropBox, Box.com, OneDrive ali Google Drive), boste morali ta račun povezati s {{organizationName}} Skupno mapo.",
"AutoSavePeriod": "Obdobje samodejnega shranjevanja",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Alt bölüm, kullanıcılar tarafından DocSpace'inizdeki varlıklarda (odalar, fırsatlar, dosyalar vb.) yapılan en son değişikliklerin (oluşturma, değiştirme, silme vb.) listesine göz atmanıza olanak tanır.",
"AuditTrailNav": "Denetim İzleme",
"AutoBackup": "Otomatik yedekleme",
"AutoBackupDescription": "DocSpace verilerinin otomatik olarak yedeklenmesi için bu seçeneği kullanın.",
"AutoBackupHelp": "<strong>Otomatik yedekleme</strong> seçeneği yerel bir sunucudan geri yükleme yapılmak istediğinde kullanılabilmesi için DocSpace veri yedekleme sürecinin otomasyonu için kullanılır.",
"AutoBackupHelpNote": "Veri depolama, otomatik yedekleme periyodu ve en yüksek kaydedilen kopya sayısını seçebilirsiniz.<br/><strong>Not:</strong> yedekleme verilerini üçüncü bir hesaba (DropBox, Box.com, OneDrive veya Google Drive) kaydetmeden önce bu hesabı {{organizationName}} Ortak Belgeler klasörüne bağlamanız gerekir.",
"AutoSavePeriod": "Otomatik kaydetme süresi",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "У цьому підрозділі можна переглядати список останніх змін (створення, зміна, видалення тощо), внесених користувачами до об’єктів (кімнат, можливостей, файлів тощо) у вашому DocSpace.",
"AuditTrailNav": "Перевірка аудиту",
"AutoBackup": "Автоматичне резервне копіювання",
"AutoBackupDescription": "Використовуйте цю опцію для автоматичного резервного копіювання даних порталу.",
"AutoBackupHelp": "Параметр <strong> Автоматичне резервне копіювання </strong> використовується для автоматизації процесу резервного копіювання даних DocSpace, щоб пізніше відновити його на локальному сервері.",
"AutoBackupHelpNote": "Виберіть збереження даних, період автоматичного резервного копіювання та максимальну кількість збережених копій.<br/><strong>Примітка:</strong>, перш ніж ви зможете зберігати дані резервної копії на сторонніх облікових записів (DropBox, Box.com, OneDrive або Google Drive), Вам потрібно буде підключити цей обліковий запис до папки Загальні документи {{organizationName}}.",
"AutoSavePeriod": "Період автозбереження",

View File

@ -29,7 +29,6 @@
"AuditSubheader": "Phần phụ cho phép bạn duyệt qua danh sách các thay đổi mới nhất (tạo, sửa đổi, xóa, v.v.) do người dùng thực hiện đối với các thực thể (phòng, cơ hội, tập tin, v.v.) trong DocSpace của bạn.",
"AuditTrailNav": "Kiểm tra Truy nguyên",
"AutoBackup": "Tự động sao lưu",
"AutoBackupDescription": "Sử dụng tùy chọn này để tự động sao lưu dữ liệu DocSpace.",
"AutoBackupHelp": "Tùy chọn <strong>Tự động sao lưu</strong> được sử dụng để tự động hóa quá trình sao lưu dữ liệu cổng thông tin và cho phép khôi phục lại sau trên một server.",
"AutoBackupHelpNote": "Chọn dung lượng dữ liệu, thời gian sao lưu tự động và số lượng tối đa bản sao sẽ được lưu.<br/><strong>Lưu ý:</strong> trước khi bạn có thể lưu dữ liệu sao lưu vào tài khoản bên thứ ba (DropBox, Box.com, OneDrive hoặc Google Drive), bạn sẽ phải kết nối tài khoản này với thư mục Tài liệu Cộng tác của {{organizationName}}.",
"AutoSavePeriod": "Thời gian lưu tự động",

View File

@ -2,5 +2,11 @@
"EmptyScreenDescription": "请检查您的互联网连接,刷新页面或稍后再试",
"GalleryEmptyScreenDescription": "选择任何表单模板来查看细节、",
"GalleryEmptyScreenHeader": "表单模板加载失败、",
"TemplateInfo": "模板信息"
"TemplateInfo": "模板信息",
"Categories": "类别",
"ViewAllTemplates": "查看模板大全",
"EmptyFormGalleryScreenDescription": "没有找到匹配查询的结果",
"Free": "免费",
"SuggestChanges": "提出修改建议"
}

View File

@ -29,7 +29,6 @@
"AuditSubheader": "浏览用户在协作空间中对实体类(房间、机会、文件等)进行的最新操作(创建、修改、删除等)",
"AuditTrailNav": "审计追踪",
"AutoBackup": "自动备份",
"AutoBackupDescription": "使用此选项,自动备份协作空间数据。",
"AutoBackupHelp": "<strong>自动备份</strong>选项用于自动进行协作空间数据备份过程,以便以后能够将其恢复到本地服务器",
"AutoBackupHelpNote": "选择数据存储、自动备份期间和最大的保存副本数量。 <br/><strong>请注意:</strong> 在您将备份数据保存到第三方账户DropBox、Box.com、OneDrive或Google Drive之前您需要将该账户连接到{{organizationName}} 的共同文件夹。",
"AutoSavePeriod": "自动保存期间",

View File

@ -71,6 +71,19 @@ export default function withBadges(WrappedComponent) {
});
};
onUnmuteClick = (e) => {
const { t, setMuteAction } = this.props;
const elem = e.target.closest(".is-mute");
const data = elem.dataset;
const { id, rootfolderid } = data;
setMuteAction(
"unmute",
{ id, rootFolderId: rootfolderid, new: data.new },
t
);
};
setConvertDialogVisible = () => {
this.props.setConvertItem(this.props.item);
this.props.setConvertDialogVisible(true);
@ -133,6 +146,7 @@ export default function withBadges(WrappedComponent) {
onShowVersionHistory={this.onShowVersionHistory}
onBadgeClick={this.onBadgeClick}
onUnpinClick={this.onUnpinClick}
onUnmuteClick={this.onUnmuteClick}
setConvertDialogVisible={this.setConvertDialogVisible}
onFilesClick={onFilesClick}
viewAs={viewAs}
@ -167,7 +181,7 @@ export default function withBadges(WrappedComponent) {
isArchiveFolderRoot,
isArchiveFolder,
} = treeFoldersStore;
const { markAsRead, setPinAction } = filesActionsStore;
const { markAsRead, setPinAction, setMuteAction } = filesActionsStore;
const { isTabletView, isDesktopClient, theme } = auth.settingsStore;
const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
const {
@ -200,6 +214,7 @@ export default function withBadges(WrappedComponent) {
setConvertItem,
isDesktopClient,
setPinAction,
setMuteAction,
isMutedBadge,
getPrimaryLink,
isArchiveFolder,

View File

@ -143,9 +143,9 @@ export default function withFileActions(WrappedFileItem) {
}
if (
e.target.tagName === "INPUT" ||
e.target.tagName === "SPAN" ||
e.target.tagName === "A" ||
e.target?.tagName === "INPUT" ||
e.target?.tagName === "SPAN" ||
e.target?.tagName === "A" ||
e.target.closest(".checkbox") ||
e.target.closest(".table-container_row-checkbox") ||
e.button !== 0 ||
@ -158,7 +158,7 @@ export default function withFileActions(WrappedFileItem) {
return;
if (viewAs === "tile") {
if (e.target.closest(".edit-button") || e.target.tagName === "IMG")
if (e.target.closest(".edit-button") || e.target?.tagName === "IMG")
return;
if (e.detail === 1) this.fileContextClick();
} else this.fileContextClick();
@ -183,7 +183,7 @@ export default function withFileActions(WrappedFileItem) {
} = this.props;
if (
(e && e.target.tagName === "INPUT") ||
(e && e.target?.tagName === "INPUT") ||
!!e.target.closest(".lock-file") ||
// !!e.target.closest(".additional-badges") ||
e.target.closest(".tag") ||

View File

@ -115,7 +115,6 @@ const withHotkeys = (Component) => {
};
const onPaste = async (e) => {
e.preventDefault();
uploadClipboardFiles(t, e);
};

View File

@ -28,7 +28,7 @@ import MainBar from "./components/MainBar";
import { Portal } from "@docspace/components";
import indexedDbHelper from "@docspace/common/utils/indexedDBHelper";
import { DeviceType, IndexedDBStores } from "@docspace/common/constants";
import AppLoader from "@docspace/common/components/AppLoader";
import { getRestoreProgress } from "@docspace/common/api/portal";
const Shell = ({ items = [], page = "home", ...rest }) => {
const {
@ -112,7 +112,19 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
});
socketHelper.on("restore-backup", () => {
setPreparationPortalDialogVisible(true);
getRestoreProgress()
.then((response) => {
if (!response) {
console.log(
"Skip show <PreparationPortalDialog /> - empty progress response"
);
return;
}
setPreparationPortalDialogVisible(true);
})
.catch((e) => {
console.error("getRestoreProgress", e);
});
});
}, [socketHelper]);

View File

@ -2,20 +2,23 @@
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import CatalogItem from "@docspace/components/catalog-item";
import { PageType } from "@docspace/common/constants";
import { getCatalogIconUrlByType } from "@docspace/common/utils/catalogIcon.helper";
import CatalogAccountsReactSvgUrl from "PUBLIC_DIR/images/catalog.accounts.react.svg?url";
import CatalogItem from "@docspace/components/catalog-item";
const PureAccountsItem = ({ showText, isActive, onClick, t }) => {
const onClickAction = React.useCallback(() => {
onClick && onClick("accounts");
}, [onClick]);
const icon = getCatalogIconUrlByType(PageType.account);
return (
<CatalogItem
key="accounts"
text={t("Accounts")}
icon={CatalogAccountsReactSvgUrl}
icon={icon}
showText={showText}
onClick={onClickAction}
isActive={isActive}

View File

@ -1,31 +1,24 @@
import CatalogFolderReactSvgUrl from "PUBLIC_DIR/images/catalog.folder.react.svg?url";
import ClearTrashReactSvgUrl from "PUBLIC_DIR/images/clear.trash.react.svg?url";
import CatalogUserReactSvgUrl from "PUBLIC_DIR/images/catalog.user.react.svg?url";
import CatalogRoomsReactSvgUrl from "PUBLIC_DIR/images/catalog.rooms.react.svg?url";
import CatalogArchiveReactSvgUrl from "PUBLIC_DIR/images/catalog.archive.react.svg?url";
import CatalogSharedReactSvgUrl from "PUBLIC_DIR/images/catalog.shared.react.svg?url";
import CatalogPortfolioReactSvgUrl from "PUBLIC_DIR/images/catalog.portfolio.react.svg?url";
import CatalogFavoritesReactSvgUrl from "PUBLIC_DIR/images/catalog.favorites.react.svg?url";
import CatalogRecentReactSvgUrl from "PUBLIC_DIR/images/catalog.recent.react.svg?url";
import CatalogPrivateReactSvgUrl from "PUBLIC_DIR/images/catalog.private.react.svg?url";
import CatalogTrashReactSvgUrl from "PUBLIC_DIR/images/catalog.trash.react.svg?url";
import React, { useState } from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import PropTypes from "prop-types";
import React, { useState } from "react";
import { inject, observer } from "mobx-react";
import CatalogItem from "@docspace/components/catalog-item";
import { withTranslation } from "react-i18next";
import {
FolderType,
ShareAccessRights,
FolderNames,
DeviceType,
} from "@docspace/common/constants";
import { withTranslation } from "react-i18next";
import { getCatalogIconUrlByType } from "@docspace/common/utils/catalogIcon.helper";
import CatalogItem from "@docspace/components/catalog-item";
import DragAndDrop from "@docspace/components/drag-and-drop";
import SettingsItem from "./SettingsItem";
import AccountsItem from "./AccountsItem";
import BonusItem from "./BonusItem";
import AccountsItem from "./AccountsItem";
import ClearTrashReactSvgUrl from "PUBLIC_DIR/images/clear.trash.react.svg?url";
const StyledDragAndDrop = styled(DragAndDrop)`
display: contents;
@ -40,6 +33,7 @@ const Item = ({
item,
dragging,
getFolderIcon,
setBufferSelection,
isActive,
getEndOfBlock,
showText,
@ -109,6 +103,7 @@ const Item = ({
const onClickAction = React.useCallback(
(folderId) => {
setBufferSelection(null);
onClick && onClick(folderId, item.title, item.rootFolderType);
},
[onClick, item.title, item.rootFolderType]
@ -169,6 +164,7 @@ const Items = ({
commonId,
currentId,
draggableItems,
setBufferSelection,
moveDragItems,
@ -204,41 +200,7 @@ const Items = ({
);
const getFolderIcon = React.useCallback((item) => {
let iconUrl = CatalogFolderReactSvgUrl;
switch (item.rootFolderType) {
case FolderType.USER:
iconUrl = CatalogUserReactSvgUrl;
break;
case FolderType.Rooms:
iconUrl = CatalogRoomsReactSvgUrl;
break;
case FolderType.Archive:
iconUrl = CatalogArchiveReactSvgUrl;
break;
case FolderType.SHARE:
iconUrl = CatalogSharedReactSvgUrl;
break;
case FolderType.COMMON:
iconUrl = CatalogPortfolioReactSvgUrl;
break;
case FolderType.Favorites:
iconUrl = CatalogFavoritesReactSvgUrl;
break;
case FolderType.Recent:
iconUrl = CatalogRecentReactSvgUrl;
break;
case FolderType.Privacy:
iconUrl = CatalogPrivateReactSvgUrl;
break;
case FolderType.TRASH:
iconUrl = CatalogTrashReactSvgUrl;
break;
default:
break;
}
return iconUrl;
return getCatalogIconUrlByType(item.rootFolderType);
}, []);
const showDragItems = React.useCallback(
@ -338,6 +300,7 @@ const Items = ({
startUpload={startUpload}
uploadEmptyFolders={uploadEmptyFolders}
item={item}
setBufferSelection={setBufferSelection}
dragging={dragging}
getFolderIcon={getFolderIcon}
isActive={item.id === activeItemId}
@ -439,6 +402,7 @@ export default inject(
const {
selection,
bufferSelection,
setBufferSelection,
dragging,
setDragging,
trashIsEmpty,
@ -483,6 +447,7 @@ export default inject(
dragging,
setDragging,
moveDragItems,
setBufferSelection,
deleteAction,
startUpload,
uploadEmptyFolders,

View File

@ -107,6 +107,9 @@ const ArticleMainButtonContent = (props) => {
isRoomsFolder,
isArchiveFolder,
setOformFromFolderId,
oformsFilter,
enablePlugins,
mainButtonItemsList,
@ -120,6 +123,7 @@ const ArticleMainButtonContent = (props) => {
mainButtonMobileVisible,
versionHistoryPanelVisible,
moveToPanelVisible,
restorePanelVisible,
copyPanelVisible,
security,
@ -204,7 +208,11 @@ const ArticleMainButtonContent = (props) => {
const onInputClick = React.useCallback((e) => (e.target.value = null), []);
const onShowGallery = () => {
navigate(`/form-gallery/${currentFolderId}/`);
const initOformFilter = (
oformsFilter || oformsFilter.getDefault()
).toUrlParams();
setOformFromFolderId(currentFolderId);
navigate(`/form-gallery/${currentFolderId}/filter?${initOformFilter}`);
};
const onInvite = React.useCallback((e) => {
@ -493,6 +501,7 @@ const ArticleMainButtonContent = (props) => {
if (currentDeviceType === DeviceType.mobile) {
mainButtonVisible =
moveToPanelVisible ||
restorePanelVisible ||
copyPanelVisible ||
selectFileDialogVisible ||
versionHistoryPanelVisible
@ -583,6 +592,7 @@ export default inject(
treeFoldersStore,
selectedFolderStore,
clientLoadingStore,
oformsStore,
pluginStore,
versionHistoryStore,
}) => {
@ -604,6 +614,7 @@ export default inject(
setInviteUsersWarningDialogVisible,
copyPanelVisible,
moveToPanelVisible,
restorePanelVisible,
selectFileDialogVisible,
} = dialogsStore;
@ -618,6 +629,7 @@ export default inject(
const { isAdmin, isOwner } = auth.userStore.user;
const { isGracePeriod } = auth.currentTariffStatusStore;
const { setOformFromFolderId, oformsFilter } = oformsStore;
const { mainButtonItemsList } = pluginStore;
return {
@ -644,6 +656,9 @@ export default inject(
currentFolderId,
setOformFromFolderId,
oformsFilter,
enablePlugins,
mainButtonItemsList,
@ -654,6 +669,7 @@ export default inject(
mainButtonMobileVisible,
moveToPanelVisible,
restorePanelVisible,
copyPanelVisible,
versionHistoryPanelVisible,
security,

View File

@ -5,6 +5,10 @@ import FileActionsConvertEditDocReactSvgUrl from "PUBLIC_DIR/images/file.actions
import LinkReactSvgUrl from "PUBLIC_DIR/images/link.react.svg?url";
import TabletLinkReactSvgUrl from "PUBLIC_DIR/images/tablet-link.reat.svg?url";
import RefreshReactSvgUrl from "PUBLIC_DIR/images/refresh.react.svg?url";
import Refresh12ReactSvgUrl from "PUBLIC_DIR/images/icons/12/refresh.react.svg?url";
import Mute12ReactSvgUrl from "PUBLIC_DIR/images/icons/12/mute.react.svg?url";
import Mute16ReactSvgUrl from "PUBLIC_DIR/images/icons/16/mute.react.svg?url";
import React, { useState } from "react";
import styled from "styled-components";
import Badge from "@docspace/components/badge";
@ -15,7 +19,7 @@ import { FileStatus, RoomsType } from "@docspace/common/constants";
import { Base } from "@docspace/components/themes";
import { ColorTheme, ThemeType } from "@docspace/components/ColorTheme";
import { isTablet } from "@docspace/components/utils/device";
import { isTablet, isDesktop } from "@docspace/components/utils/device";
import { classNames } from "@docspace/components/utils/classNames";
const StyledWrapper = styled.div`
@ -74,6 +78,7 @@ const Badges = ({
setConvertDialogVisible,
viewAs,
onUnpinClick,
onUnmuteClick,
isMutedBadge,
isArchiveFolderRoot,
isVisitor,
@ -90,11 +95,15 @@ const Badges = ({
isRoom,
pinned,
isFolder,
mute,
rootFolderId,
new: newCount,
} = item;
const showEditBadge = !locked || item.access === 0;
const isPrivacy = isPrivacyFolder && isDesktopClient;
const isForm = fileExst === ".oform";
const isPdf = fileExst === ".pdf";
const isTile = viewAs === "tile";
const countVersions = versionGroup > 999 ? "999+" : versionGroup;
@ -102,6 +111,7 @@ const Badges = ({
const contentNewItems = newItems > 999 ? "999+" : newItems;
const tabletViewBadge = !isTile && isTablet();
const desktopView = !isTile && isDesktop();
const sizeBadge = isTile || tabletViewBadge ? "medium" : "small";
@ -116,9 +126,11 @@ const Badges = ({
const iconEdit = !isForm ? FileActionsConvertEditDocReactSvgUrl : iconForm;
const iconRefresh = RefreshReactSvgUrl;
const iconRefresh = desktopView ? Refresh12ReactSvgUrl : RefreshReactSvgUrl;
const iconPin = UnpinReactSvgUrl;
const iconMute =
sizeBadge === "medium" ? Mute16ReactSvgUrl : Mute12ReactSvgUrl;
const unpinIconProps = {
"data-id": id,
@ -146,7 +158,11 @@ const Badges = ({
lineHeight: "12px",
"data-id": id,
};
const unmuteIconProps = {
"data-id": id,
"data-rootfolderid": rootFolderId,
"data-new": newCount,
};
const onShowVersionHistoryProp = item.security?.ReadHistory
? { onClick: onShowVersionHistory }
: {};
@ -160,7 +176,7 @@ const Badges = ({
return fileExst ? (
<div className="badges additional-badges file__badges">
{isEditing && !isVisitor && (
{isEditing && !isVisitor && !isPdf && (
<ColorTheme
themeId={ThemeType.IconButton}
isEditing={isEditing}
@ -239,6 +255,16 @@ const Badges = ({
/>
)}
{isRoom && mute && (
<ColorTheme
themeId={ThemeType.IconButtonMute}
onClick={onUnmuteClick}
iconName={iconMute}
size={sizeBadge}
className="badge is-mute tablet-badge"
{...unmuteIconProps}
/>
)}
{isRoom && pinned && (
<ColorTheme
themeId={ThemeType.IconButtonPin}

View File

@ -7,16 +7,20 @@ import { classNames } from "@docspace/components/utils/classNames";
const EmptyFolderWrapper = styled.div`
.empty-folder_container {
.empty-folder_container-links {
display: grid;
grid-template-columns: 12px 1fr;
grid-column-gap: 8px;
display: flex;
.flex-wrapper_container {
display: flex;
flex-wrap: wrap;
row-gap: 16px;
column-gap: 8px;
justify-content: center;
.first-button {
display: flex;
.empty-folder_container-icon {
margin-right: 8px;
}
}
}
}

View File

@ -23,18 +23,20 @@ const OptionsComponent = (props) => {
return (
<>
<div className="empty-folder_container-links">
<IconButton
data-format="docx"
className="empty-folder_container-icon"
size={12}
onClick={onCreate}
iconName={PlusSvgUrl}
isFill
/>
<Box className="flex-wrapper_container">
<Link data-format="docx" onClick={onCreate} {...linkStyles}>
{t("Document")},
</Link>
<div className="first-button">
<IconButton
data-format="docx"
className="empty-folder_container-icon"
size={12}
onClick={onCreate}
iconName={PlusSvgUrl}
isFill
/>
<Link data-format="docx" onClick={onCreate} {...linkStyles}>
{t("Document")},
</Link>
</div>
<Link data-format="xlsx" onClick={onCreate} {...linkStyles}>
{t("Spreadsheet")},
</Link>

View File

@ -49,6 +49,7 @@ const Panels = (props) => {
ownerPanelVisible,
copyPanelVisible,
moveToPanelVisible,
restorePanelVisible,
thirdPartyMoveDialogVisible,
connectDialogVisible,
deleteThirdPartyDialogVisible,
@ -118,11 +119,15 @@ const Panels = (props) => {
/>
),
ownerPanelVisible && <ChangeOwnerPanel key="change-owner-panel" />,
(moveToPanelVisible || copyPanelVisible || restoreAllPanelVisible) && (
(moveToPanelVisible ||
copyPanelVisible ||
restorePanelVisible ||
restoreAllPanelVisible) && (
<FilesSelector
key="files-selector"
isMove={moveToPanelVisible}
isCopy={copyPanelVisible}
isRestore={restorePanelVisible}
isRestoreAll={restoreAllPanelVisible}
/>
),
@ -207,6 +212,7 @@ export default inject(
ownerPanelVisible,
copyPanelVisible,
moveToPanelVisible,
restorePanelVisible,
thirdPartyMoveDialogVisible,
connectDialogVisible,
deleteThirdPartyDialogVisible,
@ -262,6 +268,7 @@ export default inject(
ownerPanelVisible,
copyPanelVisible,
moveToPanelVisible,
restorePanelVisible,
thirdPartyMoveDialogVisible,
connectDialogVisible: connectDialogVisible || !!connectItem, //TODO:
deleteThirdPartyDialogVisible,

View File

@ -28,6 +28,7 @@ export type Item = {
isDisabled?: boolean;
security: Security;
roomType: number;
fileExst?: string;
};
export type BreadCrumb = {
@ -50,7 +51,7 @@ export type setTotalCallback = (value: number) => number;
export type useSocketHelperProps = {
socketHelper: any;
socketSubscribersId: Set<string>;
socketSubscribers: Set<string>;
setItems: (callback: setItemsCallback) => void;
setBreadCrumbs: (callback: setBreadCrumbsCallback) => void;
setTotal: (callback: setTotalCallback) => void;
@ -132,6 +133,7 @@ export type FilesSelectorProps = {
isMove?: boolean;
isCopy?: boolean;
isRestore: boolean;
isRestoreAll?: boolean;
isSelect?: boolean;
@ -150,10 +152,12 @@ export type FilesSelectorProps = {
disabledItems: string[] | number[];
isFolderActions?: boolean;
setMoveToPanelVisible: (value: boolean) => void;
setRestorePanelVisible: (value: boolean) => void;
setCopyPanelVisible: (value: boolean) => void;
setRestoreAllPanelVisible: (value: boolean) => void;
setIsFolderActions: (value: boolean) => void;
setMovingInProgress: (value: boolean) => void;
setSelected: (selected: "close" | "none", clearBuffer?: boolean) => void;
setConflictDialogData: (conflicts: any, operationData: any) => void;
itemOperationToFolder: (operationData: any) => Promise<void>;
clearActiveOperations: (
@ -204,8 +208,9 @@ export type FilesSelectorProps = {
includeFolder?: boolean;
socketHelper: any;
socketSubscribersId: Set<string>;
socketSubscribers: Set<string>;
currentDeviceType: "mobile" | "tablet" | "desktop";
embedded: boolean;
withHeader: boolean;
};

View File

@ -182,6 +182,7 @@ export const convertFoldersToItems = (
const {
id,
title,
roomType,
filesCount,
foldersCount,
security,
@ -190,6 +191,7 @@ export const convertFoldersToItems = (
}: {
id: number;
title: string;
roomType: number;
filesCount: number;
foldersCount: number;
security: Security;
@ -210,6 +212,7 @@ export const convertFoldersToItems = (
parentId,
rootFolderType,
isFolder: true,
roomType,
isDisabled: !!filterParam ? false : disabledItems.includes(id),
};
});
@ -246,6 +249,7 @@ export const convertFilesToItems = (files: any, filterParam?: string) => {
rootFolderType,
isFolder: false,
isDisabled: !filterParam,
fileExst,
};
});
return items;
@ -321,6 +325,10 @@ export const useFilesHelper = ({
case FilesSelectorFilterTypes.XLSX:
filter.filterType = FilterType.SpreadsheetsOnly;
break;
case FilesSelectorFilterTypes.ALL:
filter.filterType = FilterType.FilesOnly;
break;
}
}
@ -333,7 +341,7 @@ export const useFilesHelper = ({
isErrorPath = false
) => {
if (isInit && getRootData) {
const folder = await getFolderInfo(folderId);
const folder = await getFolderInfo(folderId, true);
const isArchive = folder.rootFolderType === FolderType.Archive;
@ -432,6 +440,7 @@ export const useFilesHelper = ({
try {
await setSettings(id);
} catch (e) {
sessionStorage.removeItem("filesSelectorPath");
if (isThirdParty && rootThirdPartyId) {
await setSettings(rootThirdPartyId, true);

View File

@ -10,6 +10,7 @@ import CatalogUserReactSvgUrl from "PUBLIC_DIR/images/catalog.user.react.svg?url
import { useRootHelperProps, Item } from "../FilesSelector.types";
import { defaultBreadCrumb } from "../utils";
import { getCatalogIconUrlByType } from "@docspace/common/utils/catalogIcon.helper";
const useRootHelper = ({
setBreadCrumbs,
@ -37,6 +38,8 @@ const useRootHelper = ({
}
currentTree?.forEach((folder) => {
const avatar = getCatalogIconUrlByType(folder.rootFolderType);
if (
folder.rootFolderType === FolderType.Rooms ||
folder.rootFolderType === FolderType.USER
@ -51,11 +54,7 @@ const useRootHelper = ({
foldersCount: folder.foldersCount,
security: folder.security,
isFolder: true,
avatar:
folder.rootFolderType === FolderType.Rooms
? CatalogFolderReactSvgUrl
: CatalogUserReactSvgUrl,
avatar,
});
}
});

View File

@ -11,7 +11,7 @@ import { convertRoomsToItems } from "./useRoomsHelper";
const useSocketHelper = ({
socketHelper,
socketSubscribersId,
socketSubscribers,
setItems,
setBreadCrumbs,
setTotal,
@ -23,9 +23,9 @@ const useSocketHelper = ({
const subscribe = (id: number) => {
const roomParts = `DIR-${id}`;
if (socketSubscribersId.has(roomParts)) return (subscribedId.current = id);
if (socketSubscribers.has(roomParts)) return (subscribedId.current = id);
if (subscribedId.current && !socketSubscribersId.has(roomParts)) {
if (subscribedId.current && !socketSubscribers.has(roomParts)) {
unsubscribe(subscribedId.current, false);
}
@ -45,7 +45,7 @@ const useSocketHelper = ({
subscribedId.current = null;
}
if (id && !socketSubscribersId.has(`DIR-${id}`)) {
if (id && !socketSubscribers.has(`DIR-${id}`)) {
socketHelper.emit({
command: "unsubscribe",
data: {

View File

@ -47,6 +47,7 @@ const FilesSelector = ({
isMove,
isCopy,
isRestore,
isRestoreAll,
isSelect,
@ -68,7 +69,9 @@ const FilesSelector = ({
itemOperationToFolder,
clearActiveOperations,
setMovingInProgress,
setSelected,
setMoveToPanelVisible,
setRestorePanelVisible,
setCopyPanelVisible,
setRestoreAllPanelVisible,
@ -91,12 +94,13 @@ const FilesSelector = ({
includeFolder,
socketHelper,
socketSubscribersId,
socketSubscribers,
setMoveToPublicRoomVisible,
setInfoPanelIsMobileHidden,
currentDeviceType,
embedded,
withHeader,
}: FilesSelectorProps) => {
const { t } = useTranslation(["Files", "Common", "Translations"]);
@ -117,6 +121,7 @@ const FilesSelector = ({
id: number | string;
title: string;
path?: string[];
fileExst?: string;
} | null>(null);
const [total, setTotal] = React.useState<number>(0);
@ -129,7 +134,7 @@ const FilesSelector = ({
const { subscribe, unsubscribe } = useSocketHelper({
socketHelper,
socketSubscribersId,
socketSubscribers,
setItems,
setBreadCrumbs,
setTotal,
@ -222,7 +227,11 @@ const FilesSelector = ({
getFileList(0, item.id, false, null);
}
} else {
setSelectedFileInfo({ id: item.id, title: item.title });
setSelectedFileInfo({
id: item.id,
title: item.title,
fileExst: item.fileExst,
});
}
};
@ -301,9 +310,9 @@ const FilesSelector = ({
const onCloseAction = () => {
setInfoPanelIsMobileHidden(false);
if (onClose) {
onClose();
return;
}
@ -312,11 +321,18 @@ const FilesSelector = ({
setIsFolderActions(false);
} else if (isRestoreAll) {
setRestoreAllPanelVisible(false);
} else if (isRestore) {
setRestorePanelVisible(false);
} else {
setMoveToPanelVisible(false);
}
};
const onCloseAndDeselectAction = () => {
setSelected("none");
onCloseAction();
};
const onSearchAction = (value: string) => {
setIsFirstLoad(true);
setItems(null);
@ -351,7 +367,7 @@ const FilesSelector = ({
breadCrumbs.findIndex((f: any) => f.roomType === RoomsType.PublicRoom) >
-1;
if ((isMove || isCopy || isRestoreAll) && !isEditorDialog) {
if ((isMove || isCopy || isRestore || isRestoreAll) && !isEditorDialog) {
const folderTitle = breadCrumbs[breadCrumbs.length - 1].label;
let fileIds: any[] = [];
@ -402,7 +418,7 @@ const FilesSelector = ({
setIsRequestRunning(false);
} else {
setIsRequestRunning(false);
onCloseAction();
onCloseAndDeselectAction();
const move = !isCopy;
if (move) setMovingInProgress(move);
sessionStorage.setItem("filesSelectorPath", `${selectedItemId}`);
@ -426,7 +442,7 @@ const FilesSelector = ({
onSave(null, selectedItemId, fileName, isChecked);
onSelectTreeNode && onSelectTreeNode(selectedTreeNode);
onSelectFile && onSelectFile(selectedFileInfo, breadCrumbs);
onCloseAction();
onCloseAndDeselectAction();
//!withoutImmediatelyClose && onCloseAction();
}
};
@ -437,7 +453,8 @@ const FilesSelector = ({
isRestoreAll,
isMove,
isSelect,
filterParam
filterParam,
isRestore
);
const acceptButtonLabel = getAcceptButtonLabel(
@ -446,7 +463,8 @@ const FilesSelector = ({
isRestoreAll,
isMove,
isSelect,
filterParam
filterParam,
isRestore
);
const isDisabled = getIsDisabled(
@ -461,11 +479,13 @@ const FilesSelector = ({
selectedItemSecurity,
filterParam,
!!selectedFileInfo,
includeFolder
includeFolder,
isRestore
);
const SelectorBody = (
<Selector
withHeader={withHeader}
headerLabel={headerLabel}
withoutBackButton
searchPlaceholder={t("Common:Search")}
@ -522,8 +542,12 @@ const FilesSelector = ({
descriptionText={
!filterParam ? "" : descriptionText ?? t("Common:SelectDOCXFormat")
}
acceptButtonId={isMove || isCopy ? "select-file-modal-submit" : ""}
cancelButtonId={isMove || isCopy ? "select-file-modal-cancel" : ""}
acceptButtonId={
isMove || isCopy || isRestore ? "select-file-modal-submit" : ""
}
cancelButtonId={
isMove || isCopy || isRestore ? "select-file-modal-cancel" : ""
}
/>
);
@ -549,7 +573,7 @@ const FilesSelector = ({
</>
);
return currentDeviceType === DeviceType.mobile ? (
return currentDeviceType === DeviceType.mobile && !embedded ? (
<Portal visible={isPanelVisible} element={<div>{selectorComponent}</div>} />
) : (
selectorComponent
@ -567,14 +591,9 @@ export default inject(
dialogsStore,
filesStore,
}: any,
{ isCopy, isRestoreAll, isMove, isPanelVisible, id }: any
{ isCopy, isRestoreAll, isMove, isRestore, isPanelVisible, id }: any
) => {
const {
id: selectedId,
parentId,
rootFolderType,
socketSubscribersId,
} = selectedFolderStore;
const { id: selectedId, parentId, rootFolderType } = selectedFolderStore;
const { setConflictDialogData, checkFileConflicts, setSelectedItems } =
filesActionsStore;
@ -590,13 +609,15 @@ export default inject(
: selectedId;
const currentFolderId =
sessionPath && (isMove || isCopy || isRestoreAll)
sessionPath && (isMove || isCopy || isRestore || isRestoreAll)
? +sessionPath
: fromFolderId;
const { treeFolders } = treeFoldersStore;
const {
restorePanelVisible,
setRestorePanelVisible,
moveToPanelVisible,
setMoveToPanelVisible,
copyPanelVisible,
@ -614,22 +635,29 @@ export default inject(
const { theme, socketHelper, currentDeviceType } = auth.settingsStore;
const socketSubscribesId = socketHelper.socketSubscribers;
const {
selection,
bufferSelection,
filesList,
setMovingInProgress,
setSelected,
} = filesStore;
const { isVisible: infoPanelIsVisible, selection: infoPanelSelection } =
auth.infoPanelStore;
const selections =
isMove || isCopy || isRestoreAll
isMove || isCopy || isRestoreAll || isRestore
? isRestoreAll
? filesList
: selection.length > 0 && selection[0] != null
? selection
: bufferSelection != null
? [bufferSelection]
: infoPanelIsVisible && infoPanelSelection != null
? [infoPanelSelection]
: []
: [];
@ -658,9 +686,13 @@ export default inject(
treeFolders,
isPanelVisible: isPanelVisible
? isPanelVisible
: (moveToPanelVisible || copyPanelVisible || restoreAllPanelVisible) &&
: (moveToPanelVisible ||
copyPanelVisible ||
restorePanelVisible ||
restoreAllPanelVisible) &&
!conflictResolveDialogVisible,
setMoveToPanelVisible,
setRestorePanelVisible,
theme,
selection: selectionsWithoutEditing,
disabledItems,
@ -670,6 +702,7 @@ export default inject(
itemOperationToFolder,
clearActiveOperations,
setMovingInProgress,
setSelected,
setCopyPanelVisible,
setRestoreAllPanelVisible,
setIsFolderActions,
@ -677,7 +710,7 @@ export default inject(
setInfoPanelIsMobileHidden,
includeFolder,
socketHelper,
socketSubscribersId,
socketSubscribers: socketSubscribesId,
setMoveToPublicRoomVisible,
currentDeviceType,
};

View File

@ -18,12 +18,16 @@ export const getHeaderLabel = (
isRestoreAll?: boolean,
isMove?: boolean,
isSelect?: boolean,
filterParam?: string
filterParam?: string,
isRestore?: boolean
) => {
if (isRestore) return t("Common:RestoreTo");
if (isMove) return t("Common:MoveTo");
if (isCopy) return t("Common:Copy");
if (isRestoreAll) return t("Common:Restore");
if (isSelect) return t("Common:SelectFile");
if (isSelect) {
return filterParam ? t("Common:SelectFile") : t("Common:SelectAction");
}
if (filterParam === FilesSelectorFilterTypes.DOCX)
return t("Translations:CreateMasterFormFromFile");
@ -38,8 +42,10 @@ export const getAcceptButtonLabel = (
isRestoreAll?: boolean,
isMove?: boolean,
isSelect?: boolean,
filterParam?: string
filterParam?: string,
isRestore?: boolean
) => {
if (isRestore) return t("Common:RestoreHere");
if (isMove) return t("Translations:MoveHere");
if (isCopy) return t("Translations:CopyHere");
if (isRestoreAll) return t("Common:RestoreHere");
@ -64,7 +70,8 @@ export const getIsDisabled = (
security?: Security,
filterParam?: string,
isFileSelected?: boolean,
includeFolder?: boolean
includeFolder?: boolean,
isRestore?: boolean
) => {
if (isFirstLoad) return true;
if (isRequestRunning) return true;
@ -74,7 +81,7 @@ export const getIsDisabled = (
if (isRooms) return true;
if (isRoot) return true;
if (isCopy) return !security?.CopyTo;
if (isMove || isRestoreAll) return !security?.MoveTo;
if (isMove || isRestoreAll || isRestore) return !security?.MoveTo;
return false;
};

View File

@ -28,6 +28,7 @@ const FilesSelectorInput = (props) => {
filterParam,
descriptionText,
className,
isSelect,
} = props;
const isFilesSelection = !!filterParam;
@ -94,6 +95,7 @@ const FilesSelectorInput = (props) => {
id={id}
onClose={onClose}
isPanelVisible={isPanelVisible}
isSelect={isSelect}
{...(isFilesSelection ? filesSelectionProps : foldersSelectionProps)}
/>
</StyledBodyWrapper>

View File

@ -8,6 +8,7 @@ import Button from "@docspace/components/button";
import ComboBox from "@docspace/components/combobox";
import Checkbox from "@docspace/components/checkbox";
import Box from "@docspace/components/box";
import FieldContainer from "@docspace/components/field-container";
const Dialog = ({
t,
@ -28,6 +29,8 @@ const Dialog = ({
withForm,
}) => {
const [value, setValue] = useState("");
const [isError, setIsError] = useState(false);
const [isDisabled, setIsDisabled] = useState(false);
const [isChecked, setIsChecked] = useState(false);
const [isChanged, setIsChanged] = useState(false);
@ -66,10 +69,15 @@ const Dialog = ({
let newValue = e.target.value;
if (newValue.match(folderFormValidation)) {
toastr.warning(t("Files:ContainsSpecCharacter"));
setIsError(true);
// toastr.warning(t("Files:ContainsSpecCharacter"));
} else {
setIsError(false);
}
newValue = newValue.replace(folderFormValidation, "_");
// newValue = newValue.replace(folderFormValidation, "_");
// console.log(folderFormValidation);
setValue(newValue);
setIsChanged(true);
@ -119,19 +127,27 @@ const Dialog = ({
>
<ModalDialog.Header>{title}</ModalDialog.Header>
<ModalDialog.Body>
<TextInput
id="create-text-input"
name="create"
type="search"
scale={true}
value={value}
isAutoFocussed={true}
tabIndex={1}
onChange={onChange}
onFocus={onFocus}
isDisabled={isDisabled}
maxLength={165}
/>
<FieldContainer
hasError={isError}
labelVisible={false}
errorMessageWidth={"100%"}
errorMessage={t("Files:ContainsSpecCharacter")}
removeMargin
>
<TextInput
id="create-text-input"
name="create"
type="search"
scale={true}
value={value}
isAutoFocussed={true}
tabIndex={1}
onChange={onChange}
onFocus={onFocus}
isDisabled={isDisabled}
maxLength={165}
/>
</FieldContainer>
{isCreateDialog && extension && (
<Box displayProp="flex" alignItems="center" paddingProp="16px 0 0">
<Checkbox
@ -162,7 +178,7 @@ const Dialog = ({
scale
primary
isLoading={isDisabled}
isDisabled={isDisabled}
isDisabled={isDisabled || isError}
onClick={onSaveAction}
/>
<Button

View File

@ -4,32 +4,19 @@ import PropTypes from "prop-types";
import MobileLayout from "./MobileLayout";
import { useNavigate, useLocation } from "react-router-dom";
import {
size as deviceSize,
isTablet as isTabletUtils,
isMobile as isMobileUtils,
tablet,
} from "@docspace/components/utils/device";
import {
isIOS,
isFirefox,
isSafari,
isMobile,
isMobileOnly,
isChrome,
isTablet,
isAndroid,
} from "react-device-detect";
import { isIOS, isMobile, isChrome, isAndroid } from "react-device-detect";
import { inject, observer } from "mobx-react";
const StyledContainer = styled.div`
user-select: none;
width: 100%;
height: ${(props) =>
isMobile && isIOS ? "calc(var(--vh, 1vh) * 100)" : props.contentHeight};
/* height: ${(props) =>
(props.isTabletView || isMobileOnly) && !isFirefox
? `${props.contentHeight}px`
: "100vh"}; */
#customScrollBar {
z-index: 0;
@ -81,6 +68,7 @@ const Layout = (props) => {
if (isMobile) {
window?.visualViewport?.addEventListener("resize", onOrientationChange);
window?.visualViewport?.addEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll);
}
@ -155,52 +143,41 @@ const Layout = (props) => {
}
if (isMobileUtils() && isAndroid && isChrome) {
// height = `calc(100vh - ${correctorMobileChrome}px)`;
height = `100%`;
}
// if (isTablet && isIOS && isSafari) {
// if (
// window.innerHeight < window.innerWidth &&
// window.innerWidth > 1024
// ) {
// height = window.screen.availHeight - correctorTabletSafari;
// }
// }
if (isIOS && isMobile && e?.type === "resize" && e?.target?.height) {
const diff = window.innerHeight - e.target.height;
windowHeight -= diff;
const root = document.getElementById("root");
if (!isMobileUtils()) {
const article = document.getElementsByTagName("article")[0];
}
document.body.style.height = `${e.target.height + e.target.offsetTop}`;
document.body.style.maxHeight = `${
e.target.height + e.target.offsetTop
}`;
document.body.style.minHeight = `${
e.target.height + e.target.offsetTop
}`;
root.style.height = `calc(var(--vh,1vh) * 100)`;
root.style.maxHeight = `calc(var(--vh,1vh) * 100)`;
root.style.minHeight = `calc(var(--vh,1vh) * 100)`;
document.body.style.height = `calc(var(--vh,1vh) * 100)`;
document.body.style.maxHeight = `calc(var(--vh,1vh) * 100)`;
document.body.style.minHeight = `calc(var(--vh,1vh) * 100)`;
document.body.style.top = "0px";
document.body.style.top = `0px`;
document.body.style.position = `fixed`;
document.body.style.overflow = `hidden`;
} else {
document.body.style.scroll = `hidden`;
} else if (isMobile && isIOS) {
document.body.style.height = `100%`;
document.body.style.maxHeight = `100%`;
document.body.style.minHeight = `100%`;
document.body.style.removeProperty("bottom");
document.body.style.removeProperty("position");
document.body.style.removeProperty("overflow");
}
if (isMobile && !isIOS) {
const root = document.getElementById("root");
root.style.height = `100%`;
root.style.maxHeight = `100%`;
root.style.minHeight = `100%`;
document.body.style.height = `100%`;
document.body.style.maxHeight = `100%`;
document.body.style.minHeight = `100%`;
document.body.style.removeProperty("top");
document.body.style.removeProperty("position");
document.body.style.removeProperty("overflow");
}
let vh = windowHeight * 0.01;
@ -229,11 +206,7 @@ const Layout = (props) => {
};
return (
<StyledContainer
className="Layout"
isTabletView={isTabletUtils()}
contentHeight={contentHeight}
>
<StyledContainer className="Layout" contentHeight={contentHeight}>
{isMobileUtils() ? <MobileLayout {...props} /> : children}
</StyledContainer>
);
@ -246,9 +219,12 @@ Layout.propTypes = {
};
export default inject(({ auth, bannerStore }) => {
const { isTabletView, setIsTabletView, setWindowWidth, isFrame } =
auth.settingsStore;
return {
isTabletView: auth.settingsStore.isTabletView,
setIsTabletView: auth.settingsStore.setIsTabletView,
setWindowWidth: auth.settingsStore.setWindowWidth,
isTabletView,
setIsTabletView,
setWindowWidth,
isFrame,
};
})(observer(Layout));

View File

@ -133,19 +133,16 @@ const NavMenu = (props) => {
} = props;
const isAsideAvailable = !!asideContent;
const hideHeader = isDesktop || (!showHeader && isFrame);
const hideHeader = !showHeader && isFrame;
if (currentDeviceType !== DeviceType.mobile || !isMobile()) return <></>;
if (currentDeviceType !== DeviceType.mobile || !isMobile() || hideHeader)
return <></>;
const isPreparationPortal = location.pathname === "/preparation-portal";
return (
<LayoutContextConsumer>
{(value) => (
<StyledContainer
isLoaded={isLoaded}
isVisible={value.isVisible}
isDesktop={hideHeader}
>
<StyledContainer isLoaded={isLoaded} isVisible={value.isVisible}>
<Backdrop
visible={isBackdropVisible}
onClick={backdropClick}

View File

@ -10,7 +10,7 @@ import DropDownItem from "@docspace/components/drop-down-item";
import { Base } from "@docspace/components/themes";
import { mobile, tablet } from "@docspace/components/utils/device";
import CrossIcon from "PUBLIC_DIR/images/cross.react.svg";
import CrossIcon from "PUBLIC_DIR/images/icons/17/cross.react.svg";
import Portal from "@docspace/components/portal";
const StyledWrapper = styled.div``;
@ -70,10 +70,10 @@ const StyledControlContainer = styled.div`
StyledControlContainer.defaultProps = { theme: Base };
const StyledCrossIcon = styled(CrossIcon)`
width: 12px;
height: 12px;
width: 17px;
height: 17px;
path {
fill: ${(props) => props.theme.catalog.control.fill};
stroke: ${(props) => props.theme.catalog.control.fill};
}
`;

View File

@ -57,6 +57,7 @@ const PeopleSelector = ({
withFooterCheckbox,
footerCheckboxLabel,
isChecked,
filterUserId,
}) => {
const [itemsList, setItemsList] = useState(items);
const [searchValue, setSearchValue] = useState("");
@ -138,6 +139,9 @@ const PeopleSelector = ({
};
const removeCurrentUserFromList = (listUser) => {
if (filterUserId) {
return listUser.filter((user) => user.id !== filterUserId);
}
return listUser.filter((user) => user.id !== currentUserId);
};

View File

@ -0,0 +1,13 @@
import React from "react";
import ModalDialogContainer from "../ModalDialogContainer";
import styled from "styled-components";
export const ChangeNameContainer = styled(ModalDialogContainer)`
#modal-dialog {
max-height: none;
}
.error-label {
position: relative;
}
`;

View File

@ -8,14 +8,10 @@ import TextInput from "@docspace/components/text-input";
import Button from "@docspace/components/button";
import toastr from "@docspace/components/toast/toastr";
import ModalDialogContainer from "../ModalDialogContainer";
import { ChangeNameContainer } from "./StyledChangeName";
const ChangeNameDialog = (props) => {
const { t, ready } = useTranslation([
"ProfileAction",
"PeopleTranslations",
"Common",
]);
const { t, ready } = useTranslation(["ProfileAction", "PeopleTranslations", "Common"]);
const {
visible,
onClose,
@ -23,11 +19,26 @@ const ChangeNameDialog = (props) => {
updateProfile,
updateProfileInUsers,
fromList,
userNameRegex,
} = props;
const [firstName, setFirstName] = useState(profile.firstName);
const [lastName, setLastName] = useState(profile.lastName);
const [isSaving, setIsSaving] = useState(false);
const nameRegex = new RegExp(userNameRegex, "gu");
const [isNameValid, setIsNameValid] = useState(true);
const [isSurnameValid, setIsSurnameValid] = useState(true);
const handleNameChange = (e) => {
setFirstName(e.target.value);
setIsNameValid(nameRegex.test(e.target.value.trim()));
};
const handleSurnameChange = (e) => {
setLastName(e.target.value);
setIsSurnameValid(nameRegex.test(e.target.value.trim()));
};
const onCloseAction = () => {
if (!isSaving) {
onClose();
@ -39,6 +50,14 @@ const ChangeNameDialog = (props) => {
};
const onSaveClick = async () => {
if (
!isNameValid ||
!isSurnameValid ||
firstName.trim().length === 0 ||
lastName.trim().length === 0
)
return;
const newProfile = profile;
newProfile.firstName = firstName;
newProfile.lastName = lastName;
@ -58,31 +77,34 @@ const ChangeNameDialog = (props) => {
};
return (
<ModalDialogContainer
<ChangeNameContainer
isLoading={!ready}
visible={visible}
onClose={onCloseAction}
displayType="modal"
>
<ModalDialog.Header>
{t("PeopleTranslations:NameChangeButton")}
</ModalDialog.Header>
displayType="modal">
<ModalDialog.Header>{t("PeopleTranslations:NameChangeButton")}</ModalDialog.Header>
<ModalDialog.Body className="change-name-dialog-body">
<FieldContainer
isVertical
labelText={t("Common:FirstName")}
className="field"
>
hasError={!isNameValid}
errorMessage={
firstName.trim().length === 0
? t("Common:RequiredField")
: t("Common:IncorrectFirstName")
}>
<TextInput
className="first-name"
scale={true}
isAutoFocussed={true}
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
onChange={handleNameChange}
placeholder={t("Common:FirstName")}
isDisabled={isSaving}
onKeyDown={onKeyDown}
tabIndex={1}
hasError={!isNameValid}
/>
</FieldContainer>
@ -90,16 +112,20 @@ const ChangeNameDialog = (props) => {
isVertical
labelText={t("Common:LastName")}
className="field"
>
hasError={!isSurnameValid}
errorMessage={
lastName.trim().length === 0 ? t("Common:RequiredField") : t("Common:IncorrectLastName")
}>
<TextInput
className="last-name"
scale={true}
value={lastName}
onChange={(e) => setLastName(e.target.value)}
onChange={handleSurnameChange}
placeholder={t("Common:LastName")}
isDisabled={isSaving}
onKeyDown={onKeyDown}
tabIndex={2}
hasError={!isSurnameValid}
/>
</FieldContainer>
</ModalDialog.Body>
@ -114,6 +140,12 @@ const ChangeNameDialog = (props) => {
onClick={onSaveClick}
isLoading={isSaving}
tabIndex={3}
isDisabled={
!isNameValid ||
!isSurnameValid ||
firstName.trim().length === 0 ||
lastName.trim().length === 0
}
/>
<Button
className="cancel-button"
@ -126,14 +158,16 @@ const ChangeNameDialog = (props) => {
tabIndex={4}
/>
</ModalDialog.Footer>
</ModalDialogContainer>
</ChangeNameContainer>
);
};
export default inject(({ peopleStore }) => {
export default inject(({ peopleStore, auth }) => {
const { updateProfile } = peopleStore.targetUserStore;
const { updateProfileInUsers } = peopleStore.usersStore;
return { updateProfile, updateProfileInUsers };
const { userNameRegex } = auth.settingsStore;
return { updateProfile, updateProfileInUsers, userNameRegex };
})(observer(ChangeNameDialog));

View File

@ -71,7 +71,9 @@ const ConflictResolveDialog = (props) => {
activeFiles,
setActiveFiles,
updateActiveFiles,
setSelected,
setMoveToPanelVisible,
setRestorePanelVisible,
setCopyPanelVisible,
setRestoreAllPanelVisible,
setMoveToPublicRoomVisible,
@ -97,6 +99,7 @@ const ConflictResolveDialog = (props) => {
const onClosePanels = () => {
setConflictResolveDialogVisible(false);
setMoveToPanelVisible(false);
setRestorePanelVisible(false);
setCopyPanelVisible(false);
setRestoreAllPanelVisible(false);
setMoveToPublicRoomVisible(false);
@ -139,7 +142,11 @@ const ConflictResolveDialog = (props) => {
}
updateActiveFiles(newActiveFiles);
if (!folderIds.length && !newFileIds.length) return onClosePanels();
if (!folderIds.length && !newFileIds.length) {
setSelected("none");
onClosePanels();
return;
}
const data = {
destFolderId,
@ -151,6 +158,7 @@ const ConflictResolveDialog = (props) => {
translations,
};
setSelected("none");
onClosePanels();
try {
sessionStorage.setItem("filesSelectorPath", `${destFolderId}`);
@ -268,13 +276,15 @@ export default inject(({ auth, dialogsStore, uploadDataStore, filesStore }) => {
conflictResolveDialogData,
conflictResolveDialogItems: items,
setMoveToPanelVisible,
setRestorePanelVisible,
setRestoreAllPanelVisible,
setCopyPanelVisible,
setMoveToPublicRoomVisible,
} = dialogsStore;
const { itemOperationToFolder } = uploadDataStore;
const { activeFiles, setActiveFiles, updateActiveFiles } = filesStore;
const { activeFiles, setActiveFiles, updateActiveFiles, setSelected } =
filesStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
@ -287,7 +297,9 @@ export default inject(({ auth, dialogsStore, uploadDataStore, filesStore }) => {
activeFiles,
setActiveFiles,
updateActiveFiles,
setSelected,
setMoveToPanelVisible,
setRestorePanelVisible,
setRestoreAllPanelVisible,
setCopyPanelVisible,
setMoveToPublicRoomVisible,

View File

@ -48,7 +48,7 @@ const CreateRoomDialog = ({
}) => {
const [isScrollLocked, setIsScrollLocked] = useState(false);
const [isOauthWindowOpen, setIsOauthWindowOpen] = useState(false);
const [isWrongTitle, setIsWrongTitle] = useState(false);
const isMountRef = React.useRef(true);
React.useEffect(() => {
@ -161,7 +161,9 @@ const CreateRoomDialog = ({
setIsScrollLocked={setIsScrollLocked}
isDisabled={isLoading}
isValidTitle={isValidTitle}
isWrongTitle={isWrongTitle}
setIsValidTitle={setIsValidTitle}
setIsWrongTitle={setIsWrongTitle}
enableThirdParty={enableThirdParty}
onKeyUp={onKeyUpHandler}
/>
@ -178,7 +180,7 @@ const CreateRoomDialog = ({
primary
scale
onClick={onCreateRoom}
isDisabled={isRoomTitleChanged}
isDisabled={isRoomTitleChanged || isWrongTitle}
isLoading={isLoading}
/>
<Button

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useRef, useCallback } from "react";
import TagHandler from "./handlers/TagHandler";
import SetRoomParams from "./sub-components/SetRoomParams";
@ -19,13 +19,29 @@ const EditRoomDialog = ({
}) => {
const [isScrollLocked, setIsScrollLocked] = useState(false);
const [isValidTitle, setIsValidTitle] = useState(true);
const [isWrongTitle, setIsWrongTitle] = useState(false);
const [roomParams, setRoomParams] = useState({
...fetchedRoomParams,
});
const setRoomTags = (newTags) =>
setRoomParams({ ...roomParams, tags: newTags });
const prevRoomParams = useRef(
Object.freeze({
...roomParams,
}),
);
const compareRoomParams = (prevParams, currentParams) => {
return (
prevParams.title === currentParams.title &&
prevParams.roomOwner.id === currentParams.roomOwner.id &&
prevParams.tags.sort().toString() === currentParams.tags.sort().toString() &&
((prevParams.icon.uploadedFile === "" && currentParams.icon.uploadedFile === null) ||
prevParams.icon.uploadedFile === currentParams.icon.uploadedFile)
);
};
const setRoomTags = (newTags) => setRoomParams({ ...roomParams, tags: newTags });
const tagHandler = new TagHandler(roomParams.tags, setRoomTags, fetchedTags);
@ -49,11 +65,16 @@ const EditRoomDialog = ({
};
useEffect(() => {
if (fetchedImage)
if (fetchedImage) {
setRoomParams({
...roomParams,
icon: { ...roomParams.icon, uploadedFile: fetchedImage },
});
prevRoomParams.current = {
...roomParams,
icon: { ...roomParams.icon, uploadedFile: fetchedImage },
};
}
}, [fetchedImage]);
const onCloseAction = () => {
@ -69,8 +90,7 @@ const EditRoomDialog = ({
visible={visible}
onClose={onCloseAction}
isScrollLocked={isScrollLocked}
withFooterBorder
>
withFooterBorder>
<ModalDialog.Header>
<DialogHeader isEdit />
</ModalDialog.Header>
@ -86,7 +106,9 @@ const EditRoomDialog = ({
isEdit
isDisabled={isLoading}
isValidTitle={isValidTitle}
isWrongTitle={isWrongTitle}
setIsValidTitle={setIsValidTitle}
setIsWrongTitle={setIsWrongTitle}
onKeyUp={onKeyUpHandler}
/>
</ModalDialog.Body>
@ -99,6 +121,7 @@ const EditRoomDialog = ({
primary
scale
onClick={onEditRoom}
isDisabled={isWrongTitle || compareRoomParams(prevRoomParams.current, roomParams)}
isLoading={isLoading}
/>
<Button

View File

@ -29,6 +29,7 @@ const InputParam = React.forwardRef(
onBlur,
isDisabled,
isValidTitle,
isWrongTitle,
errorMessage,
isAutoFocussed,
onKeyUp,
@ -49,8 +50,9 @@ const InputParam = React.forwardRef(
<FieldContainer
isVertical={true}
labelVisible={false}
hasError={!isValidTitle}
hasError={!isValidTitle || isWrongTitle}
errorMessage={errorMessage}
errorMessageWidth={"100%"}
>
<TextInput
forwardedRef={ref}

View File

@ -51,11 +51,14 @@ const SetRoomParams = ({
isDisabled,
isValidTitle,
setIsValidTitle,
isWrongTitle,
setIsWrongTitle,
onKeyUp,
enableThirdParty,
setChangeRoomOwnerIsVisible,
isAdmin,
userId,
folderFormValidation,
}) => {
const [previewIcon, setPreviewIcon] = React.useState(null);
@ -64,6 +67,12 @@ const SetRoomParams = ({
const onChangeName = (e) => {
setIsValidTitle(true);
if (e.target.value.match(folderFormValidation)) {
setIsWrongTitle(true);
// toastr.warning(t("Files:ContainsSpecCharacter"));
} else {
setIsWrongTitle(false);
}
setRoomParams({ ...roomParams, title: e.target.value });
};
@ -112,7 +121,12 @@ const SetRoomParams = ({
onChange={onChangeName}
isDisabled={isDisabled}
isValidTitle={isValidTitle}
errorMessage={t("Common:RequiredField")}
isWrongTitle={isWrongTitle}
errorMessage={
isWrongTitle
? t("Files:ContainsSpecCharacter")
: t("Common:RequiredField")
}
onKeyUp={onKeyUp}
isAutoFocussed={true}
/>
@ -184,7 +198,9 @@ const SetRoomParams = ({
export default inject(({ auth, dialogsStore }) => {
const { user } = auth.userStore;
const { setChangeRoomOwnerIsVisible } = dialogsStore;
const { folderFormValidation } = auth.settingsStore;
return {
folderFormValidation,
setChangeRoomOwnerIsVisible,
isAdmin: user.isAdmin || user.isOwner,
userId: user.id,

View File

@ -13,6 +13,7 @@ const DeleteDialogComponent = (props) => {
deleteAction,
unsubscribeAction,
setBufferSelection,
setSelected,
setRemoveMediaItem,
setDeleteDialogVisible,
visible,
@ -56,6 +57,7 @@ const DeleteDialogComponent = (props) => {
};
const onDelete = () => {
setSelected("none");
onClose();
const translations = {
@ -72,6 +74,7 @@ const DeleteDialogComponent = (props) => {
};
const onUnsubscribe = () => {
setSelected("none");
onClose();
if (!selection.length) return;
@ -95,6 +98,7 @@ const DeleteDialogComponent = (props) => {
successRemoveRooms: t("Files:RoomsRemoved"),
};
setSelected("none");
onClose();
const itemsIdDeleteHaveRights = selection
@ -261,8 +265,13 @@ const DeleteDialog = withTranslation([
export default inject(
({ filesStore, dialogsStore, filesActionsStore, treeFoldersStore, auth }) => {
const { selection, isLoading, bufferSelection, setBufferSelection } =
filesStore;
const {
selection,
isLoading,
bufferSelection,
setBufferSelection,
setSelected,
} = filesStore;
const { deleteAction, unsubscribeAction, deleteRoomsAction } =
filesActionsStore;
const { isPrivacyFolder, isRecycleBinFolder, isPersonalRoom, isRoom } =
@ -296,6 +305,7 @@ export default inject(
setRemoveMediaItem,
setBufferSelection,
setSelected,
isRoomDelete,
setIsRoomDelete,

View File

@ -5,6 +5,7 @@ import Text from "@docspace/components/text";
import Checkbox from "@docspace/components/checkbox";
import LinkWithDropdown from "@docspace/components/link-with-dropdown";
import DropDownItem from "@docspace/components/drop-down-item";
import { isMobile } from "@docspace/components/utils/device";
const DownloadRow = (props) => {
const {
@ -76,10 +77,10 @@ const DownloadRow = (props) => {
containerMinWidth="fit-content"
data={dropdownItems}
directionX="left"
directionY="bottom"
directionY={isMobile() ? "both" : "bottom"}
fontSize="13px"
fontWeight={600}
hasScroll={true}
hasScroll={isMobile()}
withExpander
>
{file.format || t("OriginalFormat")}

View File

@ -93,13 +93,15 @@ class DownloadDialogComponent extends React.Component {
const viewUrl = `${singleFileUrl}&outputtype=${file.value}`;
window.open(viewUrl, "_self");
}
this.props.setSelected("none");
this.onClose();
} else if (fileConvertIds.length || folderIds.length) {
this.onClose();
downloadFiles(fileConvertIds, folderIds, {
label: t("Translations:ArchivingData"),
error: t("Common:ErrorInternalServer"),
});
this.props.setSelected("none");
this.onClose();
}
};
@ -125,7 +127,7 @@ class DownloadDialogComponent extends React.Component {
};
onSelectFormat = (e) => {
const { format, type, fileId } = e.target.dataset;
const { format, type, fileId } = e.currentTarget.dataset;
const files = this.state[type].files;
this.setState((prevState) => {
@ -396,7 +398,7 @@ const DownloadDialog = withTranslation([
export default inject(
({ auth, filesStore, dialogsStore, filesActionsStore, settingsStore }) => {
const { sortedFiles } = filesStore;
const { sortedFiles, setSelected } = filesStore;
const { extsConvertible } = settingsStore;
const { theme } = auth.settingsStore;
@ -411,6 +413,7 @@ export default inject(
extsConvertible,
setDownloadDialogVisible,
setSelected,
downloadFiles,
theme,

View File

@ -56,7 +56,7 @@ const LeaveRoomDialog = (props) => {
})
.then(() => {
if (!isAdmin) {
if (isRoot) {
if (!isRoot) {
const filter = RoomsFilter.getDefault();
navigate(`rooms/shared/filter?${filter.toUrlParams()}`);
} else {
@ -145,7 +145,7 @@ export default inject(
const folderItem = selections[0] ? selections[0] : selectedFolderStore;
const isRoomOwner = folderItem?.createdBy?.id === user.id;
const isRoot = selection.length || bufferSelection ? false : true;
const isRoot = selectedFolderStore.isRootFolder;
return {
visible,

View File

@ -17,6 +17,7 @@ const MoveToPublicRoomComponent = (props) => {
setIsVisible,
setConflictResolveDialogVisible,
setMoveToPanelVisible,
setRestorePanelVisible,
setCopyPanelVisible,
setRestoreAllPanelVisible,
moveToPublicRoomData,
@ -26,6 +27,7 @@ const MoveToPublicRoomComponent = (props) => {
itemOperationToFolder,
clearActiveOperations,
setSelectedItems,
setSelected,
} = props;
const [isLoading, setIsLoading] = useState(false);
@ -53,7 +55,9 @@ const MoveToPublicRoomComponent = (props) => {
const onClosePanels = () => {
setIsVisible(false);
setConflictResolveDialogVisible(false);
setSelected("none");
setMoveToPanelVisible(false);
setRestorePanelVisible(false);
setCopyPanelVisible(false);
setRestoreAllPanelVisible(false);
};
@ -143,13 +147,14 @@ const MoveToPublicRoomDialog = withTranslation([
export default inject(
({ dialogsStore, filesActionsStore, filesStore, uploadDataStore }) => {
const { setMovingInProgress } = filesStore;
const { setMovingInProgress, setSelected } = filesStore;
const {
moveToPublicRoomVisible,
setMoveToPublicRoomVisible,
setConflictResolveDialogVisible,
setMoveToPanelVisible,
setRestorePanelVisible,
setCopyPanelVisible,
setRestoreAllPanelVisible,
moveToPublicRoomData,
@ -164,6 +169,7 @@ export default inject(
setIsVisible: setMoveToPublicRoomVisible,
setConflictResolveDialogVisible,
setMoveToPanelVisible,
setRestorePanelVisible,
setCopyPanelVisible,
setRestoreAllPanelVisible,
moveToPublicRoomData,
@ -173,6 +179,7 @@ export default inject(
itemOperationToFolder,
clearActiveOperations,
setSelectedItems,
setSelected,
};
}
)(observer(MoveToPublicRoomDialog));

View File

@ -54,6 +54,9 @@ const ChangeRoomOwner = (props) => {
folders,
setFolders,
currentDeviceType,
roomOwnerId,
isRootFolder,
setCreatedBy,
} = props;
const [isLoading, setIsLoading] = useState(false);
@ -93,17 +96,17 @@ const ChangeRoomOwner = (props) => {
});
};
const onChangeRoomOwner = (
user,
selectedAccess,
newFooterInputValue,
isChecked
) => {
const onChangeRoomOwner = (user, isChecked) => {
setIsLoading(true);
setRoomOwner(user[0].id, [roomId])
.then(async (res) => {
setFolder(res[0]);
if (isRootFolder) {
setFolder(res[0]);
} else {
setCreatedBy(res[0].createdBy);
}
if (isChecked) await onLeaveRoom();
else toastr.success(t("Files:AppointNewOwner"));
setRoomParams && setRoomParams(res[0].createdBy);
@ -154,6 +157,8 @@ const ChangeRoomOwner = (props) => {
withFooterCheckbox={!showBackButton}
footerCheckboxLabel={t("Files:LeaveTheRoom")}
isChecked={!showBackButton}
withOutCurrentAuthorizedUser
filterUserId={roomOwnerId}
/>
</Aside>
</StyledChangeRoomOwner>
@ -187,11 +192,11 @@ export default inject(
setFolders,
} = filesStore;
const roomId = selection.length
? selection[0].id
const room = selection.length
? selection[0]
: bufferSelection
? bufferSelection.id
: selectedFolderStore.id;
? bufferSelection
: selectedFolderStore;
const { currentDeviceType } = settingsStore;
@ -202,7 +207,10 @@ export default inject(
setRoomParams: changeRoomOwnerData.setRoomParams,
setRoomOwner,
userId: user.id,
roomId,
roomId: room.id,
roomOwnerId: room?.createdBy?.id,
isRootFolder: selectedFolderStore.isRootFolder,
setCreatedBy: selectedFolderStore.setCreatedBy,
setFolder,
updateRoomMemberRole,
isAdmin: user.isOwner || user.isAdmin,

View File

@ -22,7 +22,7 @@ const LinkBlock = (props) => {
return (
<div className="edit-link_link-block">
<Text className="edit-link-text" fontSize="13px" fontWeight={600}>
<Text className="edit-link-text" fontSize="16px" fontWeight={600}>
{t("LinkName")}
</Text>
<Text className="edit-link_required-icon" color="#F24724">
@ -33,11 +33,11 @@ const LinkBlock = (props) => {
scale
size="base"
withBorder
isAutoFocussed={false}
isAutoFocussed
className="edit-link_name-input"
value={linkNameValue}
onChange={onChangeLinkName}
placeholder={t("ExternalLink")}
placeholder={t("LinkName")}
isDisabled={isLoading}
/>
@ -50,7 +50,7 @@ const LinkBlock = (props) => {
isReadOnly
className="edit-link_link-input"
value={linkValue}
placeholder={t("ExternalLink")}
placeholder={t("LinkName")}
/>
)}
</div>

View File

@ -1,8 +1,9 @@
import styled, { css } from "styled-components";
import Box from "@docspace/components/box";
import Scrollbar from "@docspace/components/scrollbar";
import ModalDialog from "@docspace/components/modal-dialog";
const StyledEditLinkPanel = styled.div`
const StyledEditLinkPanel = styled(ModalDialog)`
.edit-link-panel {
.scroll-body {
${(props) =>
@ -16,19 +17,23 @@ const StyledEditLinkPanel = styled.div`
}
}
.modal-body {
padding: 0px 0px 8px;
}
.field-label-icon {
display: none;
}
.edit-link_body {
padding: 22px 0px 20px;
padding: 4px 0px 0px;
.edit-link_link-block {
padding: 0px 16px 20px 16px;
.edit-link-text {
display: inline-flex;
margin-bottom: 4px;
margin-bottom: 8px;
}
.edit-link_required-icon {

View File

@ -1,21 +1,14 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { observer, inject } from "mobx-react";
import { withTranslation } from "react-i18next";
import copy from "copy-to-clipboard";
import isEqual from "lodash/isEqual";
import Heading from "@docspace/components/heading";
import Backdrop from "@docspace/components/backdrop";
import Aside from "@docspace/components/aside";
import Button from "@docspace/components/button";
import toastr from "@docspace/components/toast/toastr";
import Portal from "@docspace/components/portal";
import {
StyledEditLinkPanel,
StyledScrollbar,
StyledButtons,
} from "./StyledEditLinkPanel";
import ModalDialog from "@docspace/components/modal-dialog";
import { StyledEditLinkPanel } from "./StyledEditLinkPanel";
import LinkBlock from "./LinkBlock";
import ToggleBlock from "./ToggleBlock";
@ -171,95 +164,87 @@ const EditLinkPanel = (props) => {
const isPrimary = link?.sharedTo?.primary;
const editLinkPanelComponent = (
<StyledEditLinkPanel isExpired={isExpired}>
<Backdrop
onClick={onClosePanel}
visible={visible}
isAside={true}
zIndex={310}
/>
<Aside
className="edit-link-panel"
visible={visible}
onClose={onClosePanel}
zIndex={310}
withoutBodyScroll
>
<div className="edit-link_header">
<Heading className="edit-link_heading">
{isEdit
? isPrimary
? t("Files:EditGeneralLink")
: isPublic
? t("Files:EditAdditionalLink")
: t("Files:EditLink")
: t("Files:CreateNewLink")}
</Heading>
<StyledEditLinkPanel
isExpired={isExpired}
displayType="aside"
visible={visible}
onClose={onClose}
isLarge
zIndex={310}
withBodyScroll={true}
withFooterBorder={true}
>
<ModalDialog.Header>
{isEdit
? isPrimary
? t("Files:EditGeneralLink")
: isPublic
? t("Files:EditAdditionalLink")
: t("Files:EditLink")
: t("Files:CreateNewLink")}
</ModalDialog.Header>
<ModalDialog.Body>
<div className="edit-link_body">
<LinkBlock
t={t}
isEdit={isEdit}
isLoading={isLoading}
shareLink={shareLink}
linkNameValue={linkNameValue}
setLinkNameValue={setLinkNameValue}
linkValue={linkValue}
setLinkValue={setLinkValue}
/>
<PasswordAccessBlock
t={t}
isLoading={isLoading}
headerText={t("Files:PasswordAccess")}
bodyText={t("Files:PasswordLink")}
isChecked={passwordAccessIsChecked}
isPasswordValid={isPasswordValid}
passwordValue={passwordValue}
setPasswordValue={setPasswordValue}
setIsPasswordValid={setIsPasswordValid}
onChange={onPasswordAccessChange}
/>
<ToggleBlock
isLoading={isLoading}
headerText={t("Files:DisableDownload")}
bodyText={t("Files:PreventDownloadFilesAndFolders")}
isChecked={denyDownload}
onChange={onDenyDownloadChange}
/>
{!isPrimary && (
<LimitTimeBlock
isExpired={isExpired}
isLoading={isLoading}
headerText={t("Files:LimitByTimePeriod")}
bodyText={expiredLinkText}
expirationDate={expirationDate}
setExpirationDate={setExpirationDate}
setIsExpired={setIsExpired}
language={language}
/>
)}
</div>
<StyledScrollbar stype="mediumBlack">
<div className="edit-link_body">
<LinkBlock
t={t}
isEdit={isEdit}
isLoading={isLoading}
shareLink={shareLink}
linkNameValue={linkNameValue}
setLinkNameValue={setLinkNameValue}
linkValue={linkValue}
setLinkValue={setLinkValue}
/>
<PasswordAccessBlock
t={t}
isLoading={isLoading}
headerText={t("Files:PasswordAccess")}
bodyText={t("Files:PasswordLink")}
isChecked={passwordAccessIsChecked}
isPasswordValid={isPasswordValid}
passwordValue={passwordValue}
setPasswordValue={setPasswordValue}
setIsPasswordValid={setIsPasswordValid}
onChange={onPasswordAccessChange}
/>
<ToggleBlock
isLoading={isLoading}
headerText={t("Files:DisableDownload")}
bodyText={t("Files:PreventDownloadFilesAndFolders")}
isChecked={denyDownload}
onChange={onDenyDownloadChange}
/>
{!isPrimary && (
<LimitTimeBlock
isExpired={isExpired}
isLoading={isLoading}
headerText={t("Files:LimitByTimePeriod")}
bodyText={expiredLinkText}
expirationDate={expirationDate}
setExpirationDate={setExpirationDate}
setIsExpired={setIsExpired}
language={language}
/>
)}
</div>
</StyledScrollbar>
<StyledButtons>
<Button
scale
primary
size="normal"
label={isEdit ? t("Common:SaveButton") : t("Common:Create")}
isDisabled={isLoading || !linkNameIsValid || isExpired}
onClick={onSave}
/>
<Button
scale
size="normal"
label={t("Common:CancelButton")}
isDisabled={isLoading}
onClick={onClose}
/>
</StyledButtons>
</Aside>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
scale
primary
size="normal"
label={isEdit ? t("Common:SaveButton") : t("Common:Create")}
isDisabled={isLoading || !linkNameIsValid || isExpired}
onClick={onSave}
/>
<Button
scale
size="normal"
label={t("Common:CancelButton")}
isDisabled={isLoading}
onClick={onClose}
/>
</ModalDialog.Footer>
</StyledEditLinkPanel>
);

View File

@ -11,7 +11,7 @@ import CopyReactSvgUrl from "PUBLIC_DIR/images/copy.react.svg?url";
import { StyledBody } from "./StyledEmbeddingPanel";
import { objectToGetParams } from "@docspace/common/utils";
const EmbeddingBody = ({ t, link, roomId }) => {
const EmbeddingBody = ({ t, link, requestToken, roomId }) => {
const [size, setSize] = useState("auto");
const [widthValue, setWidthValue] = useState("100%");
const [heightValue, setHeightValue] = useState("100%");
@ -19,14 +19,17 @@ const EmbeddingBody = ({ t, link, roomId }) => {
const config = {
width: `${widthValue}`,
height: `${heightValue}`,
frameId: "ds-frame",
frameId: "ds-frame-embedding",
mode: "manager",
init: true,
showHeader: true,
showTitle: true,
showMenu: false,
showFilter: true,
rootPath: "/rooms/shared/",
rootPath: "/rooms/share",
id: roomId,
requestToken,
withSubfolders: false,
};
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;

View File

@ -6,9 +6,18 @@ import Aside from "@docspace/components/aside";
import { withTranslation } from "react-i18next";
import { StyledEmbeddingPanel, StyledScrollbar } from "./StyledEmbeddingPanel";
import EmbeddingBody from "./EmbeddingBody";
import { DeviceType } from "@docspace/common/constants";
import Portal from "@docspace/components/portal";
const EmbeddingPanelComponent = (props) => {
const { t, link, roomId, visible, setEmbeddingPanelIsVisible } = props;
const {
t,
link,
requestToken,
roomId,
visible,
setEmbeddingPanelIsVisible,
currentDeviceType,
} = props;
const scrollRef = useRef(null);
@ -27,37 +36,66 @@ const EmbeddingPanelComponent = (props) => {
return () => document.removeEventListener("keyup", onKeyPress);
});
return (
const embeddingPanelComponent = (
<StyledEmbeddingPanel>
<Backdrop
onClick={onClose}
visible={visible}
isAside={true}
zIndex={210}
zIndex={310}
/>
<Aside className="embedding-panel" visible={visible} onClose={onClose}>
<Aside
className="embedding-panel"
visible={visible}
onClose={onClose}
withoutBodyScroll={true}
>
<div className="embedding_header">
<Heading className="hotkeys_heading">
{t("Files:EmbeddingSettings")}
</Heading>
</div>
<StyledScrollbar ref={scrollRef} stype="mediumBlack">
<EmbeddingBody t={t} link={link} roomId={roomId} />
<EmbeddingBody
t={t}
link={link}
requestToken={requestToken}
roomId={roomId}
/>
</StyledScrollbar>
</Aside>
</StyledEmbeddingPanel>
);
const renderPortal = () => {
const rootElement = document.getElementById("root");
return (
<Portal
element={embeddingPanelComponent}
appendTo={rootElement}
visible={visible}
/>
);
};
return currentDeviceType === DeviceType.mobile
? renderPortal()
: embeddingPanelComponent;
};
export default inject(({ dialogsStore }) => {
export default inject(({ dialogsStore, auth }) => {
const { embeddingPanelIsVisible, setEmbeddingPanelIsVisible, linkParams } =
dialogsStore;
const { currentDeviceType } = auth.settingsStore;
return {
visible: embeddingPanelIsVisible,
setEmbeddingPanelIsVisible,
link: linkParams?.link?.sharedTo?.shareLink,
requestToken: linkParams?.link?.sharedTo?.requestToken,
roomId: linkParams?.roomId,
currentDeviceType,
};
})(
withTranslation(["Files", "EmbeddingPanel"])(

View File

@ -59,7 +59,12 @@ const HotkeyPanel = ({
isAside={true}
zIndex={210}
/>
<Aside className="hotkeys-panel" visible={visible} onClose={onClose}>
<Aside
className="hotkeys-panel"
visible={visible}
onClose={onClose}
withoutBodyScroll={true}
>
<div className="hotkeys_header">
<Heading className="hotkeys_heading">{t("Common:Hotkeys")}</Heading>
</div>
@ -145,11 +150,8 @@ const HotkeyPanel = ({
HotkeyPanel.defaultProps = { theme: Base };
export default inject(({ auth, publicRoomStore }) => {
const {
hotkeyPanelVisible,
setHotkeyPanelVisible,
theme,
} = auth.settingsStore;
const { hotkeyPanelVisible, setHotkeyPanelVisible, theme } =
auth.settingsStore;
return {
visible: hotkeyPanelVisible,

View File

@ -9,7 +9,6 @@ import Button from "@docspace/components/button";
import HelpButton from "@docspace/components/help-button";
import Link from "@docspace/components/link";
import ToggleButton from "@docspace/components/toggle-button";
import { isMobileOnly, isTablet } from "react-device-detect";
import { mobile } from "@docspace/components/utils/device";
import CheckIcon from "PUBLIC_DIR/images/check.edit.react.svg";
import CrossIcon from "PUBLIC_DIR/images/cross.edit.react.svg";
@ -75,9 +74,7 @@ const StyledInvitePanel = styled.div`
css`
.trackYVisible {
.scroller {
margin-right: ${isMobileOnly || isTablet
? `-20px !important`
: `-17px !important`};
margin-right: -20px !important;
}
}
`}
@ -285,12 +282,19 @@ const StyledDropDown = styled(DropDown)`
align-items: center;
gap: 8px;
height: 48px;
.list-item_content {
text-overflow: ellipsis;
overflow: hidden;
}
}
`;
const SearchItemText = styled(Text)`
line-height: 16px;
text-overflow: ellipsis;
overflow: hidden;
font-size: ${(props) =>
props.primary ? "14px" : props.info ? "11px" : "12px"};
font-weight: ${(props) => (props.primary || props.info ? "600" : "400")};
@ -298,7 +302,7 @@ const SearchItemText = styled(Text)`
color: ${(props) =>
(props.primary && !props.disabled) || props.info
? props.theme.text.color
: props.theme.text.disableColor};
: props.theme.text.emailColor};
${(props) => props.info && `margin-left: auto`}
`;
@ -439,16 +443,11 @@ const StyledInviteLanguage = styled.div`
margin-left: 4px;
color: ${(props) => props.theme.createEditRoomDialog.commonParam.textColor};
}
@media ${mobile} {
justify-content: space-between;
}
.invitation-language {
margin-right: 4px;
color: ${(props) =>
props.theme.createEditRoomDialog.commonParam.descriptionColor};
@media ${mobile} {
margin-right: 0;
}
}
.language-combo-box {
.combo-button {

View File

@ -1,4 +1,10 @@
import React, { useEffect, useState, useMemo, useRef } from "react";
import React, {
useEffect,
useState,
useCallback,
useMemo,
useRef,
} from "react";
import { observer, inject } from "mobx-react";
import { withTranslation } from "react-i18next";
@ -25,6 +31,8 @@ import Scrollbar from "@docspace/components/scrollbar";
import InfoBar from "./sub-components/InfoBar";
import { DeviceType } from "@docspace/common/constants";
import InvitePanelLoader from "./sub-components/InvitePanelLoader";
import { TIMEOUT } from "../../../helpers/filesConstants";
const InvitePanel = ({
folders,
@ -53,6 +61,9 @@ const InvitePanel = ({
filter,
currentDeviceType,
}) => {
const [invitePanelIsLoding, setInvitePanelIsLoading] = useState(
!userLink || !guestLink || !adminLink || !collaboratorLink || roomId !== -1
);
const [selectedRoom, setSelectedRoom] = useState(null);
const [hasErrors, setHasErrors] = useState(false);
const [shareLinks, setShareLinks] = useState([]);
@ -70,6 +81,9 @@ const InvitePanel = ({
const inputsRef = useRef();
const invitePanelBodyRef = useRef();
const invitePanelWrapper = useRef(null);
const invitePanelRef = useRef(null);
const windowHeight = useRef(window.innerHeight);
const onChangeExternalLinksVisible = (visible) => {
setExternalLinksVisible(visible);
@ -80,15 +94,16 @@ const InvitePanel = ({
if (room) {
setSelectedRoom(room);
return Promise.resolve();
} else {
getFolderInfo(roomId).then((info) => {
return getFolderInfo(roomId).then((info) => {
setSelectedRoom(info);
});
}
};
const getInfo = () => {
getRoomSecurityInfo(roomId).then((links) => {
return getRoomSecurityInfo(roomId).then((links) => {
const link = links && links[0];
if (link) {
const { shareLink, id, title, expirationDate } = link.sharedTo;
@ -109,10 +124,20 @@ const InvitePanel = ({
});
};
const disableInvitePanelLoader = () => {
setTimeout(() => {
setInvitePanelIsLoading(false);
}, TIMEOUT);
};
useEffect(() => {
if (roomId === -1) {
if (!userLink || !guestLink || !adminLink || !collaboratorLink)
getPortalInviteLinks();
if (!userLink || !guestLink || !adminLink || !collaboratorLink) {
setInvitePanelIsLoading(true);
getPortalInviteLinks().finally(() => {
disableInvitePanelLoader();
});
}
setShareLinks([
{
@ -144,8 +169,10 @@ const InvitePanel = ({
return;
}
selectRoom();
getInfo();
setInvitePanelIsLoading(true);
Promise.all([selectRoom(), getInfo()]).finally(() => {
disableInvitePanelLoader(false);
});
}, [roomId, userLink, guestLink, adminLink, collaboratorLink]);
useEffect(() => {
@ -167,6 +194,25 @@ const InvitePanel = ({
isMobileView && window.addEventListener("mousedown", onMouseDown);
}, [isMobileView]);
useEffect(() => {
window.visualViewport.addEventListener("resize", onResize);
return () => {
window.visualViewport.removeEventListener("resize", onResize);
};
}, []);
const onResize = useCallback((e) => {
const diff = windowHeight.current - e.target.height;
if (invitePanelRef.current) {
invitePanelRef.current.style.height = `${e.target.height - 64}px`;
// invitePanelRef.current.style.bottom = `${diff}px`;
invitePanelWrapper.current.style.height = `${e.target.height}px`;
invitePanelWrapper.current.style.bottom = `${diff}px`;
}
}, []);
const onMouseDown = (e) => {
if (e.target.id === "InvitePanelWrapper") onClose();
};
@ -308,36 +354,40 @@ const InvitePanel = ({
<StyledBlock>
<StyledHeading>{t("Common:InviteUsers")}</StyledHeading>
</StyledBlock>
{scrollAllPanelContent ? (
<div className="invite-panel-body" ref={invitePanelBodyRef}>
<Scrollbar stype="mediumBlack">{bodyInvitePanel}</Scrollbar>
</div>
{invitePanelIsLoding ? (
<InvitePanelLoader />
) : (
bodyInvitePanel
)}
{hasInvitedUsers && (
<StyledButtons>
<Button
className="send-invitation"
scale={true}
size={"normal"}
isDisabled={hasErrors}
primary
onClick={onClickSend}
label={t("SendInvitation")}
isLoading={isLoading}
/>
<Button
className="cancel-button"
scale={true}
size={"normal"}
onClick={onClose}
label={t("Common:CancelButton")}
isDisabled={isLoading}
/>
</StyledButtons>
<>
{scrollAllPanelContent ? (
<div className="invite-panel-body" ref={invitePanelBodyRef}>
<Scrollbar stype="mediumBlack">{bodyInvitePanel}</Scrollbar>
</div>
) : (
bodyInvitePanel
)}
{hasInvitedUsers && (
<StyledButtons>
<Button
className="send-invitation"
scale={true}
size={"normal"}
isDisabled={hasErrors}
primary
onClick={onClickSend}
label={t("SendInvitation")}
isLoading={isLoading}
/>
<Button
className="cancel-button"
scale={true}
size={"normal"}
onClick={onClose}
label={t("Common:CancelButton")}
isDisabled={isLoading}
/>
</StyledButtons>
)}
</>
)}
</>
);
@ -347,9 +397,11 @@ const InvitePanel = ({
id="InvitePanelWrapper"
hasInvitedUsers={hasInvitedUsers}
scrollAllPanelContent={scrollAllPanelContent}
addUsersPanelVisible={addUsersPanelVisible}>
addUsersPanelVisible={addUsersPanelVisible}
ref={invitePanelWrapper}
>
{isMobileView ? (
<div className="invite_panel">
<div className="invite_panel" ref={invitePanelRef}>
<StyledControlContainer onClick={onClose}>
<StyledCrossIconMobile />
</StyledControlContainer>
@ -368,7 +420,8 @@ const InvitePanel = ({
visible={visible}
onClose={onClose}
withoutBodyScroll
zIndex={310}>
zIndex={310}
>
{invitePanelNode}
</Aside>
</>

View File

@ -93,6 +93,7 @@ const AccessSelector = ({
manualY={"0px"}
withoutBackground={isMobileView}
withBackground={!isMobileView}
withBlur={isMobileView}
/>
)}
</StyledAccessSelector>

View File

@ -190,10 +190,11 @@ const InviteInput = ({
onClick={addUser}
disabled={shared}
height={48}
heightTablet={48}
className="list-item"
>
<Avatar size="min" role="user" source={avatar} />
<div>
<div className="list-item_content">
<SearchItemText primary disabled={shared}>
{displayName}
</SearchItemText>
@ -245,16 +246,14 @@ const InviteInput = ({
};
const closeInviteInputPanel = (e) => {
if (e?.target.tagName.toUpperCase() === "INPUT") return;
if (e?.target?.tagName?.toUpperCase() === "INPUT") return;
setSearchPanelVisible(false);
};
const foundUsers = usersList.map((user) => getItemContent(user));
const addEmailPanel = isAddEmailPanelBlocked ? (
<></>
) : (
const addEmailPanel = (
<DropDownItem
className="add-item"
style={{ width: "inherit" }}
@ -344,6 +343,7 @@ const InviteInput = ({
onSelect={onLanguageSelect}
isDisabled={false}
scaled={isMobileView}
textOverflow
scaledOptions={false}
size="content"
showDisabledItems={true}
@ -399,7 +399,11 @@ const InviteInput = ({
<StyledDropDown
width={searchRef?.current?.offsetWidth}
isDefaultMode={false}
open={searchPanelVisible}
open={
!!usersList.length
? searchPanelVisible
: searchPanelVisible && !isAddEmailPanelBlocked
}
manualX="16px"
showDisabledItems
clickOutsideAction={closeInviteInputPanel}

View File

@ -0,0 +1,49 @@
import styled from "styled-components";
import { mobile } from "@docspace/components/utils/device";
export const InvitePanelLoaderWrapper = styled.div``;
export const ExternalLinksLoaderWrapper = styled.div`
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
grid-row-gap: 8px;
padding: 16px;
border-bottom: ${(props) => props.theme.filesPanels.sharing.borderBottom};
.external-links-loader__description {
width: 80%;
grid-column: 1/-1;
}
@media ${mobile} {
.external-links-loader__description {
width: 90%;
}
}
`;
export const InviteInputLoaderWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px;
`;
export const InviteInputLoaderHeaderWrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;
export const InviteInputLoaderFooterWrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-top: 8px;
`;

View File

@ -0,0 +1,40 @@
import React from "react";
//@ts-ignore
import Loaders from "@docspace/common/components/Loaders";
import {
ExternalLinksLoaderWrapper,
InviteInputLoaderFooterWrapper,
InviteInputLoaderHeaderWrapper,
InviteInputLoaderWrapper,
InvitePanelLoaderWrapper,
} from "./InvitePanelLoader.styled";
function InvitePanelLoader() {
return (
<InvitePanelLoaderWrapper>
<ExternalLinksLoaderWrapper>
<Loaders.Rectangle width="50%" height="22px" />
<Loaders.Rectangle width="28px" height="16px" />
<Loaders.Rectangle
className="external-links-loader__description"
height="16px"
/>
</ExternalLinksLoaderWrapper>
<InviteInputLoaderWrapper>
<InviteInputLoaderHeaderWrapper>
<Loaders.Rectangle width="115px" height="22px" />
<Loaders.Rectangle width="100px" height="19px" />
</InviteInputLoaderHeaderWrapper>
<Loaders.Rectangle width="100%" height="32px" />
<InviteInputLoaderFooterWrapper>
<Loaders.Rectangle height="32px" />
<Loaders.Rectangle width="90px" height="32px" />
</InviteInputLoaderFooterWrapper>
</InviteInputLoaderWrapper>
</InvitePanelLoaderWrapper>
);
}
export default InvitePanelLoader;

View File

@ -0,0 +1 @@
export { default } from "./InvitePanelLoader";

View File

@ -6,6 +6,7 @@ import Text from "@docspace/components/text";
import { parseAddresses } from "@docspace/components/utils/email";
import { getAccessOptions } from "../utils";
import { getUserRole } from "@docspace/common/utils";
import {
StyledEditInput,
@ -35,6 +36,7 @@ const Item = ({
const name = !!avatar ? (displayName !== "" ? displayName : email) : email;
const source = !!avatar ? avatar : AtReactSvgUrl;
const role = getUserRole(item);
const [edit, setEdit] = useState(false);
const [inputValue, setInputValue] = useState(name);
@ -168,7 +170,7 @@ const Item = ({
return (
<>
<Avatar size="min" role="user" source={source} />
<Avatar size="min" role={role} source={source} />
{edit ? editBody : displayBody}
</>
);

View File

@ -125,7 +125,6 @@ const StyledVersionHistoryPanel = styled.div`
}
.version-history-panel-header {
margin-bottom: 12px;
height: 53px;
${(props) =>
props.theme.interfaceDirection === "rtl"

View File

@ -50,6 +50,7 @@ const CreateUserForm = (props) => {
roomData,
capabilities,
currentColorScheme,
userNameRegex,
defaultPage,
} = props;
const inputRef = React.useRef(null);
@ -98,6 +99,8 @@ const CreateUserForm = (props) => {
setShowGreeting(false);
};
const nameRegex = new RegExp(userNameRegex, "gu");
/*useEffect(() => {
window.addEventListener("resize", onCheckGreeting);
return () => window.removeEventListener("resize", onCheckGreeting);
@ -138,12 +141,12 @@ const CreateUserForm = (props) => {
let hasError = false;
if (!fname.trim()) {
if (!fname.trim() || !fnameValid) {
hasError = true;
setFnameValid(!hasError);
}
if (!sname.trim()) {
if (!sname.trim() || !snameValid) {
hasError = true;
setSnameValid(!hasError);
}
@ -175,8 +178,8 @@ const CreateUserForm = (props) => {
};
const personalData = {
firstname: fname,
lastname: sname,
firstname: fname.trim(),
lastname: sname.trim(),
email: email,
};
@ -235,11 +238,7 @@ const CreateUserForm = (props) => {
const fromInviteLink = linkData.type === "LinkInvite" ? true : false;
const data = Object.assign(
{ fromInviteLink: fromInviteLink },
registerData,
loginData
);
const data = Object.assign({ fromInviteLink: fromInviteLink }, registerData, loginData);
await createUser(data, key);
@ -279,13 +278,13 @@ const CreateUserForm = (props) => {
const onChangeFname = (e) => {
setFname(e.target.value);
setFnameValid(true);
setFnameValid(nameRegex.test(e.target.value.trim()));
setErrorText("");
};
const onChangeSname = (e) => {
setSname(e.target.value);
setSnameValid(true);
setSnameValid(nameRegex.test(e.target.value.trim()));
setErrorText("");
};
@ -318,7 +317,7 @@ const CreateUserForm = (props) => {
: window.open(
url,
"login",
"width=800,height=500,status=no,toolbar=no,menubar=no,resizable=yes,scrollbars=no"
"width=800,height=500,status=no,toolbar=no,menubar=no,resizable=yes,scrollbars=no",
);
getOAuthToken(tokenGetterWin).then((code) => {
@ -327,7 +326,7 @@ const CreateUserForm = (props) => {
auth: providerName,
mode: "popup",
callback: "authCallback",
})
}),
);
tokenGetterWin.location.href = getLoginLink(token, code);
@ -344,8 +343,7 @@ const CreateUserForm = (props) => {
if (!providersData[item.provider]) return;
if (index > 1) return;
const { icon, label, iconOptions, className } =
providersData[item.provider];
const { icon, label, iconOptions, className } = providersData[item.provider];
return (
<div className="buttonWrapper" key={`${item.provider}ProviderItem`}>
@ -371,10 +369,7 @@ const CreateUserForm = (props) => {
<SocialButton
iconName={SsoReactSvgUrl}
className="socialButton"
label={
capabilities?.ssoLabel ||
getProviderTranslation("sso", t, false, true)
}
label={capabilities?.ssoLabel || getProviderTranslation("sso", t, false, true)}
onClick={() => (window.location.href = capabilities?.ssoUrl)}
/>
</div>
@ -424,12 +419,7 @@ const CreateUserForm = (props) => {
<ConfirmContainer>
<GreetingContainer>
<DocspaceLogo className="docspace-logo" />
<Text
fontSize="23px"
fontWeight={700}
textAlign="left"
className="greeting-title"
>
<Text fontSize="23px" fontWeight={700} textAlign="left" className="greeting-title">
{greetingTitle}
</Text>
@ -437,11 +427,7 @@ const CreateUserForm = (props) => {
<>
{user && (
<div className="greeting-block">
<Avatar
className="avatar"
role="user"
source={userAvatar}
/>
<Avatar className="avatar" role="user" source={userAvatar} />
<div className="user-info">
<Text fontSize="15px" fontWeight={600}>
{user.firstName} {user.lastName}
@ -456,12 +442,7 @@ const CreateUserForm = (props) => {
<div className="tooltip">
<p className="tooltiptext">
{roomName ? (
<Trans
t={t}
i18nKey="WelcomeToRoom"
ns="Confirm"
key={roomName}
>
<Trans t={t} i18nKey="WelcomeToRoom" ns="Confirm" key={roomName}>
Welcome to the <strong>{{ roomName }}</strong> room!
</Trans>
) : (
@ -484,9 +465,7 @@ const CreateUserForm = (props) => {
<RegisterContainer>
{!emailFromLink && (
<>
{ssoExists() && (
<ButtonsWrapper>{ssoButton()}</ButtonsWrapper>
)}
{ssoExists() && <ButtonsWrapper>{ssoButton()}</ButtonsWrapper>}
{oauthDataExists() && (
<>
@ -499,8 +478,7 @@ const CreateUserForm = (props) => {
fontWeight="600"
color={currentColorScheme?.main?.accent}
className="more-label"
onClick={moreAuthOpen}
>
onClick={moreAuthOpen}>
{t("Common:ShowMore")}
</Link>
)}
@ -526,11 +504,8 @@ const CreateUserForm = (props) => {
labelVisible={false}
hasError={isEmailErrorShow && !emailValid}
errorMessage={
emailErrorText
? t(`Common:${emailErrorText}`)
: t("Common:RequiredField")
}
>
emailErrorText ? t(`Common:${emailErrorText}`) : t("Common:RequiredField")
}>
<EmailInput
id="login"
name="login"
@ -558,9 +533,12 @@ const CreateUserForm = (props) => {
labelVisible={false}
hasError={!fnameValid}
errorMessage={
errorText ? errorText : t("Common:RequiredField")
}
>
errorText
? errorText
: fname.trim().length === 0
? t("Common:RequiredField")
: t("Common:IncorrectFirstName")
}>
<TextInput
id="first-name"
name="first-name"
@ -583,9 +561,12 @@ const CreateUserForm = (props) => {
labelVisible={false}
hasError={!snameValid}
errorMessage={
errorText ? errorText : t("Common:RequiredField")
}
>
errorText
? errorText
: sname.trim().length === 0
? t("Common:RequiredField")
: t("Common:IncorrectLastName")
}>
<TextInput
id="last-name"
name="last-name"
@ -607,10 +588,10 @@ const CreateUserForm = (props) => {
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
errorMessage={`${t("Common:PasswordLimitMessage")}: ${getPasswordErrorMessage(
t,
settings,
)}`}>
<PasswordInput
simpleView={false}
hideNewPasswordButton
@ -631,21 +612,13 @@ const CreateUserForm = (props) => {
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
onValidateInput={onValidatePassword}
tooltipPasswordTitle={`${t(
"Common:PasswordLimitMessage"
)}:`}
tooltipPasswordLength={`${t(
"Common:PasswordMinimumLength"
)}: ${settings ? settings.minLength : 8}`}
tooltipPasswordDigits={`${t(
"Common:PasswordLimitDigits"
)}`}
tooltipPasswordCapital={`${t(
"Common:PasswordLimitUpperCase"
)}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
tooltipPasswordTitle={`${t("Common:PasswordLimitMessage")}:`}
tooltipPasswordLength={`${t("Common:PasswordMinimumLength")}: ${
settings ? settings.minLength : 8
}`}
tooltipPasswordDigits={`${t("Common:PasswordLimitDigits")}`}
tooltipPasswordCapital={`${t("Common:PasswordLimitUpperCase")}`}
tooltipPasswordSpecial={`${t("Common:PasswordLimitSpecialSymbols")}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
/>
</FieldContainer>
@ -655,11 +628,7 @@ const CreateUserForm = (props) => {
primary
size="medium"
scale={true}
label={
isLoading
? t("Common:LoadingProcessing")
: t("LoginRegistryButton")
}
label={isLoading ? t("Common:LoadingProcessing") : t("LoginRegistryButton")}
tabIndex={1}
isDisabled={isLoading}
isLoading={isLoading}
@ -675,11 +644,7 @@ const CreateUserForm = (props) => {
primary
size="medium"
scale={true}
label={
isLoading
? t("Common:LoadingProcessing")
: t("LoginRegistryButton")
}
label={isLoading ? t("Common:LoadingProcessing") : t("LoginRegistryButton")}
tabIndex={1}
isDisabled={isLoading}
isLoading={isLoading}
@ -722,6 +687,7 @@ export default inject(({ auth }) => {
getSettings,
getPortalPasswordSettings,
currentColorScheme,
userNameRegex,
} = settingsStore;
return {
@ -737,9 +703,6 @@ export default inject(({ auth }) => {
providers,
capabilities,
currentColorScheme,
userNameRegex,
};
})(
withTranslation(["Confirm", "Common", "Wizard"])(
withLoader(observer(CreateUserForm))
)
);
})(withTranslation(["Confirm", "Common", "Wizard"])(withLoader(observer(CreateUserForm))));

View File

@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { inject, observer } from "mobx-react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import ModalDialog from "@docspace/components/modal-dialog";
import Text from "@docspace/components/text";
import Box from "@docspace/components/box";
@ -16,11 +17,52 @@ const StyledModalDialog = styled(ModalDialog)`
max-width: 733px;
height: auto;
width: auto;
/* Light theme. */
--color-border-default: #d0d7de;
--color-border-muted: hsla(210, 18%, 87%, 1);
.modal-footer {
padding-right: 4px;
}
a {
color: #4781d1;
}
}
.markdown-wrapper {
width: 100%;
}
table {
border-spacing: 0;
border-collapse: collapse;
display: block;
margin-top: 0;
margin-bottom: 16px;
width: max-content;
max-width: 100%;
overflow: auto;
}
tr {
border-top: 1px solid var(--color-border-muted);
}
td,
th {
padding: 6px 13px;
border: 1px solid var(--color-border-default);
}
th {
font-weight: 600;
}
table img {
background-color: transparent;
}
`;
const DebugInfoDialog = (props) => {
@ -69,7 +111,25 @@ const DebugInfoDialog = (props) => {
heightProp={"362px"}
>
<Scrollbar stype="mediumBlack">
{md && <ReactMarkdown children={md} />}
{md && (
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
a: ({ node, href, children, ...props }) => (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
{...props}
>
{children}
</a>
),
}}
>
{md}
</ReactMarkdown>
)}
</Scrollbar>
</Box>
</ModalDialog.Footer>

View File

@ -1,5 +1,7 @@
import EmptyScreenFormGalleryReactSvgUrl from "PUBLIC_DIR/images/empty_screen_form-gallery.react.svg?url";
import React, { useEffect } from "react";
import EmptyScreenFilterAltSvgUrl from "PUBLIC_DIR/images/empty_screen_filter_alt.svg?url";
import EmptyScreenFilterAltDarkSvgUrl from "PUBLIC_DIR/images/empty_screen_filter_alt_dark.svg?url";
import { useEffect } from "react";
import { observer, inject } from "mobx-react";
import EmptyScreenContainer from "@docspace/components/empty-screen-container";
import { withTranslation } from "react-i18next";
@ -7,13 +9,40 @@ import TileContainer from "./TilesView/sub-components/TileContainer";
import FileTile from "./TilesView/FileTile";
import Loaders from "@docspace/common/components/Loaders";
import SubmitToGalleryTile from "./TilesView/sub-components/SubmitToGalleryTile";
import Link from "@docspace/components/link";
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
import IconButton from "@docspace/components/icon-button";
import styled from "styled-components";
const StyledEmptyContainerLinks = styled.div`
display: grid;
margin: 13px 0;
grid-template-columns: 12px 1fr;
grid-column-gap: 8px;
.icon {
height: 20px;
width: 12px;
margin: ${({ theme }) =>
theme.interfaceDirection !== "rtl" ? "4px 4px 0 0;" : "4px 0 0 4px;"};
cursor: pointer;
}
.link {
color: ${({ theme }) => theme.filesEmptyContainer.linkColor};
margin: ${({ theme }) =>
theme.interfaceDirection !== "rtl" ? "0 7px 0 0" : "0 0 0 7px;"};
}
`;
const SectionBodyContent = ({
t,
tReady,
theme,
oformFiles,
hasGalleryFiles,
setGallerySelected,
resetFilters,
submitToGalleryTileIsVisible,
canSubmitToFormGallery,
}) => {
@ -41,10 +70,35 @@ const SectionBodyContent = ({
<Loaders.Tiles foldersCount={0} withTitle={false} />
) : !hasGalleryFiles ? (
<EmptyScreenContainer
imageSrc={EmptyScreenFormGalleryReactSvgUrl}
imageSrc={
theme.isBase
? EmptyScreenFilterAltSvgUrl
: EmptyScreenFilterAltDarkSvgUrl
}
imageAlt="Empty Screen Gallery image"
headerText={t("GalleryEmptyScreenHeader")}
descriptionText={t("EmptyScreenDescription")}
headerText={t("Common:NotFoundTitle")}
descriptionText={t("FormGallery:EmptyFormGalleryScreenDescription")}
buttons={
<StyledEmptyContainerLinks theme={theme}>
<IconButton
className={"icon"}
size="12"
onClick={resetFilters}
iconName={ClearEmptyFilterSvgUrl}
isFill
/>
<Link
className={"link"}
onClick={resetFilters}
isHovered={true}
type={"action"}
fontWeight={"600"}
display={"flex"}
>
{t("Common:ClearFilter")}
</Link>
</StyledEmptyContainerLinks>
}
/>
) : (
<TileContainer className="tile-container">
@ -58,10 +112,12 @@ const SectionBodyContent = ({
);
};
export default inject(({ accessRightsStore, oformsStore, auth }) => ({
export default inject(({ auth, accessRightsStore, oformsStore }) => ({
theme: auth.settingsStore.theme,
oformFiles: oformsStore.oformFiles,
hasGalleryFiles: oformsStore.hasGalleryFiles,
setGallerySelected: oformsStore.setGallerySelected,
resetFilters: oformsStore.resetFilters,
submitToGalleryTileIsVisible: oformsStore.submitToGalleryTileIsVisible,
canSubmitToFormGallery: accessRightsStore.canSubmitToFormGallery,
}))(withTranslation("FormGallery")(observer(SectionBodyContent)));
}))(withTranslation("Common, FormGallery")(observer(SectionBodyContent)));

View File

@ -0,0 +1,82 @@
import * as Styled from "./index.styled";
import { inject } from "mobx-react";
import { withTranslation } from "react-i18next";
const SubList = ({
categoryType,
categories,
isDropdownOpen,
isSubHovered,
marginTop,
onCloseDropdown,
getCategoryTitle,
filterOformsByCategory,
setOformsCurrentCategory,
}) => {
const onPreventDefault = (e) => e.preventDefault();
const onFilterByCategory = (category) => {
onCloseDropdown();
setOformsCurrentCategory(category);
filterOformsByCategory(categoryType, category.id);
};
return (
<Styled.CategoryFilterSubList
open={isDropdownOpen}
isSubHovered={isSubHovered}
marginTop={marginTop}
id={`category-sub-list-${categoryType}`}
className={`dropdown-sub sub-by-${categoryType}`}
directionX={"right"}
directionY={"bottom"}
manualY={"0px"}
manualX={"0px"}
clickOutsideAction={() => {}}
maxHeight={296}
manualWidth={"206px"}
showDisabledItems={false}
isDefaultMode={false}
withBackdrop={false}
withBackground={false}
isMobileView={false}
isNoFixedHeightOptions={false}
>
{categories.map((category) => {
const categoryTitle = getCategoryTitle(category);
const onCategoryClick = () => onFilterByCategory(category);
return (
<Styled.CategoryFilterSubListItem
className="dropdown-item"
height={36}
heightTablet={36}
key={category.id}
onClick={onCategoryClick}
onMouseDown={onPreventDefault}
title={categoryTitle}
>
<div
className="item-content"
style={{
maxWidth: "182px",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{categoryTitle}
</div>
</Styled.CategoryFilterSubListItem>
);
})}
</Styled.CategoryFilterSubList>
);
};
export default inject(({ oformsStore }) => ({
getCategoryTitle: oformsStore.getCategoryTitle,
setOformsCurrentCategory: oformsStore.setOformsCurrentCategory,
filterOformsByCategory: oformsStore.filterOformsByCategory,
}))(withTranslation(["FormGallery", "Common"])(SubList));

View File

@ -0,0 +1,99 @@
import * as Styled from "./index.styled";
import DropDownItem from "@docspace/components/drop-down-item";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import SubList from "./SubList";
const CategoryFilterDesktop = ({
t,
menuItems,
currentCategory,
getCategoryTitle,
filterOformsByCategory,
...rest
}) => {
const [isOpen, setIsOpen] = useState(false);
const onToggleDropdownIsOpen = () => setIsOpen(!isOpen);
const onCloseDropdown = () => setIsOpen(false);
const [hoveredSub, setHoveredSub] = useState(null);
const onViewAllTemplates = () => filterOformsByCategory("", "");
return (
<Styled.CategoryFilterWrapper {...rest}>
<Styled.CategoryFilter
id="comboBoxLanguage"
tabIndex={1}
className={"combobox"}
opened={isOpen}
onClick={onToggleDropdownIsOpen}
onSelect={onCloseDropdown}
isDisabled={false}
showDisabledItems={true}
options={[]}
directionX={"right"}
directionY={"both"}
scaled={true}
size={"content"}
disableIconClick={false}
disableItemClick={false}
isDefaultMode={false}
fixedDirection={true}
advancedOptionsCount={5}
selectedOption={{
label:
getCategoryTitle(currentCategory) || t("FormGallery:Categories"),
}}
advancedOptions={
<>
<Styled.CategoryFilterItem
id={"ViewAllTemplates"}
key={"ViewAllTemplates"}
title={t("FormGallery:ViewAllTemplates")}
className="dropdown-item"
label={t("FormGallery:ViewAllTemplates")}
onClick={onViewAllTemplates}
onMouseEnter={() => setHoveredSub(null)}
/>
<DropDownItem isSeparator />
{menuItems?.map((item) => (
<Styled.CategoryFilterItem
id={item.key}
key={item.key}
title={t("FormGallery:FormsByBranch")}
className={`item-by-${item.key}`}
label={item.label}
onMouseEnter={() => setHoveredSub(item.key)}
onMouseLeave={() => setHoveredSub(null)}
isSubMenu
/>
))}
</>
}
/>
{menuItems?.map((item, index) => (
<SubList
key={item.key}
categoryType={item.key}
categories={item.categories || []}
isDropdownOpen={isOpen}
isSubHovered={hoveredSub === item.key}
marginTop={`${83 + index * 32}px`}
onCloseDropdown={onCloseDropdown}
/>
))}
</Styled.CategoryFilterWrapper>
);
};
export default inject(({ oformsStore }) => ({
currentCategory: oformsStore.currentCategory,
getCategoryTitle: oformsStore.getCategoryTitle,
filterOformsByCategory: oformsStore.filterOformsByCategory,
}))(withTranslation(["FormGallery"])(observer(CategoryFilterDesktop)));

Some files were not shown because too many files have changed in this diff Show More