Sửa chế độ chia sẻ privacy trực tiếp khi nhấn chuột phải lên scene
This commit is contained in:
+272
-6
@@ -10,11 +10,22 @@ let miniMapMarker = null;
|
||||
let systemSettings = { timezone: 'Asia/Ho_Chi_Minh', language: 'vi' };
|
||||
let returnToDashboardAfterEdit = false;
|
||||
let assetIdToDelete = null;
|
||||
let editMiniMap = null;
|
||||
let editMiniMapMarker = null;
|
||||
let currentEditingScene = null; // Lưu object scene đang sửa để quản lý chia sẻ
|
||||
let sharedUsersData = []; // [{id, username, email}]
|
||||
let sharedEmailsData = []; // [email]
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
try {
|
||||
console.log("--- Bắt đầu khởi tạo Frontend ---");
|
||||
|
||||
// 0. Kiểm tra tham số URL để truy cập trực tiếp
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const urlSceneId = urlParams.get('sceneId');
|
||||
const urlToken = urlParams.get('token');
|
||||
|
||||
// Ưu tiên nạp cấu hình hệ thống trước
|
||||
fetchSystemSettings();
|
||||
|
||||
@@ -26,8 +37,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Chạy tuần tự để tránh xung đột luồng xử lý
|
||||
checkAuthStatus(); // 2. Kiểm tra đăng nhập
|
||||
|
||||
// 3. Khôi phục cảnh đang xem nếu có (sau khi người dùng reload trang)
|
||||
restoreActiveScene();
|
||||
// 3. Xử lý logic vào thẳng Scene hoặc khôi phục trang
|
||||
if (urlSceneId) {
|
||||
console.log(`[Direct Access] Opening scene ${urlSceneId} from URL`);
|
||||
openScene(urlSceneId, urlToken ? 'shared' : null, urlToken);
|
||||
} else {
|
||||
restoreActiveScene();
|
||||
}
|
||||
|
||||
// Đảm bảo map đã sẵn sàng trước khi nạp data
|
||||
if (map) {
|
||||
@@ -662,16 +678,25 @@ async function handleEditDeleteScene(scene) {
|
||||
const modal = document.getElementById('action-choice-modal');
|
||||
const title = document.getElementById('action-modal-title');
|
||||
const editBtn = document.getElementById('btn-edit-action');
|
||||
const editPrivacyBtn = document.getElementById('btn-edit-privacy-action');
|
||||
const deleteBtn = document.getElementById('btn-delete-action');
|
||||
|
||||
title.innerText = `Scene: ${scene.title}`;
|
||||
modal.style.display = 'flex';
|
||||
|
||||
// Hành động Chỉnh sửa privacy
|
||||
editPrivacyBtn.onclick = () => {
|
||||
returnToDashboardAfterEdit = false;
|
||||
closeActionModal();
|
||||
// Mở modal metadata, false vì ảnh trên map luôn là ảnh mẹ (không phải child)
|
||||
openEditMetadataModal(scene, false);
|
||||
};
|
||||
|
||||
// Gán sự kiện cho nút Sửa
|
||||
editBtn.onclick = () => {
|
||||
returnToDashboardAfterEdit = false;
|
||||
closeActionModal();
|
||||
openEditSceneModal(scene);
|
||||
openEditMetadataModal(scene, false);
|
||||
};
|
||||
|
||||
// Gán sự kiện cho nút Xóa
|
||||
@@ -805,9 +830,22 @@ async function openScene(sceneId, privacy, shareToken, force = false, initialPit
|
||||
initPanoramaViewer(secureImageUrl, hotspots || [], sceneOwnerId, initialPitch, initialYaw);
|
||||
|
||||
} catch (error) {
|
||||
if (typeof closeViewer === 'function') closeViewer();
|
||||
|
||||
localStorage.removeItem('activeSceneId');
|
||||
localStorage.removeItem('activeScenePrivacy');
|
||||
localStorage.removeItem('activeSceneToken');
|
||||
|
||||
// Kiểm tra nếu đang truy cập qua link trực tiếp (URL có sceneId) mà gặp lỗi (do xóa token hoặc token không hợp lệ)
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.has('sceneId')) {
|
||||
alert("Bạn không có quyền truy cập hoặc liên kết chia sẻ đã hết hạn. Quay về bản đồ công cộng.");
|
||||
// Xóa toàn bộ tham số URL và tải lại trang để làm mới trạng thái (về trang chủ dành cho khách)
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
alert(error.message);
|
||||
}
|
||||
}
|
||||
@@ -1275,7 +1313,8 @@ async function loadMyAssets() {
|
||||
const editButton = document.createElement('button');
|
||||
editButton.className = 'edit-btn-small';
|
||||
editButton.innerText = 'Sửa Scene';
|
||||
editButton.addEventListener('click', () => openEditFromMedia(scene)); // Pass scene object directly
|
||||
const isChild = asset.parentScenes && asset.parentScenes.length > 0;
|
||||
editButton.addEventListener('click', () => openEditFromMedia(scene, isChild));
|
||||
card.querySelector('.media-actions').appendChild(editButton);
|
||||
}
|
||||
|
||||
@@ -1362,16 +1401,243 @@ window.closeSuccessModal = function(e) {
|
||||
modal.style.display = 'none';
|
||||
};
|
||||
|
||||
window.openEditFromMedia = function(scene) {
|
||||
window.openEditFromMedia = function(scene, isChild = false) {
|
||||
if (!scene || !scene._id) {
|
||||
alert("Không thể chỉnh sửa: Ảnh này không được gắn với một Scene hợp lệ.");
|
||||
return;
|
||||
}
|
||||
returnToDashboardAfterEdit = true;
|
||||
closeDashboard();
|
||||
openEditSceneModal(scene);
|
||||
openEditMetadataModal(scene, isChild);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mở Modal sửa thông tin Metadata chuyên biệt
|
||||
*/
|
||||
window.openEditMetadataModal = function(scene, isChild = false) {
|
||||
currentEditingScene = scene; // Lưu lại để dùng cho chia sẻ
|
||||
// Load dữ liệu chia sẻ hiện tại
|
||||
sharedUsersData = scene.sharedWith || [];
|
||||
sharedEmailsData = scene.sharedEmails || [];
|
||||
|
||||
document.getElementById('edit-modal-scene-id').value = scene._id;
|
||||
document.getElementById('edit-modal-title').value = scene.name || scene.title || '';
|
||||
document.getElementById('edit-modal-description').value = scene.description || '';
|
||||
|
||||
const lat = scene.gps?.lat || scene.lat;
|
||||
const lng = scene.gps?.lng || scene.lng;
|
||||
document.getElementById('edit-modal-lat').value = lat;
|
||||
document.getElementById('edit-modal-lng').value = lng;
|
||||
|
||||
// Xử lý logic Privacy cho Cảnh con
|
||||
const privacySelect = document.getElementById('edit-modal-privacy');
|
||||
const childInfo = document.getElementById('edit-child-privacy-info');
|
||||
|
||||
if (isChild) {
|
||||
privacySelect.value = scene.privacy;
|
||||
privacySelect.disabled = true;
|
||||
childInfo.style.display = 'block';
|
||||
} else {
|
||||
privacySelect.value = scene.privacy;
|
||||
privacySelect.disabled = false;
|
||||
childInfo.style.display = 'none';
|
||||
}
|
||||
|
||||
handleEditPrivacyChange(); // Cập nhật hiển thị nút bánh răng
|
||||
|
||||
document.getElementById('edit-scene-metadata-modal').style.display = 'flex';
|
||||
|
||||
// Khởi tạo Mini Map tại vị trí hiện tại của Scene
|
||||
setTimeout(() => initEditSceneMiniMap(lat, lng), 100);
|
||||
};
|
||||
|
||||
function closeEditMetadataModal() {
|
||||
document.getElementById('edit-scene-metadata-modal').style.display = 'none';
|
||||
if (returnToDashboardAfterEdit) {
|
||||
returnToDashboardAfterEdit = false;
|
||||
openDashboard();
|
||||
openDashboardTab('media-library');
|
||||
}
|
||||
}
|
||||
|
||||
function initEditSceneMiniMap(lat, lng) {
|
||||
if (editMiniMap) {
|
||||
editMiniMap.setView([lat, lng], 16);
|
||||
if (editMiniMapMarker) editMiniMapMarker.setLatLng([lat, lng]);
|
||||
editMiniMap.invalidateSize();
|
||||
return;
|
||||
}
|
||||
|
||||
editMiniMap = L.map('edit-mini-map').setView([lat, lng], 16);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(editMiniMap);
|
||||
|
||||
editMiniMapMarker = L.marker([lat, lng], { draggable: true }).addTo(editMiniMap);
|
||||
|
||||
editMiniMap.on('click', (e) => {
|
||||
const { lat, lng } = e.latlng;
|
||||
editMiniMapMarker.setLatLng([lat, lng]);
|
||||
document.getElementById('edit-modal-lat').value = lat.toFixed(6);
|
||||
document.getElementById('edit-modal-lng').value = lng.toFixed(6);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Xử lý khi thay đổi Dropdown Privacy trong Modal sửa
|
||||
*/
|
||||
window.handleEditPrivacyChange = function() {
|
||||
const privacy = document.getElementById('edit-modal-privacy').value;
|
||||
const settingsBtn = document.getElementById('btn-edit-privacy-settings');
|
||||
const isChild = document.getElementById('edit-modal-privacy').disabled;
|
||||
|
||||
if (!isChild && (privacy === 'member' || privacy === 'shared')) {
|
||||
settingsBtn.style.display = 'block';
|
||||
} else {
|
||||
settingsBtn.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mở modal cài đặt chi tiết dựa trên loại Privacy
|
||||
*/
|
||||
window.openPrivacySettingsModal = function() {
|
||||
const privacy = document.getElementById('edit-modal-privacy').value;
|
||||
if (privacy === 'member') {
|
||||
renderSharedList();
|
||||
document.getElementById('share-member-modal').style.display = 'flex';
|
||||
} else if (privacy === 'shared') {
|
||||
const baseUrl = window.location.origin + window.location.pathname;
|
||||
const token = currentEditingScene.shareToken || 'đang_tạo_mới...';
|
||||
document.getElementById('shared-link-input').value = `${baseUrl}?sceneId=${currentEditingScene._id}&token=${token}`;
|
||||
document.getElementById('share-link-modal').style.display = 'flex';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tìm kiếm người dùng để chia sẻ
|
||||
*/
|
||||
window.searchUsersToShare = async function(query) {
|
||||
const dropdown = document.getElementById('search-results-dropdown');
|
||||
if (!query || query.length < 2) {
|
||||
dropdown.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('jwt');
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/users/search?q=${encodeURIComponent(query)}`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
const users = await res.json();
|
||||
|
||||
dropdown.innerHTML = '';
|
||||
if (users.length > 0) {
|
||||
users.forEach(user => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'search-item';
|
||||
item.innerText = `${user.username} (${user.email})`;
|
||||
item.onclick = () => addMemberToShare(user);
|
||||
dropdown.appendChild(item);
|
||||
});
|
||||
dropdown.style.display = 'block';
|
||||
} else {
|
||||
// Nếu không tìm thấy user, cho phép thêm email thủ công
|
||||
if (query.includes('@')) {
|
||||
dropdown.innerHTML = `<div class="search-item" onclick="addEmailToShare('${query}')">Thêm email: ${query}</div>`;
|
||||
dropdown.style.display = 'block';
|
||||
} else {
|
||||
dropdown.style.display = 'none';
|
||||
}
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
};
|
||||
|
||||
function addMemberToShare(user) {
|
||||
if (!sharedUsersData.some(u => (u._id || u) === user._id)) {
|
||||
sharedUsersData.push(user);
|
||||
renderSharedList();
|
||||
}
|
||||
document.getElementById('share-user-search').value = '';
|
||||
document.getElementById('search-results-dropdown').style.display = 'none';
|
||||
}
|
||||
|
||||
window.addEmailToShare = function(email) {
|
||||
if (!sharedEmailsData.includes(email)) {
|
||||
sharedEmailsData.push(email);
|
||||
renderSharedList();
|
||||
}
|
||||
document.getElementById('share-user-search').value = '';
|
||||
document.getElementById('search-results-dropdown').style.display = 'none';
|
||||
};
|
||||
|
||||
function renderSharedList() {
|
||||
const list = document.getElementById('current-shared-list');
|
||||
list.innerHTML = '';
|
||||
|
||||
sharedUsersData.forEach(user => {
|
||||
const name = user.username || 'User';
|
||||
list.innerHTML += `<div class="share-list-item">👤 ${name} <span class="remove-share-btn" onclick="removeShared('user', '${user._id || user}')">×</span></div>`;
|
||||
});
|
||||
|
||||
sharedEmailsData.forEach(email => {
|
||||
list.innerHTML += `<div class="share-list-item">📧 ${email} <span class="remove-share-btn" onclick="removeShared('email', '${email}')">×</span></div>`;
|
||||
});
|
||||
}
|
||||
|
||||
window.removeShared = function(type, id) {
|
||||
if (type === 'user') sharedUsersData = sharedUsersData.filter(u => (u._id || u) !== id);
|
||||
else sharedEmailsData = sharedEmailsData.filter(e => e !== id);
|
||||
renderSharedList();
|
||||
};
|
||||
|
||||
window.closeShareMemberModal = () => document.getElementById('share-member-modal').style.display = 'none';
|
||||
window.closeShareLinkModal = () => document.getElementById('share-link-modal').style.display = 'none';
|
||||
|
||||
/**
|
||||
* Copy link chia sẻ và đóng modal
|
||||
*/
|
||||
window.copySharedLink = function() {
|
||||
const linkInput = document.getElementById('shared-link-input');
|
||||
linkInput.select();
|
||||
navigator.clipboard.writeText(linkInput.value).then(() => {
|
||||
showSuccessModal("Đã sao chép liên kết vào bộ nhớ!");
|
||||
closeShareLinkModal();
|
||||
});
|
||||
};
|
||||
|
||||
async function submitEditScene(e) {
|
||||
e.preventDefault();
|
||||
const id = document.getElementById('edit-modal-scene-id').value;
|
||||
const token = localStorage.getItem('jwt');
|
||||
|
||||
// Sử dụng FormData vì API Backend hiện tại đang dùng Multer
|
||||
const formData = new FormData();
|
||||
formData.append('title', document.getElementById('edit-modal-title').value);
|
||||
formData.append('description', document.getElementById('edit-modal-description').value);
|
||||
formData.append('lat', document.getElementById('edit-modal-lat').value);
|
||||
formData.append('lng', document.getElementById('edit-modal-lng').value);
|
||||
formData.append('privacy', document.getElementById('edit-modal-privacy').value);
|
||||
formData.append('shareExpireDays', document.getElementById('share-link-expire').value);
|
||||
// Đính kèm dữ liệu chia sẻ nâng cao
|
||||
formData.append('sharedWithUsers', JSON.stringify(sharedUsersData.map(u => u._id || u)));
|
||||
formData.append('sharedEmails', JSON.stringify(sharedEmailsData));
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/scenes/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Authorization': `Bearer ${token}` },
|
||||
body: formData
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.message);
|
||||
|
||||
showSuccessModal("Đã cập nhật thông tin cảnh thành công!");
|
||||
closeEditMetadataModal();
|
||||
loadScenes();
|
||||
} catch (err) {
|
||||
alert("Lỗi cập nhật: " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a specific tab within the dashboard.
|
||||
* @param {string} tabName - The ID of the tab pane to open (e.g., 'profile', 'my-scenes').
|
||||
|
||||
Reference in New Issue
Block a user