diff --git a/internal/router/frontend.go b/internal/router/frontend.go
index b89306a..017a61f 100644
--- a/internal/router/frontend.go
+++ b/internal/router/frontend.go
@@ -12,6 +12,7 @@ import (
"strconv"
"time"
+ "bookhoard/internal/config"
"bookhoard/internal/database"
"bookhoard/internal/handlers"
"bookhoard/internal/services"
@@ -295,6 +296,18 @@ func registerFrontendRoutes(cfg *Config) {
libraryID := libRes.LibraryID
libData := libRes.Libraries
+ // Steam Deck behavior: an offline library (all folders missing, e.g.
+ // an unmounted external drive) shows an empty shelf with a notice
+ // instead of querying items. Nothing is marked or purged.
+ if !libRes.IsAll {
+ for _, l := range libData {
+ if l.ID == libraryID && l.Offline {
+ errorMsg = l.Name + " is currently unavailable - its storage is not connected. Your books are safe and will return when the drive is reattached."
+ break
+ }
+ }
+ }
+
// Fetch saved filters for SSR
userUUID, _ := uuid.Parse(user.ID)
var savedFilters []database.SavedFilters
diff --git a/internal/router/helpers.go b/internal/router/helpers.go
index 88441bf..c3b610e 100644
--- a/internal/router/helpers.go
+++ b/internal/router/helpers.go
@@ -4,6 +4,7 @@ import (
"context"
"log"
"net/url"
+ "os"
"time"
"bookhoard/internal/database"
@@ -94,11 +95,11 @@ const selectedLibraryCookie = "selectedLibrary"
const allLibrariesSentinel = "__all__"
type LibraryResolution struct {
- LibraryID string
- IsAll bool
- LibUUID pgtype.UUID
- Libraries []templates.LibraryData
- FirstID string
+ LibraryID string
+ IsAll bool
+ LibUUID pgtype.UUID
+ Libraries []templates.LibraryData
+ FirstID string
}
func getText(t pgtype.Text) string {
@@ -132,12 +133,30 @@ func resolveLibrary(c *echo.Context, cfg *Config, userUUID string) LibraryResolu
res.Libraries = make([]templates.LibraryData, len(libraries))
for i, lib := range libraries {
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
+ // Steam Deck SD-card behavior: a library whose folders are all
+ // missing (unmounted drive) renders offline - hidden contents, no
+ // marking, no purging - and returns when the storage does.
+ offline := false
+ folders, folderErr := cfg.Queries.GetLibraryFolders(c.Request().Context(), lib.ID)
+ if folderErr != nil || len(folders) == 0 {
+ offline = true
+ } else {
+ anyFolderExists := false
+ for _, folder := range folders {
+ if _, statErr := os.Stat(folder.FolderPath); statErr == nil {
+ anyFolderExists = true
+ break
+ }
+ }
+ offline = !anyFolderExists
+ }
res.Libraries[i] = templates.LibraryData{
ID: libUUID.String(),
Name: lib.Name,
Description: getText(lib.Description),
TypeName: lib.TypeName,
MediaCount: countMap[libUUID.String()],
+ Offline: offline,
}
}
diff --git a/templates/bookshelf.templ b/templates/bookshelf.templ
index 498f64f..46c4067 100644
--- a/templates/bookshelf.templ
+++ b/templates/bookshelf.templ
@@ -148,14 +148,20 @@ templ BookShelf(
} else {
}
- for _, lib := range libraries {
+ for _, lib := range libraries {
+ if lib.Offline {
if lib.ID == currentLibraryID {
-
+
} else {
-
+
}
+ } else if lib.ID == currentLibraryID {
+
+ } else {
+
}
}
+ }
diff --git a/templates/bookshelf_templ.go b/templates/bookshelf_templ.go
index 5d08e46..1a2c378 100644
--- a/templates/bookshelf_templ.go
+++ b/templates/bookshelf_templ.go
@@ -203,98 +203,188 @@ func BookShelf(
}
}
for _, lib := range libraries {
- if lib.ID == currentLibraryID {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ } else if lib.ID == currentLibraryID {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, ")")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, ")")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
= 2) fetchAuthorValues($el)\">
= 2) fetchSeriesValues($el)\">
= 2) fetchLanguageValues($el)\">
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
= 2) fetchAuthorValues($el)\">
= 2) fetchSeriesValues($el)\">
= 2) fetchLanguageValues($el)\">
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -303,53 +393,53 @@ func BookShelf(
return templ_7745c5c3_Err
}
if errorMessage != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var12 string
- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage)
+ var templ_7745c5c3_Var18 string
+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 269, Col: 59}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 275, Col: 59}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "
Save Filter
Save Filter