Skip to content

Commit

Permalink
Merge pull request #4585 from nextcloud/feat/notify_push
Browse files Browse the repository at this point in the history
feat: Use notify push for sync messages during editing
  • Loading branch information
juliushaertl authored Jul 19, 2024
2 parents ec8bdd6 + f468541 commit 5ac996e
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 3 deletions.
26 changes: 25 additions & 1 deletion lib/Service/ApiService.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Exception;
use InvalidArgumentException;
use OCA\Files_Sharing\SharedStorage;
use OCA\NotifyPush\Queue\IQueue;
use OCA\Text\AppInfo\Application;
use OCA\Text\Db\Document;
use OCA\Text\Db\Session;
Expand All @@ -32,15 +33,16 @@
use Psr\Log\LoggerInterface;

class ApiService {

public function __construct(
private IRequest $request,
private ConfigService $configService,
private SessionService $sessionService,
private DocumentService $documentService,
private EncodingService $encodingService,
private LoggerInterface $logger,
private IL10N $l10n,
private ?string $userId,
private ?IQueue $queue,
) {
}

Expand Down Expand Up @@ -181,6 +183,7 @@ public function push(Session $session, Document $document, int $version, array $
}
try {
$result = $this->documentService->addStep($document, $session, $steps, $version, $token);
$this->addToPushQueue($document, [$awareness, ...array_values($steps)]);
} catch (InvalidArgumentException $e) {
return new DataResponse(['error' => $e->getMessage()], Http::STATUS_UNPROCESSABLE_ENTITY);
} catch (DoesNotExistException|NotPermittedException) {
Expand All @@ -190,6 +193,27 @@ public function push(Session $session, Document $document, int $version, array $
return new DataResponse($result);
}

private function addToPushQueue(Document $document, array $steps): void {
if ($this->queue === null || !$this->configService->isNotifyPushSyncEnabled()) {
return;
}

$sessions = $this->sessionService->getActiveSessions($document->getId());
$userIds = array_values(array_filter(array_unique(
array_map(fn ($session): ?string => $session['userId'], $sessions)
)));
foreach ($userIds as $userId) {
$this->queue->push('notify_custom', [
'user' => $userId,
'message' => 'text_steps',
'body' => [
'documentId' => $document->getId(),
'steps' => $steps,
],
]);
}
}

public function sync(Session $session, Document $document, int $version = 0, ?string $shareToken = null): DataResponse {
$documentId = $session->getDocumentId();
$result = [];
Expand Down
5 changes: 5 additions & 0 deletions lib/Service/ConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ public function isRichWorkspaceEnabledForUser(?string $userId): bool {
}
return $this->config->getUserValue($userId, Application::APP_NAME, 'workspace_enabled', '1') === '1';
}

public function isNotifyPushSyncEnabled(): bool {
return $this->appConfig->getValueBool(Application::APP_NAME, 'notify_push');

}
}
5 changes: 5 additions & 0 deletions lib/Service/InitialStateProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public function provideState(): void {
];
}, $this->textProcessingManager->getAvailableTaskTypes()),
);

$this->initialState->provideInitialState(
'notify_push',
$this->configService->isNotifyPushSyncEnabled(),
);
}

public function provideFileId(int $fileId): void {
Expand Down
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/logger": "^3.0.2",
"@nextcloud/moment": "^1.3.1",
"@nextcloud/notify_push": "^1.3.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.14.0",
"@quartzy/markdown-it-mentions": "^0.2.0",
Expand Down
22 changes: 22 additions & 0 deletions src/services/NotifyService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import mitt from 'mitt'
import { listen } from '@nextcloud/notify_push'
import { loadState } from '@nextcloud/initial-state'

if (!window._nc_text_notify) {
const isPushEnabled = loadState('text', 'notify_push', false)
const useNotifyPush = isPushEnabled
? listen('text_steps', (messageType, messageBody) => {
window._nc_text_notify?.emit('notify_push', { messageType, messageBody })
})
: undefined
window._nc_text_notify = useNotifyPush ? mitt() : null
}

export default () => {
return window._nc_text_notify
}
27 changes: 25 additions & 2 deletions src/services/PollingBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { logger } from '../helpers/logger.js'
import { SyncService, ERROR_TYPE } from './SyncService.js'
import { Connection } from './SessionApi.js'
import getNotifyBus from './NotifyService.js'

/**
* Minimum inverval to refetch the document changes
Expand Down Expand Up @@ -39,7 +40,9 @@ const FETCH_INTERVAL_READ_ONLY = 30000
*
* @type {number} time in ms
*/
const FETCH_INTERVAL_INVISIBLE = 60000
const FETCH_INTERVAL_INVISIBLE = 30000

const FETCH_INTERVAL_NOTIFY = 30000

/* Maximum number of retries for fetching before emitting a connection error */
const MAX_RETRY_FETCH_COUNT = 5
Expand All @@ -62,6 +65,7 @@ class PollingBackend {
#fetchRetryCounter
#pollActive
#initialLoadingFinished
#notifyPushBus

constructor(syncService, connection) {
this.#syncService = syncService
Expand All @@ -79,6 +83,7 @@ class PollingBackend {
this.#initialLoadingFinished = false
this.fetcher = setInterval(this._fetchSteps.bind(this), 50)
document.addEventListener('visibilitychange', this.visibilitychange.bind(this))
this.#notifyPushBus = getNotifyBus()
}

/**
Expand Down Expand Up @@ -110,6 +115,13 @@ class PollingBackend {
this.#pollActive = false
}

handleNotifyPush({ messageType, messageBody }) {
if (messageBody.documentId !== this.#connection.document.id) {
return
}
this._handleResponse({ data: messageBody.response })
}

_handleResponse({ data }) {
const { document, sessions } = data
this.#fetchRetryCounter = 0
Expand Down Expand Up @@ -189,15 +201,26 @@ class PollingBackend {
}

resetRefetchTimer() {
if (this.#notifyPushBus && this.#initialLoadingFinished) {
this.#fetchInterval = FETCH_INTERVAL_NOTIFY
return
}
this.#fetchInterval = FETCH_INTERVAL

}

increaseRefetchTimer() {
if (this.#notifyPushBus && this.#initialLoadingFinished) {
this.#fetchInterval = FETCH_INTERVAL_NOTIFY
return
}
this.#fetchInterval = Math.min(this.#fetchInterval * 2, FETCH_INTERVAL_MAX)
}

maximumRefetchTimer() {
if (this.#notifyPushBus && this.#initialLoadingFinished) {
this.#fetchInterval = FETCH_INTERVAL_NOTIFY
return
}
this.#fetchInterval = FETCH_INTERVAL_SINGLE_EDITOR
}

Expand Down
16 changes: 16 additions & 0 deletions src/services/WebSocketPolyfill.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { logger } from '../helpers/logger.js'
import { decodeArrayBuffer } from '../helpers/base64.ts'
import { getSteps, getAwareness } from '../helpers/yjs.js'
import getNotifyBus from './NotifyService.js'

/**
*
Expand All @@ -26,8 +27,11 @@ export default function initWebSocketPolyfill(syncService, fileId, initialSessio
onclose
onopen
#handlers
#notifyPushBus

constructor(url) {
this.#notifyPushBus = getNotifyBus()
this.#notifyPushBus?.on('notify_push', this.#onNotifyPush.bind(this))
this.url = url
logger.debug('WebSocketPolyfill#constructor', { url, fileId, initialSession })
this.#registerHandlers({
Expand Down Expand Up @@ -91,9 +95,21 @@ export default function initWebSocketPolyfill(syncService, fileId, initialSessio
Object.entries(this.#handlers)
.forEach(([key, value]) => syncService.off(key, value))
this.#handlers = []

this.#notifyPushBus?.off('notify_push', this.#onNotifyPush.bind(this))
this.onclose()
logger.debug('Websocket closed')
}

#onNotifyPush({ messageType, messageBody }) {
if (messageBody.documentId !== fileId) {
return
}
messageBody.steps.forEach(step => {
const data = decodeArrayBuffer(step)
this.onmessage({ data })
})
}

}
}
12 changes: 12 additions & 0 deletions tests/stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,15 @@ abstract public function setWantsNotification(bool $wantsNotification): void;
abstract public function setNotificationTarget(?string $notificationTarget): void;
}
}


namespace OCA\NotifyPush\Queue {
interface IQueue {
/**
* @param string $channel
* @param mixed $message
* @return void
*/
public function push(string $channel, $message);
}
}

0 comments on commit 5ac996e

Please sign in to comment.