Skip to content

Commit f70890e

Browse files
author
Dionisio
committed
fix: upload failure, share dialog, shared view styling, dark mode fixes
- Fix 'folder_id is required' upload error by adding create_home_folder through the full hexagonal architecture (trait, service, repository, auth) - Fix double upload issue with _isUploading concurrency guard - Fix Share context menu doing nothing (ID collision between sharedView and main share dialog resolved with sv- prefix) - Fix Compartidos tab duplicate headers and broken layout - Add missing .shared-dialog CSS with dark mode support - Fix dark mode white backgrounds on empty-state, shared-filters, trash-actions, action-btn, and header - Fix i18n key mismatches in sharedView - Bump version to 0.4.1 Closes #120
1 parent b7bd656 commit f70890e

14 files changed

Lines changed: 511 additions & 258 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "oxicloud"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
edition = "2024"
55
default-run = "oxicloud"
66

src/application/ports/inbound.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ pub trait FolderUseCase: Send + Sync + 'static {
5555

5656
/// Deletes a folder (ownership verified against caller_id)
5757
async fn delete_folder(&self, id: &str, caller_id: &str) -> Result<(), DomainError>;
58+
59+
/// Creates a root-level home folder for a user during registration.
60+
async fn create_home_folder(&self, user_id: &str, name: String) -> Result<FolderDto, DomainError>;
5861
}
5962

6063
/**

src/application/services/auth_application_service.rs

Lines changed: 5 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::application::dtos::folder_dto::CreateFolderDto;
21
use crate::application::dtos::user_dto::{
32
AuthResponseDto, ChangePasswordDto, LoginDto, RefreshTokenDto, RegisterDto, UserDto,
43
};
@@ -318,43 +317,7 @@ impl AuthApplicationService {
318317
let created_user = self.user_storage.create_user(user).await?;
319318

320319
// Create personal folder for the user
321-
if let Some(folder_service) = &self.folder_service {
322-
let folder_name = format!("My Folder - {}", dto.username);
323-
324-
match folder_service
325-
.create_folder(CreateFolderDto {
326-
name: folder_name,
327-
parent_id: None,
328-
})
329-
.await
330-
{
331-
Ok(folder) => {
332-
tracing::info!(
333-
"Personal folder created for user {}: {} (ID: {})",
334-
created_user.id(),
335-
folder.name,
336-
folder.id
337-
);
338-
339-
// Here we could save the folder-to-user association,
340-
// for example, in a folder-user relationship table
341-
}
342-
Err(e) => {
343-
// We don't fail registration due to a folder creation error,
344-
// but we log it for investigation
345-
tracing::error!(
346-
"Could not create personal folder for user {}: {}",
347-
created_user.id(),
348-
e
349-
);
350-
}
351-
}
352-
} else {
353-
tracing::warn!(
354-
"Folder service not configured, cannot create personal folder for user: {}",
355-
created_user.id()
356-
);
357-
}
320+
self.create_personal_folder(&dto.username, created_user.id()).await;
358321

359322
tracing::info!("User registered: {}", created_user.id());
360323
Ok(UserDto::from(created_user))
@@ -695,34 +658,8 @@ impl AuthApplicationService {
695658
// 4. Save the new admin user
696659
let created_user = self.user_storage.create_user(user).await?;
697660

698-
// 5. Create personal folder for the new admin if folder service is available
699-
if let Some(folder_service) = &self.folder_service {
700-
let folder_name = format!("My Folder - {}", dto.username);
701-
702-
match folder_service
703-
.create_folder(CreateFolderDto {
704-
name: folder_name,
705-
parent_id: None,
706-
})
707-
.await
708-
{
709-
Ok(folder) => {
710-
tracing::info!(
711-
"Personal folder created for admin {}: {} (ID: {})",
712-
created_user.id(),
713-
folder.name,
714-
folder.id
715-
);
716-
}
717-
Err(e) => {
718-
tracing::error!(
719-
"Could not create personal folder for admin {}: {}",
720-
created_user.id(),
721-
e
722-
);
723-
}
724-
}
725-
}
661+
// 5. Create personal folder for the new admin
662+
self.create_personal_folder(&dto.username, created_user.id()).await;
726663

727664
tracing::info!("Custom admin created: {}", created_user.id());
728665
Ok(UserDto::from(created_user))
@@ -828,32 +765,7 @@ impl AuthApplicationService {
828765
}
829766

830767
// Create personal folder
831-
if let Some(folder_service) = &self.folder_service {
832-
let folder_name = format!("My Folder - {}", dto.username);
833-
match folder_service
834-
.create_folder(CreateFolderDto {
835-
name: folder_name,
836-
parent_id: None,
837-
})
838-
.await
839-
{
840-
Ok(folder) => {
841-
tracing::info!(
842-
"Personal folder created for admin-created user {}: {} (ID: {})",
843-
created.id(),
844-
folder.name,
845-
folder.id
846-
);
847-
}
848-
Err(e) => {
849-
tracing::error!(
850-
"Could not create personal folder for user {}: {}",
851-
created.id(),
852-
e
853-
);
854-
}
855-
}
856-
}
768+
self.create_personal_folder(&dto.username, created.id()).await;
857769

858770
tracing::info!("Admin created user: {} ({})", dto.username, created.id());
859771
Ok(UserDto::from(created))
@@ -1290,10 +1202,7 @@ impl AuthApplicationService {
12901202
if let Some(folder_service) = &self.folder_service {
12911203
let folder_name = format!("My Folder - {}", username);
12921204
match folder_service
1293-
.create_folder(CreateFolderDto {
1294-
name: folder_name.clone(),
1295-
parent_id: None,
1296-
})
1205+
.create_home_folder(user_id, folder_name.clone())
12971206
.await
12981207
{
12991208
Ok(folder) => {

src/application/services/folder_service.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ impl FolderService {
111111
async fn delete_folder(&self, _id: &str, _caller_id: &str) -> Result<(), DomainError> {
112112
Ok(())
113113
}
114+
115+
async fn create_home_folder(&self, _user_id: &str, _name: String) -> Result<FolderDto, DomainError> {
116+
Ok(FolderDto::empty())
117+
}
114118
}
115119

116120
FolderServiceStub
@@ -154,6 +158,22 @@ impl FolderUseCase for FolderService {
154158
Ok(FolderDto::from(folder))
155159
}
156160

161+
/// Creates a root-level home folder for a user during registration.
162+
async fn create_home_folder(&self, user_id: &str, name: String) -> Result<FolderDto, DomainError> {
163+
let folder = self
164+
.folder_storage
165+
.create_home_folder(user_id, name)
166+
.await
167+
.map_err(|e| {
168+
DomainError::internal_error(
169+
"FolderStorage",
170+
format!("Failed to create home folder: {}", e),
171+
)
172+
})?;
173+
174+
Ok(FolderDto::from(folder))
175+
}
176+
157177
/// Gets a folder by its ID
158178
async fn get_folder(&self, id: &str) -> Result<FolderDto, DomainError> {
159179
let folder = self.folder_storage.get_folder(id).await.map_err(|e| {

src/application/services/share_service.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,14 @@ mod tests {
609609
async fn delete_folder_permanently(&self, _folder_id: &str) -> Result<(), DomainError> {
610610
unimplemented!()
611611
}
612+
613+
async fn create_home_folder(
614+
&self,
615+
_user_id: &str,
616+
_name: String,
617+
) -> Result<crate::domain::entities::folder::Folder, DomainError> {
618+
unimplemented!()
619+
}
612620
}
613621

614622
struct MockShareRepository {

src/application/services/trash_service_test.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,14 @@ impl FolderRepository for MockFolderRepository {
461461
))
462462
}
463463
}
464+
465+
async fn create_home_folder(
466+
&self,
467+
_user_id: &str,
468+
_name: String,
469+
) -> std::result::Result<Folder, DomainError> {
470+
Ok(Folder::default())
471+
}
464472
}
465473

466474
#[cfg(test)]

src/common/stubs.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,14 @@ impl FolderRepository for StubFolderStoragePort {
322322
async fn delete_folder_permanently(&self, _folder_id: &str) -> Result<(), DomainError> {
323323
Ok(())
324324
}
325+
326+
async fn create_home_folder(
327+
&self,
328+
_user_id: &str,
329+
_name: String,
330+
) -> Result<Folder, DomainError> {
331+
Ok(Folder::default())
332+
}
325333
}
326334

327335
// ---------------------------------------------------------------------------
@@ -414,6 +422,10 @@ impl FolderUseCase for StubFolderUseCase {
414422
async fn delete_folder(&self, _id: &str, _caller_id: &str) -> Result<(), DomainError> {
415423
Ok(())
416424
}
425+
426+
async fn create_home_folder(&self, _user_id: &str, _name: String) -> Result<FolderDto, DomainError> {
427+
Ok(FolderDto::default())
428+
}
417429
}
418430

419431
// ---------------------------------------------------------------------------

src/domain/repositories/folder_repository.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,12 @@ pub trait FolderRepository: Send + Sync + 'static {
9999

100100
/// Permanently deletes a folder (used by the trash)
101101
async fn delete_folder_permanently(&self, folder_id: &str) -> Result<(), DomainError>;
102+
103+
/// Creates a root-level home folder for a user.
104+
/// This is used during user registration to create the user's personal folder.
105+
async fn create_home_folder(
106+
&self,
107+
user_id: &str,
108+
name: String,
109+
) -> Result<Folder, DomainError>;
102110
}

src/infrastructure/repositories/pg/folder_db_repository.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -603,17 +603,11 @@ impl FolderRepository for FolderDbRepository {
603603
}
604604
Ok(())
605605
}
606-
}
607-
608-
// ── Extra helpers for blob-storage bootstrap ──
609606

610-
impl FolderDbRepository {
611-
/// Creates a root-level home folder for a user.
612-
/// This is called during user registration.
613-
pub async fn create_home_folder(
607+
async fn create_home_folder(
614608
&self,
615609
user_id: &str,
616-
name: &str,
610+
name: String,
617611
) -> Result<Folder, DomainError> {
618612
let row = sqlx::query_as::<_, (String, String, i64, i64)>(
619613
r#"
@@ -626,14 +620,14 @@ impl FolderDbRepository {
626620
EXTRACT(EPOCH FROM updated_at)::bigint
627621
"#,
628622
)
629-
.bind(name)
623+
.bind(&name)
630624
.bind(user_id)
631625
.fetch_optional(self.pool())
632626
.await
633627
.map_err(|e| DomainError::internal_error("FolderDb", format!("home folder: {e}")))?;
634628

635629
match row {
636-
Some((id, path, ca, ma)) => Self::row_to_folder(id, name.to_string(), path, None, Some(user_id.to_string()), ca, ma),
630+
Some((id, path, ca, ma)) => Self::row_to_folder(id, name.clone(), path, None, Some(user_id.to_string()), ca, ma),
637631
None => {
638632
// Already exists — fetch it
639633
let existing = sqlx::query_as::<_, (String, String, i64, i64)>(
@@ -646,16 +640,20 @@ impl FolderDbRepository {
646640
WHERE name = $1 AND user_id = $2 AND parent_id IS NULL
647641
"#,
648642
)
649-
.bind(name)
643+
.bind(&name)
650644
.bind(user_id)
651645
.fetch_one(self.pool())
652646
.await
653647
.map_err(|e| DomainError::internal_error("FolderDb", format!("home fetch: {e}")))?;
654-
Self::row_to_folder(existing.0, name.to_string(), existing.1, None, Some(user_id.to_string()), existing.2, existing.3)
648+
Self::row_to_folder(existing.0, name, existing.1, None, Some(user_id.to_string()), existing.2, existing.3)
655649
}
656650
}
657651
}
652+
}
658653

654+
// ── Extra helpers for blob-storage bootstrap ──
655+
656+
impl FolderDbRepository {
659657
/// Returns user_id for a given folder. Used by file repositories.
660658
pub async fn get_folder_user_id(&self, folder_id: &str) -> Result<String, DomainError> {
661659
sqlx::query_scalar::<_, String>("SELECT user_id FROM storage.folders WHERE id = $1::uuid")

0 commit comments

Comments
 (0)