mattermost-mobile/app/actions/websocket/files.ts
Felipe Martin 9793383832
feat: support fillewillbedownloaded and sendtoastmessage pluginapi (#9416)
* Handle FileWillBeDownloaded plugin hook rejections

- Add WebSocket handler for file_download_rejected events
- Show rejected files as file cards instead of broken thumbnails
- Display plugin rejection message in snackbar
- Store rejection reason in EphemeralStore for later retrieval
- Re-render file components when rejection events arrive
- Remove blurred preview to prevent visual blink on rejection

* chore: lint

* chore: lint and test

* fix: removed event from useEffect
2026-02-16 17:10:25 +01:00

79 lines
3 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import EphemeralStore from '@store/ephemeral_store';
import {logDebug} from '@utils/log';
import {showSnackBar} from '@utils/snack_bar';
export async function handleFileDownloadRejected(serverUrl: string, msg: WebSocketMessage) {
try {
const data = msg.data as FileDownloadRejectedEvent;
logDebug('[handleFileDownloadRejected] Received event data:', JSON.stringify(data));
const {file_id: fileId, rejection_reason: rejectionReason, download_type: downloadType} = data;
if (!fileId) {
logDebug('[handleFileDownloadRejected] No file_id in event, skipping');
return;
}
// Track this file as rejected in ephemeral store (with the rejection reason)
EphemeralStore.addRejectedFile(fileId, rejectionReason);
// Handle different download types with different UX:
// - Thumbnail: Background load, silent failure (no notification)
// - Preview: Could be auto-load or user click, only notify if seems user-initiated
// - File: User explicitly clicked download, always notify
// - Public: User requested public link, always notify
if (downloadType === 'thumbnail') {
// Thumbnails are loaded automatically in the background
// Don't show notification to avoid annoying users
return;
}
if (downloadType === 'preview') {
// Preview could be:
// a) Auto-loaded in SingleImageView in channel list (background)
// b) User opened gallery/preview modal
// For mobile, we can't easily distinguish, so we'll show notification
// but only if rejection reason looks user-facing
// Fall through to show snackbar
}
logDebug('[handleFileDownloadRejected] Showing snackbar with rejection reason:', rejectionReason);
// Show snackbar with the plugin's rejection message directly
// Only pass customMessage if there's actually a message to show
showSnackBar({
barType: SNACK_BAR_TYPE.FILE_DOWNLOAD_REJECTED,
customMessage: rejectionReason || undefined,
type: 'error',
});
} catch (error) {
logDebug('[handleFileDownloadRejected] Error handling event:', error);
// Silently fail - don't crash the app for file rejection events
}
}
export async function handleShowToast(serverUrl: string, msg: WebSocketMessage) {
try {
const {message} = msg.data;
if (!message) {
return;
}
// Display generic toast from plugin using dedicated snackbar type
showSnackBar({
barType: SNACK_BAR_TYPE.PLUGIN_TOAST,
customMessage: message,
type: 'default',
});
} catch (error) {
// Silently fail - don't crash the app for toast events
}
}