feat(ui): add share modal and users
- fix(external users): fix app starting for external users
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import { getCsrfHeaders } from '../core/csrf.js';
|
||||
import { loadFiles } from './filesView.js';
|
||||
import { updateStorageUsageDisplay } from './main.js';
|
||||
import { app } from './state.js';
|
||||
import { ui } from './ui.js';
|
||||
@@ -40,6 +39,7 @@ async function refreshUserData() {
|
||||
console.log('Storage from server: used=', userData.storage_used_bytes, 'quota=', userData.storage_quota_bytes);
|
||||
|
||||
localStorage.setItem(USER_DATA_KEY, JSON.stringify(userData));
|
||||
app.isExternalUser = !!userData.is_external;
|
||||
updateStorageUsageDisplay(userData);
|
||||
return userData;
|
||||
} catch (error) {
|
||||
@@ -93,8 +93,15 @@ async function checkAuthentication() {
|
||||
// Check session validity by calling /api/auth/me (cookie auto-sent)
|
||||
console.log('Checking session via /api/auth/me...');
|
||||
|
||||
/** @type {User} */
|
||||
/** @type {User} */
|
||||
const userData = JSON.parse(localStorage.getItem(USER_DATA_KEY) || '{}');
|
||||
// Restore the external-user flag eagerly from cache so the
|
||||
// resolveHomeFolder short-circuit fires before the /api/auth/me
|
||||
// refresh completes. The parse may produce a sparse object on
|
||||
// first load — `is_external` defaulting to falsy is correct
|
||||
// for the internal-user-by-default contract.
|
||||
app.isExternalUser = !!userData.is_external;
|
||||
if (userData.username) {
|
||||
// We have cached user data — render immediately, refresh in background
|
||||
updateUserMenuData();
|
||||
@@ -134,14 +141,22 @@ async function checkAuthentication() {
|
||||
await resolveHomeFolder();
|
||||
window.dispatchEvent(new CustomEvent('authenticationDone'));
|
||||
} else {
|
||||
// No cached user data — must verify session from server
|
||||
// No cached user data — must verify session from server.
|
||||
// This is the first-load path for magic-link redemptions
|
||||
// (cookies set server-side, no prior localStorage).
|
||||
console.log('No cached user data, fetching from server');
|
||||
try {
|
||||
const freshData = await refreshUserData();
|
||||
if (freshData?.username) {
|
||||
updateUserMenuData();
|
||||
updateStorageUsageDisplay(freshData);
|
||||
resolveHomeFolder().then(() => loadFiles());
|
||||
await resolveHomeFolder();
|
||||
// Defer to the `authenticationDone` listener in main.js
|
||||
// so the hash-driven section + path init runs in one
|
||||
// place (was previously a `loadFiles()` here which
|
||||
// bypassed the hash context and produced
|
||||
// `/api/folders//resources` for external users).
|
||||
window.dispatchEvent(new CustomEvent('authenticationDone'));
|
||||
} else {
|
||||
console.warn('Could not retrieve user data, redirecting to login');
|
||||
localStorage.removeItem(USER_DATA_KEY);
|
||||
@@ -162,6 +177,16 @@ async function checkAuthentication() {
|
||||
|
||||
async function resolveHomeFolder() {
|
||||
if (app.userHomeFolderId) return;
|
||||
// External users (grant-only recipients) do not own a home folder
|
||||
// by design — see `HomeFolderLifecycleHook::provision_if_needed`
|
||||
// which short-circuits on `is_external`. Skip the fetch + leave
|
||||
// `userHomeFolderId` null so downstream code knows to land them on
|
||||
// /#/sharedwithme instead of /files.
|
||||
if (app.isExternalUser) {
|
||||
console.log('External user — skipping home-folder resolution');
|
||||
app.breadcrumbPath = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch('/api/folders', {
|
||||
credentials: 'same-origin'
|
||||
|
||||
@@ -405,6 +405,17 @@ async function loadFiles(options = { insertHistory: true }) {
|
||||
try {
|
||||
if (!app.userHomeFolderId) await resolveHomeFolder();
|
||||
|
||||
// External users have no home folder. If they land on /files
|
||||
// without a specific folder id in the URL, redirect them to
|
||||
// /#/sharedwithme — their actual landing page. This guards
|
||||
// against `fetchResourcesPage('')` building `/api/folders//resources`.
|
||||
if (app.isExternalUser && (!app.currentPath || app.currentPath === '')) {
|
||||
clearTimeout(spinnerTimeout);
|
||||
_loading = false;
|
||||
window.location.hash = '#/sharedwithme';
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve path to home folder when none is set
|
||||
if (!app.currentPath || app.currentPath === '') {
|
||||
if (app.userHomeFolderId) {
|
||||
|
||||
@@ -407,8 +407,11 @@ function setupActionsBarDelegation() {
|
||||
function deserializeHash() {
|
||||
const hashContext = /** type {OxiContext} */ {};
|
||||
|
||||
// External users have no home folder; default them to /#/sharedwithme
|
||||
// (their actual landing) so the URL bar reflects what they'll see.
|
||||
// Internal users default to the Files section.
|
||||
// FIXME rename files into drive ?
|
||||
hashContext.section = 'files';
|
||||
hashContext.section = app.isExternalUser ? 'sharedwithme' : 'files';
|
||||
|
||||
const hash_elements = window.location.hash.split('/');
|
||||
|
||||
|
||||
@@ -299,9 +299,15 @@ function switchToFilesSection() {
|
||||
//reset files view + remove any error
|
||||
ui.resetFilesList();
|
||||
|
||||
// Reset to home folder and update breadcrumb
|
||||
app.currentPath = app.userHomeFolderId || '';
|
||||
app.breadcrumbPath = [];
|
||||
// Reset to home folder and update breadcrumb. External users have no
|
||||
// home — leave `currentPath` as the caller set it (e.g. the magic-link
|
||||
// landing's hash context) so loadFiles() doesn't fall through to
|
||||
// `/api/folders//resources`. If `currentPath` is still empty by the
|
||||
// time loadFiles() runs, it self-redirects to /#/sharedwithme.
|
||||
if (!app.isExternalUser) {
|
||||
app.currentPath = app.userHomeFolderId || '';
|
||||
app.breadcrumbPath = [];
|
||||
}
|
||||
ui.updateBreadcrumb();
|
||||
if (batchToolbar) batchToolbar.clear();
|
||||
|
||||
|
||||
@@ -59,6 +59,16 @@ export const app = {
|
||||
/** @type {string | null} */
|
||||
userHomeFolderName: null,
|
||||
|
||||
/**
|
||||
* `true` when the authenticated caller is an external (grant-only)
|
||||
* user. Externals don't own a home folder, can't enumerate users,
|
||||
* and land on `/#/sharedwithme` by default. Set by `refreshUserData`
|
||||
* and the cached-data load path from the `is_external` field of
|
||||
* `/api/auth/me`'s response.
|
||||
* @type {boolean}
|
||||
*/
|
||||
isExternalUser: false,
|
||||
|
||||
/** @type {Array<{id: string, name: string}>} */
|
||||
breadcrumbPath: [], // Array of {id, name} tracking folder navigation hierarchy
|
||||
|
||||
|
||||
Reference in New Issue
Block a user