fix: share dialog not opening + connect to backend API
- Fix showShareDialog: add try-catch, null checks, prevent textContent from destroying header icon (use span child instead) - Capture file/folder target before closeContextMenu to prevent race - createSharedLink now calls real backend POST /api/shares instead of localStorage-only mock (still caches locally for offline compat) - Fix share_handler.rs: use OptionalAuthUser instead of AuthUser to prevent 401 when auth is disabled (same pattern as delete/trash) - Add null-safety to closeShareDialog - Reset new-share-section on dialog open
This commit is contained in:
Generated
+1
-1
@@ -1686,7 +1686,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "oxicloud"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
ports::share_ports::ShareUseCase
|
||||
},
|
||||
common::errors::ErrorKind,
|
||||
interfaces::middleware::auth::AuthUser,
|
||||
interfaces::middleware::auth::OptionalAuthUser,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -32,10 +32,10 @@ pub struct VerifyPasswordRequest {
|
||||
/// Create a new shared link
|
||||
pub async fn create_shared_link(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
auth_user: AuthUser,
|
||||
auth_user: OptionalAuthUser,
|
||||
Json(dto): Json<CreateShareDto>,
|
||||
) -> impl IntoResponse {
|
||||
let user_id = &auth_user.id;
|
||||
let user_id = auth_user.0.map(|u| u.id).unwrap_or_else(|| "anonymous".to_string());
|
||||
match share_use_case.create_shared_link(&user_id, dto).await {
|
||||
Ok(share) => (StatusCode::CREATED, Json(share)).into_response(),
|
||||
Err(err) => {
|
||||
@@ -69,10 +69,10 @@ pub async fn get_shared_link(
|
||||
/// Get all shared links created by the current user
|
||||
pub async fn get_user_shares(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
auth_user: AuthUser,
|
||||
auth_user: OptionalAuthUser,
|
||||
Query(query): Query<GetSharesQuery>,
|
||||
) -> impl IntoResponse {
|
||||
let user_id = &auth_user.id;
|
||||
let user_id = auth_user.0.map(|u| u.id).unwrap_or_else(|| "anonymous".to_string());
|
||||
let page = query.page.unwrap_or(1);
|
||||
let per_page = query.per_page.unwrap_or(20);
|
||||
|
||||
|
||||
+104
-45
@@ -62,8 +62,9 @@ const contextMenus = {
|
||||
});
|
||||
|
||||
document.getElementById('share-folder-option').addEventListener('click', () => {
|
||||
if (window.app.contextMenuTargetFolder) {
|
||||
this.showShareDialog(window.app.contextMenuTargetFolder, 'folder');
|
||||
const folder = window.app.contextMenuTargetFolder;
|
||||
if (folder) {
|
||||
this.showShareDialog(folder, 'folder');
|
||||
}
|
||||
window.ui.closeContextMenu();
|
||||
});
|
||||
@@ -165,8 +166,9 @@ const contextMenus = {
|
||||
});
|
||||
|
||||
document.getElementById('share-file-option').addEventListener('click', () => {
|
||||
if (window.app.contextMenuTargetFile) {
|
||||
this.showShareDialog(window.app.contextMenuTargetFile, 'file');
|
||||
const file = window.app.contextMenuTargetFile;
|
||||
if (file) {
|
||||
this.showShareDialog(file, 'file');
|
||||
}
|
||||
window.ui.closeFileContextMenu();
|
||||
});
|
||||
@@ -433,23 +435,42 @@ const contextMenus = {
|
||||
* @param {string} itemType - 'file' or 'folder'
|
||||
*/
|
||||
showShareDialog(item, itemType) {
|
||||
// Update dialog title based on item type
|
||||
const dialogHeader = document.getElementById('share-dialog').querySelector('.share-dialog-header');
|
||||
try {
|
||||
const shareDialog = document.getElementById('share-dialog');
|
||||
if (!shareDialog) {
|
||||
console.error('Share dialog element not found in DOM');
|
||||
window.ui.showNotification('Error', 'Share dialog not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update dialog title — use the <span> inside header to preserve <i> icon
|
||||
const dialogHeader = shareDialog.querySelector('.share-dialog-header');
|
||||
if (dialogHeader) {
|
||||
const headerSpan = dialogHeader.querySelector('span');
|
||||
const titleText = itemType === 'file' ?
|
||||
(window.i18n ? window.i18n.t('dialogs.share_file') : 'Share file') :
|
||||
(window.i18n ? window.i18n.t('dialogs.share_folder') : 'Share folder');
|
||||
if (headerSpan) {
|
||||
headerSpan.textContent = titleText;
|
||||
} else {
|
||||
dialogHeader.textContent = titleText;
|
||||
}
|
||||
}
|
||||
|
||||
const itemName = document.getElementById('shared-item-name');
|
||||
|
||||
// Update dialog content
|
||||
dialogHeader.textContent = itemType === 'file' ?
|
||||
(window.i18n ? window.i18n.t('dialogs.share_file') : 'Share file') :
|
||||
(window.i18n ? window.i18n.t('dialogs.share_folder') : 'Share folder');
|
||||
|
||||
itemName.textContent = item.name;
|
||||
if (itemName) itemName.textContent = item.name;
|
||||
|
||||
// Reset form
|
||||
document.getElementById('share-password').value = '';
|
||||
document.getElementById('share-expiration').value = '';
|
||||
document.getElementById('share-permission-read').checked = true;
|
||||
document.getElementById('share-permission-write').checked = false;
|
||||
document.getElementById('share-permission-reshare').checked = false;
|
||||
const pwField = document.getElementById('share-password');
|
||||
const expField = document.getElementById('share-expiration');
|
||||
if (pwField) pwField.value = '';
|
||||
if (expField) expField.value = '';
|
||||
const permRead = document.getElementById('share-permission-read');
|
||||
const permWrite = document.getElementById('share-permission-write');
|
||||
const permReshare = document.getElementById('share-permission-reshare');
|
||||
if (permRead) permRead.checked = true;
|
||||
if (permWrite) permWrite.checked = false;
|
||||
if (permReshare) permReshare.checked = false;
|
||||
|
||||
// Store the current item and type for use when creating the share
|
||||
window.app.shareDialogItem = item;
|
||||
@@ -526,14 +547,23 @@ const contextMenus = {
|
||||
document.getElementById('existing-shares-section').style.display = 'none';
|
||||
}
|
||||
|
||||
// Hide new-share section from previous use
|
||||
const newShareSection = document.getElementById('new-share-section');
|
||||
if (newShareSection) newShareSection.style.display = 'none';
|
||||
|
||||
// Show dialog
|
||||
document.getElementById('share-dialog').style.display = 'flex';
|
||||
shareDialog.style.display = 'flex';
|
||||
console.log('Share dialog opened for', itemType, item.name);
|
||||
} catch (error) {
|
||||
console.error('Error opening share dialog:', error);
|
||||
window.ui.showNotification('Error', 'Could not open share dialog');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a shared link with the configured options
|
||||
*/
|
||||
createSharedLink() {
|
||||
async createSharedLink() {
|
||||
if (!window.app.shareDialogItem || !window.app.shareDialogItemType) {
|
||||
window.ui.showNotification('Error', 'Could not share the item');
|
||||
return;
|
||||
@@ -546,46 +576,74 @@ const contextMenus = {
|
||||
const permissionWrite = document.getElementById('share-permission-write').checked;
|
||||
const permissionReshare = document.getElementById('share-permission-reshare').checked;
|
||||
|
||||
// Prepare options
|
||||
const options = {
|
||||
const item = window.app.shareDialogItem;
|
||||
const itemType = window.app.shareDialogItemType;
|
||||
|
||||
// Build DTO for backend API
|
||||
const createDto = {
|
||||
item_id: item.id,
|
||||
item_type: itemType,
|
||||
password: password || null,
|
||||
expirationDate: expirationDate || null,
|
||||
expires_at: expirationDate ? Math.floor(new Date(expirationDate).getTime() / 1000) : null,
|
||||
permissions: {
|
||||
read: permissionRead,
|
||||
write: permissionWrite,
|
||||
reshare: permissionReshare
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const item = window.app.shareDialogItem;
|
||||
const itemType = window.app.shareDialogItemType;
|
||||
|
||||
// Create share
|
||||
const shareInfo = window.fileSharing.generateSharedLink(
|
||||
item.id,
|
||||
itemType,
|
||||
options
|
||||
);
|
||||
const token = localStorage.getItem('oxicloud_token');
|
||||
const headers = { 'Content-Type': 'application/json' };
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
|
||||
const response = await fetch('/api/shares', {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(createDto)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errBody = await response.json().catch(() => ({}));
|
||||
throw new Error(errBody.error || `Server error ${response.status}`);
|
||||
}
|
||||
|
||||
const shareInfo = await response.json();
|
||||
|
||||
// Also save to localStorage for offline / shared-view compatibility
|
||||
window.fileSharing.saveSharedLink({
|
||||
id: shareInfo.id,
|
||||
type: shareInfo.item_type,
|
||||
itemId: shareInfo.item_id,
|
||||
url: shareInfo.url,
|
||||
token: shareInfo.token,
|
||||
password_protected: shareInfo.has_password,
|
||||
expires_at: shareInfo.expires_at ? new Date(shareInfo.expires_at * 1000).toISOString() : null,
|
||||
permissions: shareInfo.permissions,
|
||||
created_at: new Date(shareInfo.created_at * 1000).toISOString(),
|
||||
access_count: shareInfo.access_count || 0,
|
||||
name: item.name,
|
||||
dateShared: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Update UI with new share
|
||||
const shareUrl = document.getElementById('generated-share-url');
|
||||
shareUrl.value = shareInfo.url;
|
||||
document.getElementById('new-share-section').style.display = 'block';
|
||||
|
||||
// Focus and select for easy copying
|
||||
shareUrl.focus();
|
||||
shareUrl.select();
|
||||
if (shareUrl) {
|
||||
shareUrl.value = shareInfo.url;
|
||||
document.getElementById('new-share-section').style.display = 'block';
|
||||
shareUrl.focus();
|
||||
shareUrl.select();
|
||||
}
|
||||
|
||||
// Show success message
|
||||
window.ui.showNotification('Link created', 'Shared link created successfully');
|
||||
|
||||
// Reload existing shares
|
||||
this.showShareDialog(item, itemType);
|
||||
window.ui.showNotification(
|
||||
window.i18n ? window.i18n.t('notifications.link_created') : 'Link created',
|
||||
window.i18n ? window.i18n.t('notifications.share_success') : 'Shared link created successfully'
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error creating shared link:', error);
|
||||
window.ui.showNotification('Error', 'Could not create shared link');
|
||||
window.ui.showNotification('Error', error.message || 'Could not create shared link');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -639,7 +697,8 @@ const contextMenus = {
|
||||
* Close share dialog
|
||||
*/
|
||||
closeShareDialog() {
|
||||
document.getElementById('share-dialog').style.display = 'none';
|
||||
const dialog = document.getElementById('share-dialog');
|
||||
if (dialog) dialog.style.display = 'none';
|
||||
window.app.shareDialogItem = null;
|
||||
window.app.shareDialogItemType = null;
|
||||
},
|
||||
|
||||
+2
-2
@@ -204,8 +204,8 @@ const ui = {
|
||||
contextMenus.closeShareDialog();
|
||||
});
|
||||
|
||||
document.getElementById('share-confirm-btn').addEventListener('click', () => {
|
||||
contextMenus.createSharedLink();
|
||||
document.getElementById('share-confirm-btn').addEventListener('click', async () => {
|
||||
await contextMenus.createSharedLink();
|
||||
});
|
||||
|
||||
document.getElementById('copy-share-btn').addEventListener('click', async () => {
|
||||
|
||||
Reference in New Issue
Block a user