diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index c4954948a72..b77c76acf5a 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -16,12 +16,14 @@ use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\AppFramework\IAppContainer; use OCP\Federation\ICloudFederationProviderManager; class Application extends App implements IBootstrap { + + public const APP_ID = 'federatedfilesharing'; + public function __construct() { - parent::__construct('federatedfilesharing'); + parent::__construct(self::APP_ID); } public function register(IRegistrationContext $context): void { @@ -33,14 +35,13 @@ class Application extends App implements IBootstrap { $context->injectFn(Closure::fromCallable([$this, 'registerCloudFederationProvider'])); } - private function registerCloudFederationProvider(ICloudFederationProviderManager $manager, - IAppContainer $appContainer): void { + private function registerCloudFederationProvider(ICloudFederationProviderManager $manager): void { $fileResourceTypes = ['file', 'folder']; foreach ($fileResourceTypes as $type) { $manager->addCloudFederationProvider($type, 'Federated Files Sharing', - function () use ($appContainer): CloudFederationProviderFiles { - return $appContainer->get(CloudFederationProviderFiles::class); + function (): CloudFederationProviderFiles { + return \OCP\Server::get(CloudFederationProviderFiles::class); }); } } diff --git a/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php b/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php index 34fbd85db5a..b8a4a7d03e7 100644 --- a/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php +++ b/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php @@ -8,6 +8,7 @@ declare(strict_types=1); */ namespace OCA\FederatedFileSharing\Listeners; +use OCA\FederatedFileSharing\AppInfo\Application; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\App\IAppManager; @@ -35,7 +36,8 @@ class LoadAdditionalScriptsListener implements IEventListener { if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled()) { $this->initialState->provideInitialState('notificationsEnabled', $this->appManager->isEnabledForUser('notifications')); - Util::addInitScript('federatedfilesharing', 'external'); + Util::addStyle(Application::APP_ID, 'init-files'); + Util::addInitScript(Application::APP_ID, 'init-files'); } } } diff --git a/apps/federatedfilesharing/lib/Settings/Admin.php b/apps/federatedfilesharing/lib/Settings/Admin.php index fc685f952c7..e66f1a1e417 100644 --- a/apps/federatedfilesharing/lib/Settings/Admin.php +++ b/apps/federatedfilesharing/lib/Settings/Admin.php @@ -6,6 +6,7 @@ */ namespace OCA\FederatedFileSharing\Settings; +use OCA\FederatedFileSharing\AppInfo\Application; use OCA\FederatedFileSharing\FederatedShareProvider; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; @@ -43,7 +44,9 @@ class Admin implements IDelegatedSettings { $this->initialState->provideInitialState('lookupServerUploadEnabled', $this->fedShareProvider->isLookupServerUploadEnabled()); $this->initialState->provideInitialState('federatedTrustedShareAutoAccept', $this->fedShareProvider->isFederatedTrustedShareAutoAccept()); - return new TemplateResponse('federatedfilesharing', 'settings-admin', [], ''); + \OCP\Util::addStyle(Application::APP_ID, 'settings-admin'); + \OCP\Util::addScript(Application::APP_ID, 'settings-admin'); + return new TemplateResponse(Application::APP_ID, 'settings-admin', renderAs: ''); } /** diff --git a/apps/federatedfilesharing/lib/Settings/Personal.php b/apps/federatedfilesharing/lib/Settings/Personal.php index 2889fb77c1f..fc401c404d4 100644 --- a/apps/federatedfilesharing/lib/Settings/Personal.php +++ b/apps/federatedfilesharing/lib/Settings/Personal.php @@ -8,6 +8,7 @@ declare(strict_types=1); */ namespace OCA\FederatedFileSharing\Settings; +use OCA\FederatedFileSharing\AppInfo\Application; use OCA\FederatedFileSharing\FederatedShareProvider; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; @@ -41,7 +42,9 @@ class Personal implements ISettings { $this->initialState->provideInitialState('cloudId', $cloudID); $this->initialState->provideInitialState('docUrlFederated', $this->urlGenerator->linkToDocs('user-sharing-federated')); - return new TemplateResponse('federatedfilesharing', 'settings-personal', [], TemplateResponse::RENDER_AS_BLANK); + \OCP\Util::addStyle(Application::APP_ID, 'settings-personal'); + \OCP\Util::addScript(Application::APP_ID, 'settings-personal'); + return new TemplateResponse(Application::APP_ID, 'settings-personal', renderAs: TemplateResponse::RENDER_AS_BLANK); } /** diff --git a/apps/federatedfilesharing/src/components/AdminSettings.vue b/apps/federatedfilesharing/src/components/AdminSettings.vue index fad255c87ec..a9546d276c7 100644 --- a/apps/federatedfilesharing/src/components/AdminSettings.vue +++ b/apps/federatedfilesharing/src/components/AdminSettings.vue @@ -2,38 +2,184 @@ - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors - SPDX-License-Identifier: AGPL-3.0-or-later --> + + + - - diff --git a/apps/federatedfilesharing/src/external.js b/apps/federatedfilesharing/src/init-files.js similarity index 95% rename from apps/federatedfilesharing/src/external.js rename to apps/federatedfilesharing/src/init-files.js index d42947f7880..4efcb2b699b 100644 --- a/apps/federatedfilesharing/src/external.js +++ b/apps/federatedfilesharing/src/init-files.js @@ -13,33 +13,6 @@ import { generateUrl } from '@nextcloud/router' import { showRemoteShareDialog } from './services/dialogService.ts' import logger from './services/logger.ts' -window.OCA.Sharing = window.OCA.Sharing ?? {} - -/** - * Shows "add external share" dialog. - * - * @param {object} share the share - * @param {string} share.remote remote server URL - * @param {string} share.owner owner name - * @param {string} share.name name of the shared folder - * @param {string} share.token authentication token - * @param {boolean} passwordProtected true if the share is password protected - * @param {Function} callback the callback - */ -window.OCA.Sharing.showAddExternalDialog = function(share, passwordProtected, callback) { - const owner = share.ownerDisplayName || share.owner - const name = share.name - - // Clean up the remote URL for display - const remote = share.remote - .replace(/^https?:\/\//, '') // remove http:// or https:// - .replace(/\/$/, '') // remove trailing slash - - showRemoteShareDialog(name, owner, remote, passwordProtected) - .then((password) => callback(true, { ...share, password })) - .catch(() => callback(false, share)) -} - window.addEventListener('DOMContentLoaded', () => { processIncomingShareFromUrl() @@ -118,7 +91,7 @@ function processIncomingShareFromUrl() { // clear hash, it is unlikely that it contain any extra parameters location.hash = '' params.passwordProtected = parseInt(params.protected, 10) === 1 - window.OCA.Sharing.showAddExternalDialog( + showAddExternalDialog( params, params.passwordProtected, callbackAddShare, @@ -133,7 +106,7 @@ async function processSharesToConfirm() { // check for new server-to-server shares which need to be approved const { data: shares } = await axios.get(generateUrl('/apps/files_sharing/api/externalShares')) for (let index = 0; index < shares.length; ++index) { - window.OCA.Sharing.showAddExternalDialog( + showAddExternalDialog( shares[index], false, function(result, share) { @@ -149,3 +122,28 @@ async function processSharesToConfirm() { ) } } + +/** + * Shows "add external share" dialog. + * + * @param {object} share the share + * @param {string} share.remote remote server URL + * @param {string} share.owner owner name + * @param {string} share.name name of the shared folder + * @param {string} share.token authentication token + * @param {boolean} passwordProtected true if the share is password protected + * @param {Function} callback the callback + */ +function showAddExternalDialog(share, passwordProtected, callback) { + const owner = share.ownerDisplayName || share.owner + const name = share.name + + // Clean up the remote URL for display + const remote = share.remote + .replace(/^https?:\/\//, '') // remove http:// or https:// + .replace(/\/$/, '') // remove trailing slash + + showRemoteShareDialog(name, owner, remote, passwordProtected) + .then((password) => callback(true, { ...share, password })) + .catch(() => callback(false, share)) +} diff --git a/apps/federatedfilesharing/src/main-personal.js b/apps/federatedfilesharing/src/main-personal.js deleted file mode 100644 index f4317ac4170..00000000000 --- a/apps/federatedfilesharing/src/main-personal.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -import { getCSPNonce } from '@nextcloud/auth' -import { translate as t } from '@nextcloud/l10n' -import Vue from 'vue' -import PersonalSettings from './components/PersonalSettings.vue' - -__webpack_nonce__ = getCSPNonce() - -Vue.mixin({ - methods: { - t, - }, -}) - -const PersonalSettingsView = Vue.extend(PersonalSettings) -new PersonalSettingsView().$mount('#vue-personal-federated') diff --git a/apps/federatedfilesharing/src/services/dialogService.spec.ts b/apps/federatedfilesharing/src/services/dialogService.spec.ts index 368dcd929f3..58dd44e37ca 100644 --- a/apps/federatedfilesharing/src/services/dialogService.spec.ts +++ b/apps/federatedfilesharing/src/services/dialogService.spec.ts @@ -47,7 +47,7 @@ describe('federatedfilesharing: dialog service', () => { } } - expect(await promise).toBe('') + await expect(promise).resolves.toBe('') }) it('rejects if cancelled', async () => { @@ -60,6 +60,6 @@ describe('federatedfilesharing: dialog service', () => { } } - expect(async () => await promise).rejects.toThrow() + await expect(promise).rejects.toThrow() }) }) diff --git a/apps/federatedfilesharing/src/services/dialogService.ts b/apps/federatedfilesharing/src/services/dialogService.ts index d02b2aaf310..807011583b6 100644 --- a/apps/federatedfilesharing/src/services/dialogService.ts +++ b/apps/federatedfilesharing/src/services/dialogService.ts @@ -14,23 +14,24 @@ import RemoteShareDialog from '../components/RemoteShareDialog.vue' * @param remote The remote address * @param passwordRequired True if the share is password protected */ -export function showRemoteShareDialog( +export async function showRemoteShareDialog( name: string, owner: string, remote: string, passwordRequired = false, ): Promise { - const { promise, reject, resolve } = Promise.withResolvers() - - spawnDialog(RemoteShareDialog, { name, owner, remote, passwordRequired }, (status, password) => { - if (passwordRequired && status) { - resolve(password as string) - } else if (status) { - resolve(undefined) - } else { - reject() - } + const [status, password] = await spawnDialog(RemoteShareDialog, { + name, + owner, + remote, + passwordRequired, }) - return promise + if (passwordRequired && status) { + return password as string + } else if (status) { + return + } else { + throw new Error('Dialog was cancelled') + } } diff --git a/apps/federatedfilesharing/src/main-admin.js b/apps/federatedfilesharing/src/settings-admin.ts similarity index 53% rename from apps/federatedfilesharing/src/main-admin.js rename to apps/federatedfilesharing/src/settings-admin.ts index 14cee69fb1a..9a2a1908dcf 100644 --- a/apps/federatedfilesharing/src/main-admin.js +++ b/apps/federatedfilesharing/src/settings-admin.ts @@ -3,23 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { getCSPNonce } from '@nextcloud/auth' import { loadState } from '@nextcloud/initial-state' -import { translate as t } from '@nextcloud/l10n' -import Vue from 'vue' +import { createApp } from 'vue' import AdminSettings from './components/AdminSettings.vue' -__webpack_nonce__ = getCSPNonce() - -Vue.mixin({ - methods: { - t, - }, -}) +import 'vite/modulepreload-polyfill' const internalOnly = loadState('federatedfilesharing', 'internalOnly', false) if (!internalOnly) { - const AdminSettingsView = Vue.extend(AdminSettings) - new AdminSettingsView().$mount('#vue-admin-federated') + const app = createApp(AdminSettings) + app.mount('#vue-admin-federated') } diff --git a/apps/federatedfilesharing/src/settings-personal.ts b/apps/federatedfilesharing/src/settings-personal.ts new file mode 100644 index 00000000000..74be7cab422 --- /dev/null +++ b/apps/federatedfilesharing/src/settings-personal.ts @@ -0,0 +1,12 @@ +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { createApp } from 'vue' +import PersonalSettings from './components/PersonalSettings.vue' + +import 'vite/modulepreload-polyfill' + +const app = createApp(PersonalSettings) +app.mount('#vue-personal-federated') diff --git a/apps/federatedfilesharing/templates/settings-admin.php b/apps/federatedfilesharing/templates/settings-admin.php index 786dcdf2165..641228524e6 100644 --- a/apps/federatedfilesharing/templates/settings-admin.php +++ b/apps/federatedfilesharing/templates/settings-admin.php @@ -3,8 +3,6 @@ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ - -\OCP\Util::addScript('federatedfilesharing', 'vue-settings-admin'); ?>
diff --git a/apps/federatedfilesharing/templates/settings-personal.php b/apps/federatedfilesharing/templates/settings-personal.php index dffd5712b7e..1e4bdaf9b5c 100644 --- a/apps/federatedfilesharing/templates/settings-personal.php +++ b/apps/federatedfilesharing/templates/settings-personal.php @@ -3,8 +3,6 @@ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ - -\OCP\Util::addScript('federatedfilesharing', 'vue-settings-personal'); ?>
diff --git a/build/frontend-legacy/webpack.modules.cjs b/build/frontend-legacy/webpack.modules.cjs index fb396aae90a..6309857c1ae 100644 --- a/build/frontend-legacy/webpack.modules.cjs +++ b/build/frontend-legacy/webpack.modules.cjs @@ -57,11 +57,6 @@ module.exports = { oauth2: { oauth2: path.join(__dirname, 'apps/oauth2/src', 'main.js'), }, - federatedfilesharing: { - external: path.join(__dirname, 'apps/federatedfilesharing/src', 'external.js'), - 'vue-settings-admin': path.join(__dirname, 'apps/federatedfilesharing/src', 'main-admin.js'), - 'vue-settings-personal': path.join(__dirname, 'apps/federatedfilesharing/src', 'main-personal.js'), - }, profile: { main: path.join(__dirname, 'apps/profile/src', 'main.ts'), }, diff --git a/build/frontend-legacy/apps/federatedfilesharing b/build/frontend/apps/federatedfilesharing similarity index 100% rename from build/frontend-legacy/apps/federatedfilesharing rename to build/frontend/apps/federatedfilesharing diff --git a/build/frontend/vite.config.mts b/build/frontend/vite.config.mts index fe3667df0cc..b86568f2b9d 100644 --- a/build/frontend/vite.config.mts +++ b/build/frontend/vite.config.mts @@ -12,6 +12,11 @@ const modules = { 'settings-admin-example-content': resolve(import.meta.dirname, 'apps/dav/src', 'settings-admin-example-content.ts'), 'settings-personal-availability': resolve(import.meta.dirname, 'apps/dav/src', 'settings-personal-availability.ts'), }, + federatedfilesharing: { + 'init-files': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'init-files.js'), + 'settings-admin': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'settings-admin.ts'), + 'settings-personal': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'settings-personal.ts'), + }, files_reminders: { init: resolve(import.meta.dirname, 'apps/files_reminders/src', 'files-init.ts'), }, diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index 712dc3bc3bb..10621144e8d 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -1209,11 +1209,6 @@ )]]> - - - - - diff --git a/tsconfig.json b/tsconfig.json index 86896bbbe36..4cda8f05a10 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "include": ["./apps/**/*.ts", "./apps/**/*.vue", "./core/**/*.ts", "./core/**/*.vue", "./*.d.ts"], "exclude": ["./**/*.cy.ts"], "compilerOptions": { - "lib": ["DOM", "ESNext"], + "lib": ["DOM", "DOM.Iterable", "DOM.AsyncIterable", "ESNext"], "outDir": "./dist/", "target": "ESNext", "module": "ESNext", diff --git a/vitest.config.ts b/vitest.config.ts index 70fa4ea84ac..1ce29c6770d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,5 +3,11 @@ * SPDX-License-Identifier: CC0-1.0 */ +import { defineConfig } from 'vitest/config' + // stub - for the moment see build/frontend/vitest.config.ts -export default {} +export default defineConfig({ + test: { + projects: ['build/frontend*'], + }, +})