Skip to content

Feat/library#136

Open
MaryWylde wants to merge 17 commits into
devfrom
feat/library
Open

Feat/library#136
MaryWylde wants to merge 17 commits into
devfrom
feat/library

Conversation

@MaryWylde
Copy link
Copy Markdown
Contributor

No description provided.

MaryWylde and others added 16 commits June 1, 2026 13:36
Import the keepsimple Library feature source from
github.com/keepsimpleio/library into keepsimple's existing folder
structure, namespaced under `library/` to avoid collisions:

- components atoms/molecules/organisms -> src/components/library/
- templates                            -> src/layouts/library/
- types (+ d.ts type shims)            -> src/local-types/library/
- utils                                -> src/utils/library/
- hooks                                -> src/hooks/library/
- constants (+ config/seo.config.ts)   -> src/constants/library/
- contexts                             -> src/components/Context/library/
- axios/cookie libs                    -> src/lib/library/
- client services (src/api + app/api)  -> src/api/library/
- code-imported assets                 -> src/assets/library/
- shared scss                          -> src/styles/library/
- AGENT.md -> src/components/library/LIBRARY_AGENT.md

Excluded per task: package.json/lockfile, node_modules, CI/workflows,
.claude/.husky/.idea, eslint/tsconfig, README/docs, Storybook (.storybook
+ *.stories.*), App-Router routing/infra (app/page, app/layout, loading,
app/auth), all NextAuth setup, and route handlers (libraries proxy,
geoip /api/user, test-login mock). Imports/deps/auth/route are fixed in
later steps.
Copy Library's public/images into public/library/images so they don't
collide with keepsimple's public root, and rewrite the three absolute
public references in the imported code from /images/... to
/library/images/...:

- atoms/Icon/Icon.tsx       — SVG sprite href (/library/images/icons/all.svg)
- constants/library/seo.config.ts — default OG image path
- layouts/library/Home/Home.tsx   — landing cover image

JS module imports of assets (@/assets/images/*) are alias rewrites,
handled in Step 4.
Add only the runtime packages the migrated Library code imports that
keepsimple was missing (versions from Library, since these are new —
no shared-version conflict). Shared packages (react, react-dom, next,
next-auth, classnames, react-tooltip, date-fns, sass, geoip-lite) keep
keepsimple's existing versions.

Added (dependencies):
- axios ^1.13.4            — Library Strapi client (src/lib/library/axios)
- js-cookie ^3.0.5         — cookie helper (src/lib/library/cookie)
- zod ^4.3.6               — form/validation schemas (src/utils/library/schema)
- react-hook-form ^7.71.1  — forms in modals
- @hookform/resolvers ^5.2.2 — zod<->react-hook-form bridge
- react-dropzone ^15.0.0   — ImageDropzone molecule
- react-day-picker ^10.0.0 — DatePicker molecule
- @dnd-kit/core ^6.3.1, @dnd-kit/sortable ^10.0.0, @dnd-kit/utilities ^3.2.2
                           — ReorderGrid drag-and-drop

Added (devDependencies):
- @types/js-cookie ^3.0.6  — js-cookie ships no types

Not added: commander/fs-extra (Library generators — not migrated),
Storybook/vitest/playwright tooling (excluded). Single yarn install run
with yarn classic (v1) to keep yarn.lock in its canonical v1 format.
Rewrite Library's @/* aliases to keepsimple's per-folder aliases, relocate
assets under @iCons, strip 'use client', convert next/navigation to
next/router, and rewire auth to keepsimple's NextAuth + @api/auth. Make
the next-auth.d.ts a proper module augmentation so it no longer clobbers
next-auth's default export.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add Pages Router entry points for the migrated Library feature:
/library (Home) and /library/[username] (dashboard). Both use
getServerSideProps for SSR and reuse keepsimple's SeoGenerator
instead of next/head blocks. Scope a sassOptions.additionalData
injection in next.config.js to library SCSS modules only, so the
placeholder selectors the original app injected globally resolve
without touching keepsimple's own styles.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the Library nav item (flagged via isLibraryEnabled), a "My Library"
shortcut in the user-profile dropdown, and notFound guards on the library
pages when the feature is disabled.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Switch the sticky global header to #F8F1E5 on /library paths (including the
dark-theme variant, since the library has no dark mode) so it blends with the
library surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Retire the old library Header organism in favor of a sticky LibraryToolbar:
shelf jump-pills with overflow arrows, a Search Everywhere input, and an Add
shelf control that disables at the 21-shelf cap. Adds Input onKeyDown for the
search and MAX_SHELVES_PER_LIBRARY.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move the library's global CSS behind a single library-global entry imported
from _app, scope resets under .library, and wrap portaled menus/modals in a
.library container so they inherit the tokens. Self-host Source Serif 4 (the
Google Fonts @import is blocked by CSP) and declare font-family vars.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Override SVGO's removeViewBox in the SVGR loader so downscaled shelf icons keep
their coordinate system instead of cropping, and strip stray blue frame-outline
artifacts from the book and video glyphs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Edit a shelf name by clicking it (drop the settings-menu edit entry), drop the
doubled brown border on the settings-trigger dropdown, append new shelves at the
end with an inline loader instead of a full-screen swap, hide private shelves
from non-owners, give overflowing object rows a 12px #C0B6AE scrollbar with 40px
top headroom, and glide cards to their new slots after a reorder via a FLIP
animation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the reorderObjects/reorderShelves endpoints and wire the step-2 ReorderGrid
to persist on save. Resolve the shelf id from a passed-in fallback so edit mode
no longer silently skips, and sort populated shelves/objects by order:asc so the
saved sequence survives a refetch. Stop swallowing reorder errors — log status +
body, roll back the optimistic order, and message the user. Also derive the
ImageDropzone preview synchronously so the cover no longer flashes empty.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Prefer the optimistically updated currentShelves over the stale one-shot
library fetch for the sidebar book/video/song totals so counts bump on upload,
and load the tag list on mount so the Tags panel isn't empty until the first
mutation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Always render the tag column (even when empty) and fix container dimensions so
book/audio/video cards keep a consistent width with or without tags, and paint
the book cover gradient immediately so freshly uploaded covers don't flash blank.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stamp publishedAt when creating a tag (the content type is draftAndPublish), so
newly created tags don't vanish on the next load.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MaryWylde
Copy link
Copy Markdown
Contributor Author

@claude

@claude
Copy link
Copy Markdown

claude Bot commented Jun 3, 2026

Claude finished @MaryWylde's task in 2m 37s —— View job


PR Review: Feat/library

  • Gather PR context
  • Review API layer (src/api/library/)
  • Review component architecture (atoms, molecules, contexts)
  • Review config/infrastructure changes (next.config.js, package.json)
  • Review modifications to existing components (Header, Navbar, UserProfile)
  • Post comprehensive review feedback

Good foundational work — the atomic component hierarchy, Zod+RHF form validation, FLIP animation on card reorder, and the optimistic-with-rollback pattern in AddObjectModal are all well-done. Below are the issues worth resolving before merge.


Bugs

1. AuthContext — logout doesn't reset in-memory state

handleLogout clears the cookie but never calls setToken(null) or setAccountData(null). Any component reading token or accountData from context will continue to see the logged-in user until a hard refresh. Fix this →

2. GlobalStateContext — double fetch of libraries on first authenticated load

Two useEffects both call refetchLibraries() on mount when a token exists. The unconditional first effect ([refetchLibraries]) fires and doesn't set didAttemptLibrariesLoad.current, so the second effect's guard doesn't fire, and it calls refetchLibraries() a second time. Fix this →

3. AddObjectModal — "Select tag to reorder" dropdown is dead UI

The tag-filter dropdown in step 2 renders with no onChange or value, so it neither reads nor writes any state. It does nothing.

// src/components/library/organisms/AddObjectModal/AddObjectModal.tsx
<Dropdown
  options={selectedTags.map(t => ({ value: String(t.id), label: t.name }))}
  === 0 ? 'Pick tags above first' : 'Select a tag to filter'}
  disabled={selectedTags.length === 0}
  // ← no onChange or value
/>

Either wire it up to filter reorderItems or remove it until the feature is ready. Fix this →


Type Safety

4. GlobalStateContextlibraries typed as unknown

libraries: unknown | null;  // consumers must cast every time

getLibrariesList() returns StrapiLibrariesResponse | null when called from this context. Narrowing the type here eliminates downstream casts. Fix this →

5. createTag.ts — missing return type

createTag has no explicit return type annotation, unlike every other API function in this module. This leaves its resolved shape as any. Fix this →


Architecture / Consistency

6. getTagsList — App Router SSR branch in a Pages Router project

getTagsList has a typeof window === 'undefined' guard that dynamically imports next/headers (an App Router API). This project uses Pages Router (useRouter from next/router). In Pages Router SSR (getServerSideProps), next/headers is not available and this branch would throw. The comment says "when called from a Server Component" — but Pages Router has no Server Components.

If this is intentional preparation for an App Router migration, add a clear comment. If not, remove the dead branch.

7. reorderObjects / reorderShelves — no error handling

All other API functions in this module wrap their calls in try/catch and return null on failure. reorderObjects, reorderShelves, createObject, and uploadFile don't, so callers must handle every error path themselves. AddObjectModal does wrap the call in try/catch, but the inconsistency is a footgun for future callers. Fix this →

8. AuthContext — manual cookie manipulation instead of js-cookie

js-cookie is already a declared dependency. handleProviderSignIn and handleLogout manipulate document.cookie directly — including constructing Secure and SameSite attributes by hand. This is fragile and inconsistent with the getCookie helper used in useEffect. Use Cookies.remove() from js-cookie instead.


Minor / Cosmetic

9. UserProfile.tsxLibraryIcon has an unusual aspect ratio

The icon is rendered at width={20} height={11}, which is not square and doesn't match the other menu icons in the dropdown (Settings uses Image with no explicit non-square dimensions). Confirm this is intentional for the SVG's natural aspect ratio.

10. Incomplete hy locale in UserProfile

The hy (Armenian) locale label for myLibrary is 'My Library' (English), while ru already has the correct translation. This was likely an oversight. Fix this →

11. Three open TODOs blocking completeness

// TODO: hook up URL metadata auto-fetch (YouTube / Spotify / generic OG)
// TODO: drop the client-side `slug` once Strapi makes `object.slug` optional
// TODO: drop once Strapi sets `owner` in `beforeCreate`

All are in AddObjectModal.tsx. The slug and owner hacks are server-side dependencies — fine to ship with comments. The URL metadata fetch is a UI gap (the "Search" button does nothing); consider removing the button until it's wired up, so users don't click a no-op control.


- flatten src/api/library/library/ up one level and split strapi.ts into
  per-call files (getLibrariesList, getLibrariesPaginated, getSingleLibrary,
  user/getUserInfo, shared libraryCardPopulate); drop dead getShelves
- remove App-Router leftovers: molecules/SEO, molecules/SignInModal,
  molecules/UserDropDown, utils/library/seo.ts (host reuses SeoGenerator,
  login modal, and keepsimple header)
- consolidate library fonts into globals.scss; delete styles/library/fonts.scss
- Dropdown: move inline styles into module.scss classes
- AudioCard: replace hardcoded focus color with --focus-ring var
- AuthContext: reset in-memory token/account on logout, use js-cookie helpers
- GlobalStateContext: drop unconditional first fetch (double-load), type
  libraries as StrapiLibrariesResponse; simplify Sidebar narrowing
- createTag: explicit response type; getTagsList: drop dead SSR header branch
- UserProfile: fill in hy (Armenian) menu labels
- gitignore LIBRARY_TODO.md scratch file

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant