diff --git a/Cargo.toml b/Cargo.toml index 6a9a44689..6a67522ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,13 @@ members = [ "src/logs", "src/xrc", "src/user", + "src/dialectica", "src/alex_wallet", "src/emporium", "src/logs", "src/asset_manager", "src/feed", "src/alex_revshare", + "src/kairos", ] resolver = "2" diff --git a/MONOREPO.md b/MONOREPO.md new file mode 100644 index 000000000..738dd0c0c --- /dev/null +++ b/MONOREPO.md @@ -0,0 +1,290 @@ +# Frontend Monorepo Migration Plan + +## Overview +Convert alex_frontend from single app to monorepo workspace with shared core library and individual apps (starting with lbry). + +## Status: ✅ COMPLETED +**Last Updated**: November 27, 2024 +**Current State**: Monorepo successfully set up with core library and lbry app + +## Goals +- ✅ Core library for shared components, hooks, services +- ✅ Self-contained apps (lbry, future sonora) +- ✅ @ alias pointing to core for all imports +- ✅ Minimal file changes, maximum safety + +## Safety Measures +- ✅ Complete backup option available +- ✅ Step-by-step approach with testing at each phase +- ✅ Preserve all existing functionality + +## Migration Steps + +### Phase 1: Safety & Structure Setup + +- [x] **Step 1: Create Backup** (Optional - structure preserved) + +- [x] **Step 2: Update Root package.json** + - ✅ Added workspace configuration: `"workspaces": ["src/alex_frontend/*"]` + - ✅ Kept all existing dependencies and scripts + - ✅ Added typecheck script: `"typecheck": "tsc --noEmit"` + +- [x] **Step 3: Create Core Directory** + - ✅ Core directory created at `src/alex_frontend/core` + - ✅ All shared code moved to core + +- [x] **Step 4: Create Core package.json** + ```json + { + "name": "core", + "version": "1.0.0", + "private": true, + "main": "index.ts" + } + ``` + +- [x] **Step 5: Create lbry app** + - ✅ Lbry app created at `src/alex_frontend/lbry` + - ✅ App-specific code preserved in lbry/src + +- [x] **Step 6: Create Lbry package.json** + - ✅ Full package.json with all dependencies + - ✅ Scripts configured for dev, build, and test + +### Phase 2: Config Updates (One by One) + +- [x] **Step 7: Update Root tailwind.config.js** + - ✅ Content paths include all workspace files + - ✅ Existing theme and plugins preserved + +- [x] **Step 8: Verify Root postcss.config.js** + - ✅ Works correctly for all apps + - ✅ No changes needed + +- [x] **Step 9: Update Root components.json** + - ✅ Core components.json exists in core directory + - ✅ Aliases configured for shadcn components + +- [x] **Step 10: Update Root tsconfig.json** + - ✅ @ alias configured: `"@/*": ["./src/alex_frontend/core/*"]` + - ✅ Include paths set for all workspace TypeScript files + - ✅ All existing configuration preserved + +### Phase 3: App-Specific Updates + +- [x] **Step 11: Copy webpack to lbry** + - ✅ Webpack config exists in lbry directory + +- [x] **Step 12: Update Lbry webpack.config.js** + - ✅ Entry path configured: `path.join(__dirname, "index.tsx")` + - ✅ @ alias configured: `"@": path.resolve(__dirname, "../core")` + - ✅ HTML plugin configured with correct public path + - ✅ All loaders and plugins properly configured + - ✅ Development and production modes working + +- [x] **Step 13: Clean Up Original Structure** + - ✅ Original src moved to core + - ✅ Lbry has its own src for app-specific code + - ✅ Public assets in lbry/public + +### Phase 4: Testing + +- [x] **Step 14: Install Dependencies** + - ✅ Dependencies installed at workspace root + - ✅ Workspace packages linked correctly + +- [x] **Step 15: Test Lbry App** + - ✅ TypeScript compilation passes without errors + - ✅ Webpack configuration validated + - ✅ App entry point (index.tsx) correctly configured + +- [x] **Step 16: Verify @ Imports Work** + - ✅ @ imports working in lbry/src/App.tsx + - ✅ Core providers imported successfully + - ✅ Core components imported successfully + - ✅ All TypeScript paths resolved correctly + +## Final Structure + +``` +ugd/ +├── src/ +│ ├── legacy_alex_frontend/ # BACKUP - Complete original +│ └── alex_frontend/ +│ ├── core/ # Shared library +│ │ ├── package.json # @alexandria/core +│ │ ├── components/ # Shared components +│ │ ├── lib/ # Shadcn components +│ │ ├── hooks/ # Shared hooks +│ │ ├── services/ # Shared services +│ │ ├── store/ # Redux logic +│ │ ├── utils/ # Utilities +│ │ ├── features/ # Feature slices +│ │ ├── contexts/ # React contexts +│ │ ├── providers/ # React providers +│ │ ├── guards/ # Auth guards +│ │ ├── types/ # TypeScript types +│ │ ├── styles/ # Global styles +│ │ ├── fonts/ # Font files +│ │ ├── data/ # Static data +│ │ └── config/ # Config files +│ └── lbry/ # Main app +│ ├── package.json # @alexandria/lbry +│ ├── webpack.config.js +│ ├── public/ # All public assets +│ │ ├── index.html +│ │ ├── fonts/ +│ │ ├── icons/ +│ │ ├── images/ +│ │ ├── logos/ +│ │ └── models/ +│ └── src/ # App-specific code +│ ├── index.js +│ ├── App.tsx +│ ├── apps/ # App modules +│ ├── pages/ # Page components +│ ├── layouts/ # Layout components +│ └── routes/ # Route definitions +├── package.json # UPDATED: workspace config +├── tailwind.config.js # UPDATED: content paths +├── components.json # UPDATED: core paths +├── postcss.config.js # UNCHANGED +├── tsconfig.json # UPDATED: @ paths +└── webpack.config.js # UNCHANGED (reference) +``` + +## Import Pattern Examples + +After migration, all apps will import from core: +```tsx +// Components +import { Button } from '@/lib/components/button'; +import { Dialog } from '@/lib/components/dialog'; + +// Custom components +import { Header } from '@/components/Header'; + +// Hooks +import { useAuth } from '@/hooks/useAuth'; + +// Services +import { apiService } from '@/services/apiService'; + +// Utils +import { formatDate } from '@/utils/formatDate'; + +// Types +import { User } from '@/types/User'; +``` + +## Future Steps (After Verification) + +1. **Create sonora app** + ```bash + cp -r src/alex_frontend/lbry src/alex_frontend/sonora + # Update package.json name + # Remove non-audio features + ``` + +2. **Create bibliotheca app** + ```bash + cp -r src/alex_frontend/lbry src/alex_frontend/bibliotheca + # Update package.json name + # Remove non-book features + ``` + +3. **Optimize core library** + - Create index.ts exports + - Tree-shaking optimization + - Remove app-specific code + +## Rollback Plan + +If issues occur, restore from backup: +```bash +rm -rf src/alex_frontend +cp -r src/legacy_alex_frontend src/alex_frontend +# Restore original configs if modified +``` + +## Notes + +- **Shadcn components**: All in core/lib/components +- **@ alias**: Points to core for all apps +- **Public assets**: Each app has its own +- **Shared configs**: Root level (tailwind, postcss, components.json) +- **App configs**: Webpack per app +- **Dependencies**: All in root package.json (workspace) +- **Testing**: Test lbry first before creating other apps + +## Common Issues & Solutions + +**Issue**: Module not found when importing from @/ +**Solution**: Check tsconfig.json paths and webpack alias + +**Issue**: Tailwind styles not applied +**Solution**: Verify tailwind.config.js content paths include all apps + +**Issue**: Shadcn component not found +**Solution**: Check components.json aliases point to core paths + +**Issue**: Webpack build fails +**Solution**: Verify webpack.config.js paths are relative to lbry directory + +## Verification Checklist + +- [x] Lbry app structure correct +- [x] Workspace configuration working +- [x] @ imports work correctly +- [x] TypeScript compilation passes +- [x] All paths resolve correctly +- [x] Core library accessible from lbry +- [x] Webpack configuration validated +- [x] Package.json scripts configured +- [x] All dependencies properly installed + +## What Has Been Achieved + +1. **Successful Monorepo Structure**: + - Core library at `src/alex_frontend/core` contains all shared code + - Lbry app at `src/alex_frontend/lbry` with app-specific code + - Workspace configuration functioning correctly + +2. **Import System Working**: + - @ alias properly configured in tsconfig.json and webpack + - All imports from core working in lbry app + - TypeScript paths resolving correctly + +3. **Configuration Files**: + - Root package.json has workspace configuration + - Individual package.json files for core and lbry + - Webpack, TypeScript, and other configs properly set up + +## Next Steps for Sonora App + +To create the Sonora audio NFT marketplace app: + +1. **Copy lbry structure**: + ```bash + cp -r src/alex_frontend/lbry src/alex_frontend/sonora + ``` + +2. **Update sonora/package.json**: + - Change name to "sonora" + - Keep all dependencies + +3. **Clean up sonora/src**: + - Remove book-specific components + - Keep routing structure + - Add audio-specific features + +4. **Update routes**: + - Modify routes for audio NFT functionality + - Keep authentication and common flows + +--- + +**Created**: November 2024 +**Status**: ✅ COMPLETED +**Last Updated**: November 27, 2024 +**Next Step**: Create Sonora app following the same pattern \ No newline at end of file diff --git a/Makefile b/Makefile index 63fcdc69f..9a444c19f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all clean fresh ii xrc icrc7 icrc7-scion nft-manager alex-backend perpetua feed icp-swap tokenomics user system-api alex-wallet vetkd emporium logs asset-manager alex-revshare ensure-identities clean-identities test icp-ledger lbry alex tokens frontend help +.PHONY: all clean fresh ii xrc icrc7 icrc7-scion nft-manager alex-backend perpetua feed icp-swap tokenomics user system-api alex-wallet vetkd emporium logs asset-manager alex-revshare dialectica kairos ensure-identities clean-identities test icp-ledger lbry alex tokens frontend help # Start dfx and basic setup clean: @@ -163,6 +163,22 @@ alex-revshare: candid-extractor target/wasm32-unknown-unknown/release/alex_revshare.wasm > src/alex_revshare/alex_revshare.did dfx deploy alex_revshare --specified-id e454q-riaaa-aaaap-qqcyq-cai +# Deploy Dialectica +dialectica: + @echo "Deploying Dialectica..." + cargo build --release --target wasm32-unknown-unknown --package dialectica + candid-extractor target/wasm32-unknown-unknown/release/dialectica.wasm > src/dialectica/dialectica.did + dfx deploy dialectica + dfx generate dialectica + +# Deploy Kairos +kairos: + @echo "Deploying Kairos..." + cargo build --release --target wasm32-unknown-unknown --package kairos + candid-extractor target/wasm32-unknown-unknown/release/kairos.wasm > src/kairos/kairos.did + dfx deploy kairos + dfx generate kairos + # Ensure all required identities exist (without switching current identity) ensure-identities: @echo "Ensuring identities exist..." @@ -277,6 +293,8 @@ help: @echo " logs - Deploy Logs" @echo " asset-manager - Deploy Asset Manager" @echo " alex-revshare - Deploy Alex Revshare" + @echo " dialectica - Deploy Dialectica canister" + @echo " kairos - Deploy Kairos canister" @echo " ensure-identities - Ensure all required identities, Create if don't exist" @echo " clean-identities - Remove all project identities" @echo " icp-ledger - Deploy ICP Ledger (LICP)" diff --git a/components.json b/components.json index 83ecefb26..d6a821da1 100644 --- a/components.json +++ b/components.json @@ -1,18 +1,18 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "tailwind": { - "config": "tailwind.config.js", - "css": "src/alex_frontend/src/styles/tailwind.css", - "baseColor": "slate", - "cssVariables": true - }, - "rsc": false, - "tsx": true, - "aliases": { - "components": "@/lib/components", - "utils": "@/lib/utils", - "hooks": "@/lib/hooks", - "ui": "@/lib/components" - } + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "tailwind": { + "config": "tailwind.config.js", + "css": "src/alex_frontend/core/tailwind.css", + "baseColor": "slate", + "cssVariables": true + }, + "rsc": false, + "tsx": true, + "aliases": { + "components": "@/lib/components", + "utils": "@/lib/utils", + "hooks": "@/lib/hooks", + "ui": "@/lib/components" + } } \ No newline at end of file diff --git a/dfx.json b/dfx.json index db11b4cd4..e6379b498 100644 --- a/dfx.json +++ b/dfx.json @@ -32,6 +32,11 @@ "package": "user", "candid": "src/user/user.did" }, + "dialectica": { + "type": "rust", + "package": "dialectica", + "candid": "src/dialectica/dialectica.did" + }, "alex_backend": { "candid": "src/alex_backend/alex_backend.did", "package": "alex_backend", @@ -140,6 +145,11 @@ "package": "alex_revshare", "type": "rust", "specified_id": "e454q-riaaa-aaaap-qqcyq-cai" + }, + "kairos": { + "type": "rust", + "package": "kairos", + "candid": "src/kairos/kairos.did" } }, "defaults": { diff --git a/package.json b/package.json index 32dff423c..89778f392 100644 --- a/package.json +++ b/package.json @@ -1,454 +1,217 @@ { - - "name": "alex_frontend", - - "version": "0.1.0", - - "description": "Internet Computer starter application", - - "keywords": [ - - "Internet Computer", - - "Motoko", - - "JavaScript", - - "Canister" - - ], - - "scripts": { - - "dev": "webpack serve --mode development --open", - - "build": "NODE_ENV=production webpack --mode production", - - "prebuild": "dfx generate", - - "start": "webpack serve --mode development --env development", - - "deploy:local": "dfx deploy --network=local", - - "deploy:ic": "dfx deploy --network=ic", - - "generate": "dfx generate alex_backend", - - "typecheck": "tsc --noEmit", - - "test": "jest", - - "test:watch": "jest --watch", - - "test:coverage": "jest --coverage" - - }, - - "dependencies": { - - "@apollo/client": "^3.11.8", - - "@dfinity/agent": "^3.2.7", - - "@dfinity/assets": "^3.2.7", - - "@dfinity/auth-client": "^3.2.7", - - "@dfinity/candid": "^3.2.7", - - "@dfinity/identity": "^3.2.7", - - "@dfinity/ledger-icp": "^6.0.1", - - "@dfinity/principal": "^3.2.7", - - "@dfinity/utils": "^3.1.0", - - "@fortawesome/fontawesome-svg-core": "^6.6.0", - - "@fortawesome/free-regular-svg-icons": "^6.6.0", - - "@fortawesome/free-solid-svg-icons": "^6.6.0", - - "@fortawesome/react-fontawesome": "^0.2.2", - - "@irys/sdk": "^0.2.11", - - "@meilisearch/instant-meilisearch": "^0.20.0", - - "@radix-ui/react-accordion": "^1.2.2", - - "@radix-ui/react-alert-dialog": "^1.1.2", - - "@radix-ui/react-aspect-ratio": "^1.1.1", - - "@radix-ui/react-avatar": "^1.1.9", - - "@radix-ui/react-checkbox": "^1.1.2", - - "@radix-ui/react-collapsible": "^1.1.2", - - "@radix-ui/react-dialog": "^1.1.13", - - "@radix-ui/react-dropdown-menu": "^2.1.2", - - "@radix-ui/react-label": "^2.1.0", - - "@radix-ui/react-popover": "^1.1.2", - - "@radix-ui/react-progress": "^1.1.1", - - "@radix-ui/react-scroll-area": "^1.2.8", - - "@radix-ui/react-select": "^2.1.4", - - "@radix-ui/react-separator": "^1.1.1", - - "@radix-ui/react-slider": "^1.2.1", - - "@radix-ui/react-slot": "^1.1.0", - - "@radix-ui/react-switch": "^1.1.1", - - "@radix-ui/react-tabs": "^1.1.1", - - "@radix-ui/react-toast": "^1.2.2", - - "@radix-ui/react-toggle": "^1.1.0", - - "@radix-ui/react-toggle-group": "^1.1.0", - - "@radix-ui/react-tooltip": "^1.1.6", - - "@reduxjs/toolkit": "^2.5.0", - - "@slide-computer/signer": "^4.0.0", - - "@slide-computer/signer-extension": "^3.20.0", - - "@slide-computer/signer-web": "^4.0.0", - - "@solana/wallet-adapter-base": "^0.9.23", - - "@solana/wallet-adapter-react": "^0.15.35", - - "@solana/wallet-adapter-react-ui": "^0.9.35", - - "@tanstack/react-query": "^5.62.8", - - "@tanstack/react-query-devtools": "^5.83.0", - - "@tanstack/react-router": "^1.129.8", - - "@tanstack/react-router-devtools": "^1.120.16", - - "@tanstack/react-virtual": "^3.12.0", - - "@tensorflow/tfjs": "^4.21.0", - - "@types/dompurify": "^3.0.5", - - "@types/react-beautiful-dnd": "^13.1.8", - - "@xstate/store": "^3.9.2", - - "antd": "^5.20.5", - - "arweave": "^1.15.5", - - "autoprefixer": "^10.4.20", - - "axios": "^1.7.7", - - "babel-loader": "^9.2.1", - - "browserify-zlib": "^0.2.0", - - "class-variance-authority": "^0.7.0", - - "clsx": "^2.1.1", - - "cmdk": "^1.0.0", - - "date-fns": "^3.6.0", - - "dompurify": "^3.2.3", - - "echarts": "^5.6.0", - - "epubjs": "^0.3.93", - - "ethers": "^6.13.2", - - "flatted": "^3.3.1", - - "formik": "^2.4.6", - - "graphql": "^16.11.0", - - "html-to-text": "^9.0.5", - - "human-crypto-keys": "^0.1.4", - - "ic-mops": "^1.0.1", - - "ic-use-actor": "^0.3.1", - - "ic-vetkd-utils": "file:ic-vetkd-utils-0.1.0.tgz", - - "instantsearch.css": "^8.5.0", - - "instantsearch.js": "^4.74.0", - - "jwk-to-pem": "^2.0.7", - - "lodash": "^4.17.21", - - "lru-cache": "^11.0.2", - - "lucide-react": "^0.539.0", - - "meilisearch": "^0.42.0", - - "nanoid": "^5.0.7", - - "next-themes": "^0.3.0", - - "npm": "^10.8.3", - - "nprogress": "^0.2.0", - - "nsfwjs": "^4.2.0", - - "papaparse": "^5.4.1", - - "path": "^0.12.7", - - "path-browserify": "^1.0.1", - - "postcss": "^8.4.45", - - "react": "^18.3.1", - - "react-beautiful-dnd": "^13.1.1", - - "react-circle-flags": "^0.0.20", - - "react-csv": "^2.2.2", - - "react-day-picker": "^8.10.1", - - "react-dom": "^18.3.1", - - "react-error-boundary": "^5.0.0", - - "react-instantsearch-dom": "^6.40.4", - - "react-intersection-observer": "^9.16.0", - - "react-lazy-load-image-component": "^1.6.3", - - "react-markdown": "^9.1.0", - - "react-paginate": "^8.2.0", - - "react-pdf": "^10.0.1", - - "react-plock": "^3.5.1", - - "react-qr-code": "^2.0.15", - - "react-rating-star-with-type": "^1.2.2", - - "react-redux": "^9.1.2", - - "react-responsive-masonry": "^2.3.0", - - "react-syntax-highlighter": "^15.6.1", - - "react-viewer": "^3.2.2", - - "react-zoom-pan-pinch": "^3.7.0", - - "remark-gfm": "^4.0.1", - - "root": "github:tanstack/react-virtual", - - "sonner": "^1.5.0", - - "styled-components": "^6.1.13", - - "swiper": "^11.1.12", - - "swr": "^2.3.4", - - "tailwind-merge": "^2.5.3", - - "tailwind-scrollbar": "^3.1.0", - - "tailwindcss": "^3.4.10", - - "tailwindcss-animate": "^1.0.7", - - "ts-prune": "^0.10.3", - - "ts-unused-exports": "^11.0.1", - - "unist-util-visit": "^5.0.0", - - "viem": "^2.21.56", - - "wagmi": "^2.14.3", - - "yup": "^1.4.0" - - }, - - "devDependencies": { - - "@babel/core": "^7.25.2", - - "@babel/plugin-proposal-class-properties": "^7.18.6", - - "@babel/plugin-transform-runtime": "^7.27.1", - - "@babel/preset-env": "^7.25.4", - - "@babel/preset-react": "^7.24.7", - - "@babel/preset-typescript": "^7.26.0", - - "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1", - - "@svgr/webpack": "^8.1.0", - - "@tanstack/router-plugin": "^1.120.16", - - "@testing-library/jest-dom": "^6.6.3", - - "@testing-library/react": "^16.2.0", - - "@testing-library/user-event": "^14.6.1", - - "@types/antd": "^1.0.0", - - "@types/html-to-text": "^9.0.4", - - "@types/human-crypto-keys": "^0.1.3", - - "@types/jest": "^29.5.14", - - "@types/jwk-to-pem": "^2.0.3", - - "@types/lodash": "^4.17.14", - - "@types/nprogress": "^0.2.3", - - "@types/pako": "^2.0.3", - - "@types/react": "^19.1.8", - - "@types/react-csv": "^1.1.10", - - "@types/react-dom": "^19.1.6", - - "@types/react-instantsearch-dom": "^6.12.8", - - "@types/react-lazy-load-image-component": "^1.6.4", - - "@types/react-responsive-masonry": "^2.1.3", - - "@types/react-syntax-highlighter": "^15.5.13", - - "@types/sjcl": "^1.0.34", - - "@types/text-encoding": "^0.0.39", - - "assert": "^2.1.0", - - "babel-jest": "^29.7.0", - - "buffer": "^6.0.3", - - "copy-webpack-plugin": "^12.0.2", - - "crypto-browserify": "^3.12.0", - - "css-loader": "^7.1.2", - - "dotenv": "^16.4.5", - - "events": "3.3.0", - - "file-loader": "^6.2.0", - - "html-webpack-plugin": "5.6.0", - - "https-browserify": "^1.0.0", - - "identity-obj-proxy": "^3.0.0", - - "imports-loader": "^5.0.0", - - "jest": "^29.7.0", - - "jest-environment-jsdom": "^29.7.0", - - "null-loader": "^4.0.1", - - "os-browserify": "^0.3.0", - - "postcss-loader": "^8.1.1", - - "postcss-nested": "^6.2.0", - - "process": "^0.11.10", - - "react-refresh": "^0.17.0", - - "stream-browserify": "^3.0.0", - - "stream-http": "^3.2.0", - - "style-loader": "^4.0.0", - - "terser-webpack-plugin": "^5.3.10", - - "ts-loader": "^9.5.1", - - "url": "^0.11.4", - - "util": "0.12.5", - - "vm-browserify": "^1.1.2", - - "webpack": "^5.94.0", - - "webpack-bundle-analyzer": "^4.10.2", - - "webpack-cli": "^5.1.4", - - "webpack-dev-server": "^5.1.0" - - }, - - "engines": { - - "node": "^12 || ^14 || ^16 || ^18 || ^20 || ^22" - - }, - - "browserslist": [ - - "last 2 chrome version", - - "last 2 firefox version", - - "last 2 safari version", - - "last 2 edge version" - - ] - + "name": "alex", + "workspaces": [ + "src/alex_frontend/*" + ], + "version": "0.1.0", + "description": "Alexandria workspace", + "keywords": [ + "Internet Computer", + "Motoko", + "JavaScript", + "Canister", + "Workspace" + ], + "scripts": { + "prebuild": "dfx generate", + "dev": "npm run dev --workspaces --if-present", + "test": "npm run test --workspaces --if-present", + "start": "npm run start --workspaces --if-present", + "build": "npm run build --workspaces --if-present", + "typecheck": "npm run typecheck --workspaces --if-present" + }, + "dependencies": { + "@apollo/client": "^3.11.8", + "@dfinity/agent": "^3.2.7", + "@dfinity/assets": "^3.2.7", + "@dfinity/auth-client": "^3.2.7", + "@dfinity/candid": "^3.2.7", + "@dfinity/identity": "^3.2.7", + "@dfinity/ledger-icp": "^6.0.1", + "@dfinity/principal": "^3.2.7", + "@dfinity/utils": "^3.1.0", + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", + "@irys/sdk": "^0.2.11", + "@meilisearch/instant-meilisearch": "^0.20.0", + "@radix-ui/react-accordion": "^1.2.2", + "@radix-ui/react-alert-dialog": "^1.1.2", + "@radix-ui/react-aspect-ratio": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.9", + "@radix-ui/react-checkbox": "^1.1.2", + "@radix-ui/react-collapsible": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.13", + "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-progress": "^1.1.1", + "@radix-ui/react-scroll-area": "^1.2.8", + "@radix-ui/react-select": "^2.1.4", + "@radix-ui/react-separator": "^1.1.1", + "@radix-ui/react-slider": "^1.2.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.1", + "@radix-ui/react-tabs": "^1.1.1", + "@radix-ui/react-toast": "^1.2.2", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.6", + "@reduxjs/toolkit": "^2.5.0", + "@slide-computer/signer": "^4.0.0", + "@slide-computer/signer-extension": "^3.20.0", + "@slide-computer/signer-web": "^4.0.0", + "@solana/wallet-adapter-base": "^0.9.23", + "@solana/wallet-adapter-react": "^0.15.35", + "@solana/wallet-adapter-react-ui": "^0.9.35", + "@tanstack/react-query": "^5.62.8", + "@tanstack/react-query-devtools": "^5.83.0", + "@tanstack/react-router": "^1.129.8", + "@tanstack/react-router-devtools": "^1.120.16", + "@tanstack/react-virtual": "^3.12.0", + "@tensorflow/tfjs": "^4.21.0", + "@types/dompurify": "^3.0.5", + "@types/react-beautiful-dnd": "^13.1.8", + "@xstate/store": "^3.9.2", + "antd": "^5.20.5", + "arweave": "^1.15.5", + "autoprefixer": "^10.4.20", + "axios": "^1.7.7", + "babel-loader": "^9.2.1", + "browserify-zlib": "^0.2.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "dompurify": "^3.2.3", + "echarts": "^5.6.0", + "epubjs": "^0.3.93", + "ethers": "^6.13.2", + "flatted": "^3.3.1", + "formik": "^2.4.6", + "graphql": "^16.11.0", + "html-to-text": "^9.0.5", + "human-crypto-keys": "^0.1.4", + "ic-mops": "^1.0.1", + "ic-use-actor": "^0.3.1", + "ic-vetkd-utils": "file:ic-vetkd-utils-0.1.0.tgz", + "instantsearch.css": "^8.5.0", + "instantsearch.js": "^4.74.0", + "jwk-to-pem": "^2.0.7", + "lodash": "^4.17.21", + "lru-cache": "^11.0.2", + "lucide-react": "^0.539.0", + "meilisearch": "^0.42.0", + "nanoid": "^5.0.7", + "next-themes": "^0.3.0", + "npm": "^10.8.3", + "nprogress": "^0.2.0", + "nsfwjs": "^4.2.0", + "papaparse": "^5.4.1", + "path": "^0.12.7", + "path-browserify": "^1.0.1", + "postcss": "^8.4.45", + "react": "^18.3.1", + "react-beautiful-dnd": "^13.1.1", + "react-circle-flags": "^0.0.20", + "react-csv": "^2.2.2", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-error-boundary": "^5.0.0", + "react-instantsearch-dom": "^6.40.4", + "react-intersection-observer": "^9.16.0", + "react-lazy-load-image-component": "^1.6.3", + "react-markdown": "^9.1.0", + "react-paginate": "^8.2.0", + "react-pdf": "^10.0.1", + "react-plock": "^3.5.1", + "react-qr-code": "^2.0.15", + "react-rating-star-with-type": "^1.2.2", + "react-redux": "^9.1.2", + "react-responsive-masonry": "^2.3.0", + "react-syntax-highlighter": "^15.6.1", + "react-viewer": "^3.2.2", + "react-zoom-pan-pinch": "^3.7.0", + "remark-gfm": "^4.0.1", + "root": "github:tanstack/react-virtual", + "sonner": "^1.5.0", + "styled-components": "^6.1.13", + "swiper": "^11.1.12", + "swr": "^2.3.4", + "tailwind-merge": "^2.5.3", + "tailwind-scrollbar": "^3.1.0", + "tailwindcss": "^3.4.10", + "tailwindcss-animate": "^1.0.7", + "ts-prune": "^0.10.3", + "ts-unused-exports": "^11.0.1", + "unist-util-visit": "^5.0.0", + "viem": "^2.21.56", + "wagmi": "^2.14.3", + "yup": "^1.4.0" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-transform-runtime": "^7.27.1", + "@babel/preset-env": "^7.25.4", + "@babel/preset-react": "^7.24.7", + "@babel/preset-typescript": "^7.26.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1", + "@svgr/webpack": "^8.1.0", + "@tanstack/router-plugin": "^1.120.16", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.1", + "@types/antd": "^1.0.0", + "@types/html-to-text": "^9.0.4", + "@types/human-crypto-keys": "^0.1.3", + "@types/jest": "^29.5.14", + "@types/jwk-to-pem": "^2.0.3", + "@types/lodash": "^4.17.14", + "@types/nprogress": "^0.2.3", + "@types/pako": "^2.0.3", + "@types/react": "^19.1.8", + "@types/react-csv": "^1.1.10", + "@types/react-dom": "^19.1.6", + "@types/react-instantsearch-dom": "^6.12.8", + "@types/react-lazy-load-image-component": "^1.6.4", + "@types/react-responsive-masonry": "^2.1.3", + "@types/react-syntax-highlighter": "^15.5.13", + "@types/sjcl": "^1.0.34", + "@types/text-encoding": "^0.0.39", + "assert": "^2.1.0", + "babel-jest": "^29.7.0", + "buffer": "^6.0.3", + "copy-webpack-plugin": "^12.0.2", + "crypto-browserify": "^3.12.0", + "css-loader": "^7.1.2", + "dotenv": "^16.4.5", + "events": "3.3.0", + "file-loader": "^6.2.0", + "html-webpack-plugin": "5.6.0", + "https-browserify": "^1.0.0", + "identity-obj-proxy": "^3.0.0", + "imports-loader": "^5.0.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "null-loader": "^4.0.1", + "os-browserify": "^0.3.0", + "postcss-loader": "^8.1.1", + "postcss-nested": "^6.2.0", + "process": "^0.11.10", + "react-refresh": "^0.17.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.10", + "ts-loader": "^9.5.1", + "url": "^0.11.4", + "util": "0.12.5", + "vm-browserify": "^1.1.2", + "webpack": "^5.94.0", + "webpack-bundle-analyzer": "^4.10.2", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0" + } } - diff --git a/src/alex_frontend/bibliotheca/index.tsx b/src/alex_frontend/bibliotheca/index.tsx new file mode 100644 index 000000000..af8e743b4 --- /dev/null +++ b/src/alex_frontend/bibliotheca/index.tsx @@ -0,0 +1,98 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +// import WebFont from "webfontloader"; +import App from "./src/App"; + +// Create a loading indicator +// const loadingIndicator = document.createElement('div'); +// loadingIndicator.id = 'app-loading-indicator'; +// loadingIndicator.innerHTML = ` +// +//
+//
Loading Alexandria...
+// `; +// document.body.appendChild(loadingIndicator); + +// // Function to remove the loading indicator +// function removeLoadingIndicator() { +// const indicator = document.getElementById('app-loading-indicator'); +// if (indicator) { +// indicator.classList.add('fade-out'); +// setTimeout(() => { +// if (indicator.parentNode) { +// indicator.parentNode.removeChild(indicator); +// } +// }, 300); +// } +// } + +// // Wrapper component to handle loading indicator removal +// const AppWithLoadingHandler = () => { +// useEffect(() => { +// // Remove loading indicator after component mounts +// // Using a small timeout to ensure the app has rendered +// const timer = setTimeout(() => { +// removeLoadingIndicator(); +// }, 100); + +// return () => clearTimeout(timer); +// }, []); + +// return ; +// }; + +// WebFont.load({ +// google: { +// families: ["Syne", "Roboto Condensed"], +// }, +// active: () => { +// console.log("Fonts loaded"); +// } +// }); + +document.addEventListener("DOMContentLoaded", () => { + const container = document.getElementById("root"); + if (container) { + createRoot(container).render( + + {/* */} + + + ); + } +}); \ No newline at end of file diff --git a/src/alex_frontend/bibliotheca/package.json b/src/alex_frontend/bibliotheca/package.json new file mode 100644 index 000000000..a0b9ed18b --- /dev/null +++ b/src/alex_frontend/bibliotheca/package.json @@ -0,0 +1,14 @@ +{ + "name": "bibliotheca", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "webpack serve --mode development --open", + "start": "webpack serve --mode development --env development", + "build": "NODE_ENV=production webpack --mode production", + "typecheck": "tsc --noEmit", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" + } +} diff --git a/postcss.config.js b/src/alex_frontend/bibliotheca/postcss.config.js similarity index 100% rename from postcss.config.js rename to src/alex_frontend/bibliotheca/postcss.config.js diff --git a/src/alex_frontend/public/.ic-assets.json b/src/alex_frontend/bibliotheca/public/.ic-assets.json similarity index 100% rename from src/alex_frontend/public/.ic-assets.json rename to src/alex_frontend/bibliotheca/public/.ic-assets.json diff --git a/src/alex_frontend/public/.well-known/ic-domains b/src/alex_frontend/bibliotheca/public/.well-known/ic-domains similarity index 100% rename from src/alex_frontend/public/.well-known/ic-domains rename to src/alex_frontend/bibliotheca/public/.well-known/ic-domains diff --git a/src/alex_frontend/public/.well-known/ii-alternative-origins b/src/alex_frontend/bibliotheca/public/.well-known/ii-alternative-origins similarity index 100% rename from src/alex_frontend/public/.well-known/ii-alternative-origins rename to src/alex_frontend/bibliotheca/public/.well-known/ii-alternative-origins diff --git a/src/alex_frontend/public/README.md b/src/alex_frontend/bibliotheca/public/README.md similarity index 100% rename from src/alex_frontend/public/README.md rename to src/alex_frontend/bibliotheca/public/README.md diff --git a/src/alex_frontend/public/audit.md b/src/alex_frontend/bibliotheca/public/audit.md similarity index 100% rename from src/alex_frontend/public/audit.md rename to src/alex_frontend/bibliotheca/public/audit.md diff --git a/src/alex_frontend/public/faq.md b/src/alex_frontend/bibliotheca/public/faq.md similarity index 100% rename from src/alex_frontend/public/faq.md rename to src/alex_frontend/bibliotheca/public/faq.md diff --git a/src/alex_frontend/public/favicon.ico b/src/alex_frontend/bibliotheca/public/favicon.ico similarity index 100% rename from src/alex_frontend/public/favicon.ico rename to src/alex_frontend/bibliotheca/public/favicon.ico diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/OFL.txt b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/OFL.txt similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/OFL.txt rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/OFL.txt diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/README.txt b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/README.txt similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/README.txt rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/README.txt diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/RobotoCondensed-Italic-VariableFont_wght.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/RobotoCondensed-Italic-VariableFont_wght.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/RobotoCondensed-Italic-VariableFont_wght.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/RobotoCondensed-Italic-VariableFont_wght.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/RobotoCondensed-VariableFont_wght.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/RobotoCondensed-VariableFont_wght.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/RobotoCondensed-VariableFont_wght.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/RobotoCondensed-VariableFont_wght.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Black.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Black.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Black.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Black.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-BlackItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-BlackItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-BlackItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-BlackItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Bold.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Bold.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Bold.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Bold.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-BoldItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-BoldItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-BoldItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-BoldItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBold.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBold.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBold.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBold.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBoldItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBoldItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBoldItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraBoldItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLight.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLight.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLight.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLight.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLightItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLightItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLightItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ExtraLightItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Italic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Italic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Italic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Italic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Light.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Light.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Light.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Light.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-LightItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-LightItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-LightItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-LightItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Medium.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Medium.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Medium.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Medium.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-MediumItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-MediumItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-MediumItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-MediumItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Regular.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Regular.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Regular.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Regular.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBold.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBold.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBold.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBold.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBoldItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBoldItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBoldItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-SemiBoldItalic.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Thin.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Thin.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-Thin.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-Thin.ttf diff --git a/src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ThinItalic.ttf b/src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ThinItalic.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Roboto_Condensed/static/RobotoCondensed-ThinItalic.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Roboto_Condensed/static/RobotoCondensed-ThinItalic.ttf diff --git a/src/alex_frontend/public/fonts/Syne/OFL.txt b/src/alex_frontend/bibliotheca/public/fonts/Syne/OFL.txt similarity index 100% rename from src/alex_frontend/public/fonts/Syne/OFL.txt rename to src/alex_frontend/bibliotheca/public/fonts/Syne/OFL.txt diff --git a/src/alex_frontend/public/fonts/Syne/README.txt b/src/alex_frontend/bibliotheca/public/fonts/Syne/README.txt similarity index 100% rename from src/alex_frontend/public/fonts/Syne/README.txt rename to src/alex_frontend/bibliotheca/public/fonts/Syne/README.txt diff --git a/src/alex_frontend/public/fonts/Syne/Syne-VariableFont_wght.ttf b/src/alex_frontend/bibliotheca/public/fonts/Syne/Syne-VariableFont_wght.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Syne/Syne-VariableFont_wght.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Syne/Syne-VariableFont_wght.ttf diff --git a/src/alex_frontend/public/fonts/Syne/static/Syne-Bold.ttf b/src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-Bold.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Syne/static/Syne-Bold.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-Bold.ttf diff --git a/src/alex_frontend/public/fonts/Syne/static/Syne-ExtraBold.ttf b/src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-ExtraBold.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Syne/static/Syne-ExtraBold.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-ExtraBold.ttf diff --git a/src/alex_frontend/public/fonts/Syne/static/Syne-Medium.ttf b/src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-Medium.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Syne/static/Syne-Medium.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-Medium.ttf diff --git a/src/alex_frontend/public/fonts/Syne/static/Syne-Regular.ttf b/src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-Regular.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Syne/static/Syne-Regular.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-Regular.ttf diff --git a/src/alex_frontend/public/fonts/Syne/static/Syne-SemiBold.ttf b/src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-SemiBold.ttf similarity index 100% rename from src/alex_frontend/public/fonts/Syne/static/Syne-SemiBold.ttf rename to src/alex_frontend/bibliotheca/public/fonts/Syne/static/Syne-SemiBold.ttf diff --git a/src/alex_frontend/public/icons/annotations.svg b/src/alex_frontend/bibliotheca/public/icons/annotations.svg similarity index 100% rename from src/alex_frontend/public/icons/annotations.svg rename to src/alex_frontend/bibliotheca/public/icons/annotations.svg diff --git a/src/alex_frontend/public/icons/book-horizontal.svg b/src/alex_frontend/bibliotheca/public/icons/book-horizontal.svg similarity index 100% rename from src/alex_frontend/public/icons/book-horizontal.svg rename to src/alex_frontend/bibliotheca/public/icons/book-horizontal.svg diff --git a/src/alex_frontend/public/icons/book-horizontal.svg:Zone.Identifier b/src/alex_frontend/bibliotheca/public/icons/book-horizontal.svg:Zone.Identifier similarity index 100% rename from src/alex_frontend/public/icons/book-horizontal.svg:Zone.Identifier rename to src/alex_frontend/bibliotheca/public/icons/book-horizontal.svg:Zone.Identifier diff --git a/src/alex_frontend/public/icons/book-vertical.svg b/src/alex_frontend/bibliotheca/public/icons/book-vertical.svg similarity index 100% rename from src/alex_frontend/public/icons/book-vertical.svg rename to src/alex_frontend/bibliotheca/public/icons/book-vertical.svg diff --git a/src/alex_frontend/public/icons/book-vertical.svg:Zone.Identifier b/src/alex_frontend/bibliotheca/public/icons/book-vertical.svg:Zone.Identifier similarity index 100% rename from src/alex_frontend/public/icons/book-vertical.svg:Zone.Identifier rename to src/alex_frontend/bibliotheca/public/icons/book-vertical.svg:Zone.Identifier diff --git a/src/alex_frontend/public/icons/bookmark-empty.svg b/src/alex_frontend/bibliotheca/public/icons/bookmark-empty.svg similarity index 100% rename from src/alex_frontend/public/icons/bookmark-empty.svg rename to src/alex_frontend/bibliotheca/public/icons/bookmark-empty.svg diff --git a/src/alex_frontend/public/icons/bookmark.svg b/src/alex_frontend/bibliotheca/public/icons/bookmark.svg similarity index 100% rename from src/alex_frontend/public/icons/bookmark.svg rename to src/alex_frontend/bibliotheca/public/icons/bookmark.svg diff --git a/src/alex_frontend/public/icons/bookmarks.svg b/src/alex_frontend/bibliotheca/public/icons/bookmarks.svg similarity index 100% rename from src/alex_frontend/public/icons/bookmarks.svg rename to src/alex_frontend/bibliotheca/public/icons/bookmarks.svg diff --git a/src/alex_frontend/public/icons/favicon.png b/src/alex_frontend/bibliotheca/public/icons/favicon.png similarity index 100% rename from src/alex_frontend/public/icons/favicon.png rename to src/alex_frontend/bibliotheca/public/icons/favicon.png diff --git a/src/alex_frontend/public/icons/favicon.svg b/src/alex_frontend/bibliotheca/public/icons/favicon.svg similarity index 100% rename from src/alex_frontend/public/icons/favicon.svg rename to src/alex_frontend/bibliotheca/public/icons/favicon.svg diff --git a/src/alex_frontend/public/icons/loader.gif b/src/alex_frontend/bibliotheca/public/icons/loader.gif similarity index 100% rename from src/alex_frontend/public/icons/loader.gif rename to src/alex_frontend/bibliotheca/public/icons/loader.gif diff --git a/src/alex_frontend/public/icons/open-book.svg b/src/alex_frontend/bibliotheca/public/icons/open-book.svg similarity index 100% rename from src/alex_frontend/public/icons/open-book.svg rename to src/alex_frontend/bibliotheca/public/icons/open-book.svg diff --git a/src/alex_frontend/public/icons/resize-full.svg b/src/alex_frontend/bibliotheca/public/icons/resize-full.svg similarity index 100% rename from src/alex_frontend/public/icons/resize-full.svg rename to src/alex_frontend/bibliotheca/public/icons/resize-full.svg diff --git a/src/alex_frontend/public/icons/resize-small.svg b/src/alex_frontend/bibliotheca/public/icons/resize-small.svg similarity index 100% rename from src/alex_frontend/public/icons/resize-small.svg rename to src/alex_frontend/bibliotheca/public/icons/resize-small.svg diff --git a/src/alex_frontend/public/icons/search.svg b/src/alex_frontend/bibliotheca/public/icons/search.svg similarity index 100% rename from src/alex_frontend/public/icons/search.svg rename to src/alex_frontend/bibliotheca/public/icons/search.svg diff --git a/src/alex_frontend/public/icons/settings.svg b/src/alex_frontend/bibliotheca/public/icons/settings.svg similarity index 100% rename from src/alex_frontend/public/icons/settings.svg rename to src/alex_frontend/bibliotheca/public/icons/settings.svg diff --git a/src/alex_frontend/public/icons/sidebar-open.svg b/src/alex_frontend/bibliotheca/public/icons/sidebar-open.svg similarity index 100% rename from src/alex_frontend/public/icons/sidebar-open.svg rename to src/alex_frontend/bibliotheca/public/icons/sidebar-open.svg diff --git a/src/alex_frontend/public/icons/sidebar.svg b/src/alex_frontend/bibliotheca/public/icons/sidebar.svg similarity index 100% rename from src/alex_frontend/public/icons/sidebar.svg rename to src/alex_frontend/bibliotheca/public/icons/sidebar.svg diff --git a/src/alex_frontend/public/icons/toc.svg b/src/alex_frontend/bibliotheca/public/icons/toc.svg similarity index 100% rename from src/alex_frontend/public/icons/toc.svg rename to src/alex_frontend/bibliotheca/public/icons/toc.svg diff --git a/src/alex_frontend/public/icons/upload.svg b/src/alex_frontend/bibliotheca/public/icons/upload.svg similarity index 100% rename from src/alex_frontend/public/icons/upload.svg rename to src/alex_frontend/bibliotheca/public/icons/upload.svg diff --git a/src/alex_frontend/public/images/ index.d.ts b/src/alex_frontend/bibliotheca/public/images/ index.d.ts similarity index 100% rename from src/alex_frontend/public/images/ index.d.ts rename to src/alex_frontend/bibliotheca/public/images/ index.d.ts diff --git a/src/alex_frontend/public/images/8-logo.png b/src/alex_frontend/bibliotheca/public/images/8-logo.png similarity index 100% rename from src/alex_frontend/public/images/8-logo.png rename to src/alex_frontend/bibliotheca/public/images/8-logo.png diff --git a/src/alex_frontend/public/images/ALEX Logo.svg b/src/alex_frontend/bibliotheca/public/images/ALEX Logo.svg similarity index 100% rename from src/alex_frontend/public/images/ALEX Logo.svg rename to src/alex_frontend/bibliotheca/public/images/ALEX Logo.svg diff --git a/src/alex_frontend/public/images/LBRY Logo.svg b/src/alex_frontend/bibliotheca/public/images/LBRY Logo.svg similarity index 100% rename from src/alex_frontend/public/images/LBRY Logo.svg rename to src/alex_frontend/bibliotheca/public/images/LBRY Logo.svg diff --git a/src/alex_frontend/public/images/NFT Logo 1 (2).svg b/src/alex_frontend/bibliotheca/public/images/NFT Logo 1 (2).svg similarity index 100% rename from src/alex_frontend/public/images/NFT Logo 1 (2).svg rename to src/alex_frontend/bibliotheca/public/images/NFT Logo 1 (2).svg diff --git a/src/alex_frontend/public/images/NFT Logo 2.svg b/src/alex_frontend/bibliotheca/public/images/NFT Logo 2.svg similarity index 100% rename from src/alex_frontend/public/images/NFT Logo 2.svg rename to src/alex_frontend/bibliotheca/public/images/NFT Logo 2.svg diff --git a/src/alex_frontend/public/images/Vector.png b/src/alex_frontend/bibliotheca/public/images/Vector.png similarity index 100% rename from src/alex_frontend/public/images/Vector.png rename to src/alex_frontend/bibliotheca/public/images/Vector.png diff --git a/src/alex_frontend/public/images/alex-logo.svg b/src/alex_frontend/bibliotheca/public/images/alex-logo.svg similarity index 100% rename from src/alex_frontend/public/images/alex-logo.svg rename to src/alex_frontend/bibliotheca/public/images/alex-logo.svg diff --git a/src/alex_frontend/public/images/arweave.svg b/src/alex_frontend/bibliotheca/public/images/arweave.svg similarity index 100% rename from src/alex_frontend/public/images/arweave.svg rename to src/alex_frontend/bibliotheca/public/images/arweave.svg diff --git a/src/alex_frontend/public/images/audit_header.png b/src/alex_frontend/bibliotheca/public/images/audit_header.png similarity index 100% rename from src/alex_frontend/public/images/audit_header.png rename to src/alex_frontend/bibliotheca/public/images/audit_header.png diff --git a/src/alex_frontend/public/images/books/1984.png b/src/alex_frontend/bibliotheca/public/images/books/1984.png similarity index 100% rename from src/alex_frontend/public/images/books/1984.png rename to src/alex_frontend/bibliotheca/public/images/books/1984.png diff --git a/src/alex_frontend/public/images/books/brave-new-world.png b/src/alex_frontend/bibliotheca/public/images/books/brave-new-world.png similarity index 100% rename from src/alex_frontend/public/images/books/brave-new-world.png rename to src/alex_frontend/bibliotheca/public/images/books/brave-new-world.png diff --git a/src/alex_frontend/public/images/books/meditations.png b/src/alex_frontend/bibliotheca/public/images/books/meditations.png similarity index 100% rename from src/alex_frontend/public/images/books/meditations.png rename to src/alex_frontend/bibliotheca/public/images/books/meditations.png diff --git a/src/alex_frontend/public/images/books/placeholder-cover.png b/src/alex_frontend/bibliotheca/public/images/books/placeholder-cover.png similarity index 100% rename from src/alex_frontend/public/images/books/placeholder-cover.png rename to src/alex_frontend/bibliotheca/public/images/books/placeholder-cover.png diff --git a/src/alex_frontend/public/images/books/sapiens.png b/src/alex_frontend/bibliotheca/public/images/books/sapiens.png similarity index 100% rename from src/alex_frontend/public/images/books/sapiens.png rename to src/alex_frontend/bibliotheca/public/images/books/sapiens.png diff --git a/src/alex_frontend/public/images/categories/art-and-recreation.png b/src/alex_frontend/bibliotheca/public/images/categories/art-and-recreation.png similarity index 100% rename from src/alex_frontend/public/images/categories/art-and-recreation.png rename to src/alex_frontend/bibliotheca/public/images/categories/art-and-recreation.png diff --git a/src/alex_frontend/public/images/categories/generalities-and-it.png b/src/alex_frontend/bibliotheca/public/images/categories/generalities-and-it.png similarity index 100% rename from src/alex_frontend/public/images/categories/generalities-and-it.png rename to src/alex_frontend/bibliotheca/public/images/categories/generalities-and-it.png diff --git a/src/alex_frontend/public/images/categories/high_res/art-and-recreation_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/art-and-recreation_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/art-and-recreation_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/art-and-recreation_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/generalities-and-it_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/generalities-and-it_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/generalities-and-it_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/generalities-and-it_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/history-and-geography_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/history-and-geography_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/history-and-geography_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/history-and-geography_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/language_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/language_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/language_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/language_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/literature_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/literature_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/literature_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/literature_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/philosophy_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/philosophy_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/philosophy_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/philosophy_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/religion_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/religion_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/religion_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/religion_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/science_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/science_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/science_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/science_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/social-sciences_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/social-sciences_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/social-sciences_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/social-sciences_high-res.png diff --git a/src/alex_frontend/public/images/categories/high_res/technology_high-res.png b/src/alex_frontend/bibliotheca/public/images/categories/high_res/technology_high-res.png similarity index 100% rename from src/alex_frontend/public/images/categories/high_res/technology_high-res.png rename to src/alex_frontend/bibliotheca/public/images/categories/high_res/technology_high-res.png diff --git a/src/alex_frontend/public/images/categories/history-and-geography.png b/src/alex_frontend/bibliotheca/public/images/categories/history-and-geography.png similarity index 100% rename from src/alex_frontend/public/images/categories/history-and-geography.png rename to src/alex_frontend/bibliotheca/public/images/categories/history-and-geography.png diff --git a/src/alex_frontend/public/images/categories/language.png b/src/alex_frontend/bibliotheca/public/images/categories/language.png similarity index 100% rename from src/alex_frontend/public/images/categories/language.png rename to src/alex_frontend/bibliotheca/public/images/categories/language.png diff --git a/src/alex_frontend/public/images/categories/literature.png b/src/alex_frontend/bibliotheca/public/images/categories/literature.png similarity index 100% rename from src/alex_frontend/public/images/categories/literature.png rename to src/alex_frontend/bibliotheca/public/images/categories/literature.png diff --git a/src/alex_frontend/public/images/categories/philosophy.png b/src/alex_frontend/bibliotheca/public/images/categories/philosophy.png similarity index 100% rename from src/alex_frontend/public/images/categories/philosophy.png rename to src/alex_frontend/bibliotheca/public/images/categories/philosophy.png diff --git a/src/alex_frontend/public/images/categories/religion.png b/src/alex_frontend/bibliotheca/public/images/categories/religion.png similarity index 100% rename from src/alex_frontend/public/images/categories/religion.png rename to src/alex_frontend/bibliotheca/public/images/categories/religion.png diff --git a/src/alex_frontend/public/images/categories/science.png b/src/alex_frontend/bibliotheca/public/images/categories/science.png similarity index 100% rename from src/alex_frontend/public/images/categories/science.png rename to src/alex_frontend/bibliotheca/public/images/categories/science.png diff --git a/src/alex_frontend/public/images/categories/social-sciences.png b/src/alex_frontend/bibliotheca/public/images/categories/social-sciences.png similarity index 100% rename from src/alex_frontend/public/images/categories/social-sciences.png rename to src/alex_frontend/bibliotheca/public/images/categories/social-sciences.png diff --git a/src/alex_frontend/public/images/categories/technology.png b/src/alex_frontend/bibliotheca/public/images/categories/technology.png similarity index 100% rename from src/alex_frontend/public/images/categories/technology.png rename to src/alex_frontend/bibliotheca/public/images/categories/technology.png diff --git a/src/alex_frontend/public/images/default-cover.jpg b/src/alex_frontend/bibliotheca/public/images/default-cover.jpg similarity index 100% rename from src/alex_frontend/public/images/default-cover.jpg rename to src/alex_frontend/bibliotheca/public/images/default-cover.jpg diff --git a/src/alex_frontend/public/images/error.png b/src/alex_frontend/bibliotheca/public/images/error.png similarity index 100% rename from src/alex_frontend/public/images/error.png rename to src/alex_frontend/bibliotheca/public/images/error.png diff --git a/src/alex_frontend/public/images/ethereum.svg b/src/alex_frontend/bibliotheca/public/images/ethereum.svg similarity index 100% rename from src/alex_frontend/public/images/ethereum.svg rename to src/alex_frontend/bibliotheca/public/images/ethereum.svg diff --git a/src/alex_frontend/public/images/gradient-bg.png b/src/alex_frontend/bibliotheca/public/images/gradient-bg.png similarity index 100% rename from src/alex_frontend/public/images/gradient-bg.png rename to src/alex_frontend/bibliotheca/public/images/gradient-bg.png diff --git a/src/alex_frontend/public/images/header-logo.png b/src/alex_frontend/bibliotheca/public/images/header-logo.png similarity index 100% rename from src/alex_frontend/public/images/header-logo.png rename to src/alex_frontend/bibliotheca/public/images/header-logo.png diff --git a/src/alex_frontend/public/images/ic.svg b/src/alex_frontend/bibliotheca/public/images/ic.svg similarity index 100% rename from src/alex_frontend/public/images/ic.svg rename to src/alex_frontend/bibliotheca/public/images/ic.svg diff --git a/src/alex_frontend/public/images/icp-logo.png b/src/alex_frontend/bibliotheca/public/images/icp-logo.png similarity index 100% rename from src/alex_frontend/public/images/icp-logo.png rename to src/alex_frontend/bibliotheca/public/images/icp-logo.png diff --git a/src/alex_frontend/public/images/lbry-logo.svg b/src/alex_frontend/bibliotheca/public/images/lbry-logo.svg similarity index 100% rename from src/alex_frontend/public/images/lbry-logo.svg rename to src/alex_frontend/bibliotheca/public/images/lbry-logo.svg diff --git a/src/alex_frontend/public/images/logo.png b/src/alex_frontend/bibliotheca/public/images/logo.png similarity index 100% rename from src/alex_frontend/public/images/logo.png rename to src/alex_frontend/bibliotheca/public/images/logo.png diff --git a/src/alex_frontend/public/images/menu.svg b/src/alex_frontend/bibliotheca/public/images/menu.svg similarity index 100% rename from src/alex_frontend/public/images/menu.svg rename to src/alex_frontend/bibliotheca/public/images/menu.svg diff --git a/src/alex_frontend/public/images/nfid-logo.png b/src/alex_frontend/bibliotheca/public/images/nfid-logo.png similarity index 100% rename from src/alex_frontend/public/images/nfid-logo.png rename to src/alex_frontend/bibliotheca/public/images/nfid-logo.png diff --git a/src/alex_frontend/public/images/no-file.png b/src/alex_frontend/bibliotheca/public/images/no-file.png similarity index 100% rename from src/alex_frontend/public/images/no-file.png rename to src/alex_frontend/bibliotheca/public/images/no-file.png diff --git a/src/alex_frontend/public/images/oisy-logo.svg b/src/alex_frontend/bibliotheca/public/images/oisy-logo.svg similarity index 100% rename from src/alex_frontend/public/images/oisy-logo.svg rename to src/alex_frontend/bibliotheca/public/images/oisy-logo.svg diff --git a/src/alex_frontend/public/images/plug-logo.png b/src/alex_frontend/bibliotheca/public/images/plug-logo.png similarity index 100% rename from src/alex_frontend/public/images/plug-logo.png rename to src/alex_frontend/bibliotheca/public/images/plug-logo.png diff --git a/src/alex_frontend/public/images/question-mark.svg b/src/alex_frontend/bibliotheca/public/images/question-mark.svg similarity index 100% rename from src/alex_frontend/public/images/question-mark.svg rename to src/alex_frontend/bibliotheca/public/images/question-mark.svg diff --git a/src/alex_frontend/public/images/solana.svg b/src/alex_frontend/bibliotheca/public/images/solana.svg similarity index 100% rename from src/alex_frontend/public/images/solana.svg rename to src/alex_frontend/bibliotheca/public/images/solana.svg diff --git a/src/alex_frontend/public/images/tick.png b/src/alex_frontend/bibliotheca/public/images/tick.png similarity index 100% rename from src/alex_frontend/public/images/tick.png rename to src/alex_frontend/bibliotheca/public/images/tick.png diff --git a/src/alex_frontend/public/images/usd.svg b/src/alex_frontend/bibliotheca/public/images/usd.svg similarity index 100% rename from src/alex_frontend/public/images/usd.svg rename to src/alex_frontend/bibliotheca/public/images/usd.svg diff --git a/src/alex_frontend/public/images/walletconnect.svg b/src/alex_frontend/bibliotheca/public/images/walletconnect.svg similarity index 100% rename from src/alex_frontend/public/images/walletconnect.svg rename to src/alex_frontend/bibliotheca/public/images/walletconnect.svg diff --git a/src/alex_frontend/public/index.html b/src/alex_frontend/bibliotheca/public/index.html similarity index 100% rename from src/alex_frontend/public/index.html rename to src/alex_frontend/bibliotheca/public/index.html diff --git a/src/alex_frontend/public/introduction/index.html b/src/alex_frontend/bibliotheca/public/introduction/index.html similarity index 100% rename from src/alex_frontend/public/introduction/index.html rename to src/alex_frontend/bibliotheca/public/introduction/index.html diff --git a/src/alex_frontend/public/introduction/particles.css b/src/alex_frontend/bibliotheca/public/introduction/particles.css similarity index 100% rename from src/alex_frontend/public/introduction/particles.css rename to src/alex_frontend/bibliotheca/public/introduction/particles.css diff --git a/src/alex_frontend/public/introduction/styles.css b/src/alex_frontend/bibliotheca/public/introduction/styles.css similarity index 100% rename from src/alex_frontend/public/introduction/styles.css rename to src/alex_frontend/bibliotheca/public/introduction/styles.css diff --git a/src/alex_frontend/public/js/libs/epub.min.js b/src/alex_frontend/bibliotheca/public/js/libs/epub.min.js similarity index 100% rename from src/alex_frontend/public/js/libs/epub.min.js rename to src/alex_frontend/bibliotheca/public/js/libs/epub.min.js diff --git a/src/alex_frontend/public/js/libs/jszip.min.js b/src/alex_frontend/bibliotheca/public/js/libs/jszip.min.js similarity index 100% rename from src/alex_frontend/public/js/libs/jszip.min.js rename to src/alex_frontend/bibliotheca/public/js/libs/jszip.min.js diff --git a/src/alex_frontend/public/js/libs/jszip.min.js.LICENSE.txt b/src/alex_frontend/bibliotheca/public/js/libs/jszip.min.js.LICENSE.txt similarity index 100% rename from src/alex_frontend/public/js/libs/jszip.min.js.LICENSE.txt rename to src/alex_frontend/bibliotheca/public/js/libs/jszip.min.js.LICENSE.txt diff --git a/src/alex_frontend/public/js/libs/md5.min.js b/src/alex_frontend/bibliotheca/public/js/libs/md5.min.js similarity index 100% rename from src/alex_frontend/public/js/libs/md5.min.js rename to src/alex_frontend/bibliotheca/public/js/libs/md5.min.js diff --git a/src/alex_frontend/public/js/libs/md5.min.js.LICENSE.txt b/src/alex_frontend/bibliotheca/public/js/libs/md5.min.js.LICENSE.txt similarity index 100% rename from src/alex_frontend/public/js/libs/md5.min.js.LICENSE.txt rename to src/alex_frontend/bibliotheca/public/js/libs/md5.min.js.LICENSE.txt diff --git a/src/alex_frontend/public/logos/Alexandrian.svg b/src/alex_frontend/bibliotheca/public/logos/Alexandrian.svg similarity index 100% rename from src/alex_frontend/public/logos/Alexandrian.svg rename to src/alex_frontend/bibliotheca/public/logos/Alexandrian.svg diff --git a/src/alex_frontend/public/logos/Emporium.svg b/src/alex_frontend/bibliotheca/public/logos/Emporium.svg similarity index 100% rename from src/alex_frontend/public/logos/Emporium.svg rename to src/alex_frontend/bibliotheca/public/logos/Emporium.svg diff --git a/src/alex_frontend/public/logos/Permasearch.svg b/src/alex_frontend/bibliotheca/public/logos/Permasearch.svg similarity index 100% rename from src/alex_frontend/public/logos/Permasearch.svg rename to src/alex_frontend/bibliotheca/public/logos/Permasearch.svg diff --git a/src/alex_frontend/public/logos/Perpetua.svg b/src/alex_frontend/bibliotheca/public/logos/Perpetua.svg similarity index 100% rename from src/alex_frontend/public/logos/Perpetua.svg rename to src/alex_frontend/bibliotheca/public/logos/Perpetua.svg diff --git a/src/alex_frontend/public/logos/Pinax.svg b/src/alex_frontend/bibliotheca/public/logos/Pinax.svg similarity index 100% rename from src/alex_frontend/public/logos/Pinax.svg rename to src/alex_frontend/bibliotheca/public/logos/Pinax.svg diff --git a/src/alex_frontend/public/logos/Syllogos.svg b/src/alex_frontend/bibliotheca/public/logos/Syllogos.svg similarity index 100% rename from src/alex_frontend/public/logos/Syllogos.svg rename to src/alex_frontend/bibliotheca/public/logos/Syllogos.svg diff --git a/src/alex_frontend/public/logos/alex.png b/src/alex_frontend/bibliotheca/public/logos/alex.png similarity index 100% rename from src/alex_frontend/public/logos/alex.png rename to src/alex_frontend/bibliotheca/public/logos/alex.png diff --git a/src/alex_frontend/public/logos/icrc7.png b/src/alex_frontend/bibliotheca/public/logos/icrc7.png similarity index 100% rename from src/alex_frontend/public/logos/icrc7.png rename to src/alex_frontend/bibliotheca/public/logos/icrc7.png diff --git a/src/alex_frontend/public/logos/icrc7_scion.png b/src/alex_frontend/bibliotheca/public/logos/icrc7_scion.png similarity index 100% rename from src/alex_frontend/public/logos/icrc7_scion.png rename to src/alex_frontend/bibliotheca/public/logos/icrc7_scion.png diff --git a/src/alex_frontend/public/logos/lbry.png b/src/alex_frontend/bibliotheca/public/logos/lbry.png similarity index 100% rename from src/alex_frontend/public/logos/lbry.png rename to src/alex_frontend/bibliotheca/public/logos/lbry.png diff --git a/src/alex_frontend/public/logos/third_party/daopad_logo.png b/src/alex_frontend/bibliotheca/public/logos/third_party/daopad_logo.png similarity index 100% rename from src/alex_frontend/public/logos/third_party/daopad_logo.png rename to src/alex_frontend/bibliotheca/public/logos/third_party/daopad_logo.png diff --git a/src/alex_frontend/public/logos/third_party/kong_locker.png b/src/alex_frontend/bibliotheca/public/logos/third_party/kong_locker.png similarity index 100% rename from src/alex_frontend/public/logos/third_party/kong_locker.png rename to src/alex_frontend/bibliotheca/public/logos/third_party/kong_locker.png diff --git a/src/alex_frontend/public/logos/third_party/lbry_fun.svg b/src/alex_frontend/bibliotheca/public/logos/third_party/lbry_fun.svg similarity index 100% rename from src/alex_frontend/public/logos/third_party/lbry_fun.svg rename to src/alex_frontend/bibliotheca/public/logos/third_party/lbry_fun.svg diff --git a/src/alex_frontend/public/models/mobilenet_v2_mid/group1-shard1of2 b/src/alex_frontend/bibliotheca/public/models/mobilenet_v2_mid/group1-shard1of2 similarity index 100% rename from src/alex_frontend/public/models/mobilenet_v2_mid/group1-shard1of2 rename to src/alex_frontend/bibliotheca/public/models/mobilenet_v2_mid/group1-shard1of2 diff --git a/src/alex_frontend/public/models/mobilenet_v2_mid/group1-shard2of2 b/src/alex_frontend/bibliotheca/public/models/mobilenet_v2_mid/group1-shard2of2 similarity index 100% rename from src/alex_frontend/public/models/mobilenet_v2_mid/group1-shard2of2 rename to src/alex_frontend/bibliotheca/public/models/mobilenet_v2_mid/group1-shard2of2 diff --git a/src/alex_frontend/public/models/mobilenet_v2_mid/model.json b/src/alex_frontend/bibliotheca/public/models/mobilenet_v2_mid/model.json similarity index 100% rename from src/alex_frontend/public/models/mobilenet_v2_mid/model.json rename to src/alex_frontend/bibliotheca/public/models/mobilenet_v2_mid/model.json diff --git a/src/alex_frontend/public/permsearch_notes.md b/src/alex_frontend/bibliotheca/public/permsearch_notes.md similarity index 100% rename from src/alex_frontend/public/permsearch_notes.md rename to src/alex_frontend/bibliotheca/public/permsearch_notes.md diff --git a/src/alex_frontend/public/tailwind.css b/src/alex_frontend/bibliotheca/public/tailwind.css similarity index 100% rename from src/alex_frontend/public/tailwind.css rename to src/alex_frontend/bibliotheca/public/tailwind.css diff --git a/src/alex_frontend/public/test.epub b/src/alex_frontend/bibliotheca/public/test.epub similarity index 100% rename from src/alex_frontend/public/test.epub rename to src/alex_frontend/bibliotheca/public/test.epub diff --git a/src/alex_frontend/bibliotheca/routeTree.gen.ts b/src/alex_frontend/bibliotheca/routeTree.gen.ts new file mode 100644 index 000000000..a151acd8a --- /dev/null +++ b/src/alex_frontend/bibliotheca/routeTree.gen.ts @@ -0,0 +1,255 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { createFileRoute } from '@tanstack/react-router' + +import { Route as rootRouteImport } from './src/routes/__root' +import { Route as IndexRouteImport } from './src/routes/index' +import { Route as AuthUploadRouteImport } from './src/routes/_auth/upload' +import { Route as AuthShelfRouteImport } from './src/routes/_auth/shelf' +import { Route as AuthMarketRouteImport } from './src/routes/_auth/market' +import { Route as AuthLibraryRouteImport } from './src/routes/_auth/library' +import { Route as AuthBrowseRouteImport } from './src/routes/_auth/browse' +import { Route as AuthStudioPrincipalRouteImport } from './src/routes/_auth/studio.$principal' +import { Route as AuthArchivePrincipalRouteImport } from './src/routes/_auth/archive.$principal' + +const AuthRouteLazyRouteImport = createFileRoute('/_auth')() + +const AuthRouteLazyRoute = AuthRouteLazyRouteImport.update({ + id: '/_auth', + getParentRoute: () => rootRouteImport, +} as any).lazy(() => + import('./src/routes/_auth/route.lazy').then((d) => d.Route), +) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any).lazy(() => import('./src/routes/index.lazy').then((d) => d.Route)) +const AuthUploadRoute = AuthUploadRouteImport.update({ + id: '/upload', + path: '/upload', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/upload.lazy').then((d) => d.Route), +) +const AuthShelfRoute = AuthShelfRouteImport.update({ + id: '/shelf', + path: '/shelf', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/shelf.lazy').then((d) => d.Route), +) +const AuthMarketRoute = AuthMarketRouteImport.update({ + id: '/market', + path: '/market', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/market.lazy').then((d) => d.Route), +) +const AuthLibraryRoute = AuthLibraryRouteImport.update({ + id: '/library', + path: '/library', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/library.lazy').then((d) => d.Route), +) +const AuthBrowseRoute = AuthBrowseRouteImport.update({ + id: '/browse', + path: '/browse', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/browse.lazy').then((d) => d.Route), +) +const AuthStudioPrincipalRoute = AuthStudioPrincipalRouteImport.update({ + id: '/studio/$principal', + path: '/studio/$principal', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/studio.$principal.lazy').then((d) => d.Route), +) +const AuthArchivePrincipalRoute = AuthArchivePrincipalRouteImport.update({ + id: '/archive/$principal', + path: '/archive/$principal', + getParentRoute: () => AuthRouteLazyRoute, +} as any).lazy(() => + import('./src/routes/_auth/archive.$principal.lazy').then((d) => d.Route), +) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/browse': typeof AuthBrowseRoute + '/library': typeof AuthLibraryRoute + '/market': typeof AuthMarketRoute + '/shelf': typeof AuthShelfRoute + '/upload': typeof AuthUploadRoute + '/archive/$principal': typeof AuthArchivePrincipalRoute + '/studio/$principal': typeof AuthStudioPrincipalRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/browse': typeof AuthBrowseRoute + '/library': typeof AuthLibraryRoute + '/market': typeof AuthMarketRoute + '/shelf': typeof AuthShelfRoute + '/upload': typeof AuthUploadRoute + '/archive/$principal': typeof AuthArchivePrincipalRoute + '/studio/$principal': typeof AuthStudioPrincipalRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/_auth': typeof AuthRouteLazyRouteWithChildren + '/_auth/browse': typeof AuthBrowseRoute + '/_auth/library': typeof AuthLibraryRoute + '/_auth/market': typeof AuthMarketRoute + '/_auth/shelf': typeof AuthShelfRoute + '/_auth/upload': typeof AuthUploadRoute + '/_auth/archive/$principal': typeof AuthArchivePrincipalRoute + '/_auth/studio/$principal': typeof AuthStudioPrincipalRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/browse' + | '/library' + | '/market' + | '/shelf' + | '/upload' + | '/archive/$principal' + | '/studio/$principal' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/browse' + | '/library' + | '/market' + | '/shelf' + | '/upload' + | '/archive/$principal' + | '/studio/$principal' + id: + | '__root__' + | '/' + | '/_auth' + | '/_auth/browse' + | '/_auth/library' + | '/_auth/market' + | '/_auth/shelf' + | '/_auth/upload' + | '/_auth/archive/$principal' + | '/_auth/studio/$principal' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AuthRouteLazyRoute: typeof AuthRouteLazyRouteWithChildren +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/_auth': { + id: '/_auth' + path: '' + fullPath: '' + preLoaderRoute: typeof AuthRouteLazyRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/_auth/upload': { + id: '/_auth/upload' + path: '/upload' + fullPath: '/upload' + preLoaderRoute: typeof AuthUploadRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + '/_auth/shelf': { + id: '/_auth/shelf' + path: '/shelf' + fullPath: '/shelf' + preLoaderRoute: typeof AuthShelfRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + '/_auth/market': { + id: '/_auth/market' + path: '/market' + fullPath: '/market' + preLoaderRoute: typeof AuthMarketRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + '/_auth/library': { + id: '/_auth/library' + path: '/library' + fullPath: '/library' + preLoaderRoute: typeof AuthLibraryRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + '/_auth/browse': { + id: '/_auth/browse' + path: '/browse' + fullPath: '/browse' + preLoaderRoute: typeof AuthBrowseRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + '/_auth/studio/$principal': { + id: '/_auth/studio/$principal' + path: '/studio/$principal' + fullPath: '/studio/$principal' + preLoaderRoute: typeof AuthStudioPrincipalRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + '/_auth/archive/$principal': { + id: '/_auth/archive/$principal' + path: '/archive/$principal' + fullPath: '/archive/$principal' + preLoaderRoute: typeof AuthArchivePrincipalRouteImport + parentRoute: typeof AuthRouteLazyRoute + } + } +} + +interface AuthRouteLazyRouteChildren { + AuthBrowseRoute: typeof AuthBrowseRoute + AuthLibraryRoute: typeof AuthLibraryRoute + AuthMarketRoute: typeof AuthMarketRoute + AuthShelfRoute: typeof AuthShelfRoute + AuthUploadRoute: typeof AuthUploadRoute + AuthArchivePrincipalRoute: typeof AuthArchivePrincipalRoute + AuthStudioPrincipalRoute: typeof AuthStudioPrincipalRoute +} + +const AuthRouteLazyRouteChildren: AuthRouteLazyRouteChildren = { + AuthBrowseRoute: AuthBrowseRoute, + AuthLibraryRoute: AuthLibraryRoute, + AuthMarketRoute: AuthMarketRoute, + AuthShelfRoute: AuthShelfRoute, + AuthUploadRoute: AuthUploadRoute, + AuthArchivePrincipalRoute: AuthArchivePrincipalRoute, + AuthStudioPrincipalRoute: AuthStudioPrincipalRoute, +} + +const AuthRouteLazyRouteWithChildren = AuthRouteLazyRoute._addFileChildren( + AuthRouteLazyRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AuthRouteLazyRoute: AuthRouteLazyRouteWithChildren, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/src/alex_frontend/bibliotheca/src/App.tsx b/src/alex_frontend/bibliotheca/src/App.tsx new file mode 100644 index 000000000..60beac49e --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/App.tsx @@ -0,0 +1,109 @@ +import React, { useRef, useEffect } from "react"; +import { ErrorBoundary } from "react-error-boundary"; +import ReduxProvider from "@/providers/ReduxProvider"; + +import "../tailwind.css"; +// import SessionProvider from "./providers/SessionProvider"; + +import NProgress from "nprogress"; + +// import "./styles/style.css"; + +import "nprogress/nprogress.css"; + +import UserProvider from "@/providers/UserProvider"; +import { ThemeProvider } from "@/providers/ThemeProvider"; +import ActorProvider from "@/providers/ActorProvider"; +import { IdentityProvider } from "@/lib/ic-use-identity"; +import NsfwProvider from "@/providers/NsfwProvider"; +import ErrorFallback from "@/components/fallbacks/ErrorFallback"; + + +import { RouterProvider, createRouter } from '@tanstack/react-router' +import { routeTree } from "../routeTree.gen"; +import { SWRConfig } from 'swr'; +import ContentLoadingSpinner from "@/components/ContentLoadingSpinner"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { TooltipProvider } from "@/lib/components/tooltip"; + + + +// Create a client +const queryClient = new QueryClient() + +// Create a new router instance +const router = createRouter({ + routeTree, + defaultPendingComponent: ContentLoadingSpinner, + defaultPendingMs: 0, // Show immediately + defaultPendingMinMs: 0 // Keep visible for 0ms minimum +}) + +// Register the router instance for type safety +declare module '@tanstack/react-router' { + interface Register { + router: typeof router + } +} + +// Subscribe to events +router.subscribe('onBeforeLoad', ({pathChanged}) => { + if (pathChanged) { + NProgress.start(); + // TanStack Query automatically handles request cancellation on route changes + // Both SWR and TanStack Query caches are preserved for performance benefits + } +}); +router.subscribe('onLoad', () => NProgress.done()) + +export default function App() { + // const [isReady, setIsReady] = useState(false); + + // // useEffect(() => { + // // const introduced = localStorage.getItem('IntroductionShown'); + // // if(!introduced) window.location.href = "/introduction" + // // }, []); + + + // // costs a re render + // // Mark the app as ready after a short delay to ensure all providers are initialized + // useEffect(() => { + // const timer = setTimeout(() => { + // setIsReady(true); + // console.log("App is ready"); + // }, 100); + + // return () => clearTimeout(timer); + // }, []); + + return ( + + + + + + + + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/src/alex_frontend/bibliotheca/src/components/Logo.tsx b/src/alex_frontend/bibliotheca/src/components/Logo.tsx new file mode 100644 index 000000000..403c079a8 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/components/Logo.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Link } from "@tanstack/react-router"; + +interface LogoProps { + className?: string; +} + +function Logo({ className = "" }: LogoProps) { + return ( + +
+ + Bibliotheca + + + powered by Alexandria + +
+ + ); +} + +export default Logo; diff --git a/src/alex_frontend/bibliotheca/src/components/Tabs.tsx b/src/alex_frontend/bibliotheca/src/components/Tabs.tsx new file mode 100644 index 000000000..9bc2b8a71 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/components/Tabs.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { Link } from "@tanstack/react-router"; + +export default function Tabs() { + return ( +
+ + Browse + + + + Upload + + + + Library + + + Shop + +
+ ); +} diff --git a/src/alex_frontend/bibliotheca/src/layout/BaseLayout.tsx b/src/alex_frontend/bibliotheca/src/layout/BaseLayout.tsx new file mode 100644 index 000000000..d1c212a00 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/layout/BaseLayout.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { Outlet } from "@tanstack/react-router"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; + +import { Toaster } from "@/lib/components/sonner"; +import { useRiskWarning } from "@/hooks/useRiskWarning"; +import RiskWarningModal from "@/components/RiskWarningModal"; + +import Header from "./Header"; + +const BaseLayout = () => { + const { showRiskWarning, handleCloseRiskWarning } = useRiskWarning(); + + return ( +
+ {showRiskWarning && ( + + )} + +
+ +
+ +
+ + + + + + +
+ ); +}; + +export default BaseLayout; diff --git a/src/alex_frontend/bibliotheca/src/layout/Header.tsx b/src/alex_frontend/bibliotheca/src/layout/Header.tsx new file mode 100644 index 000000000..2bc3536ce --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/layout/Header.tsx @@ -0,0 +1,85 @@ +import React, { Suspense } from "react"; +import { useAppSelector } from "@/store/hooks/useAppSelector"; +import Logo from "./../components/Logo"; +import Tabs from "./../components/Tabs"; +import { useIdentity } from "@/lib/ic-use-identity"; +import Login from "@/features/login"; +import Processing from "@/components/Processing"; +import { useUser } from "@/hooks/actors"; + +import { lazy } from "react"; +import { ModeToggle } from "@/lib/components/mode-toggle"; + +const InlineSignup = lazy(() => + import("@/features/signup").then((module) => ({ + default: module.InlineSignup, + })) +); + +const Auth = lazy(() => import("@/features/auth")); + +const AccountButton = lazy(() => + import("@/features/account").then((module) => ({ + default: module.AccountButton, + })) +); + +export const Entry = () => { + const { actor } = useUser(); + const { identity } = useIdentity(); + const { user } = useAppSelector((state) => state.auth); + const { loading } = useAppSelector((state) => state.login); + + // Then check if we have an identity + if (!identity) return ; + + // Show loading state while waiting for actor + if (!actor) return ; + + // Show loading state during login with backend + if (loading) return ; + + // If we have identity and actor but no user, show signup + if (!user) + return ( + // load signup module only when needed + }> + + + ); + + return ( +
+
+ } + > + + + + ); +}; + +function Header() { + // bg-gray-900 + + return ( +
+
+
+ +
+
+ +
+
+ + +
+
+
+ ); +} + +export default Header; diff --git a/src/alex_frontend/bibliotheca/src/pages/BrowsePage.tsx b/src/alex_frontend/bibliotheca/src/pages/BrowsePage.tsx new file mode 100644 index 000000000..0dde79a93 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/pages/BrowsePage.tsx @@ -0,0 +1,244 @@ +import React, { useEffect, useState } from "react"; +import { Button } from "@/lib/components/button"; +import { + Database, + Globe, + ArrowDown, + BookOpen, + Coins, + AlertCircle, + Loader2, +} from "lucide-react"; +import { BookCard } from "@/features/bibliotheca/components/BookCard"; +import { BookModal } from "@/features/bibliotheca/components/BookModal"; +import { MintButton } from "@/features/bibliotheca/components/MintButton"; +import { useArweaveBooks } from "@/features/bibliotheca/hooks/useArweaveBooks"; +import { fetchBooks } from "@/features/bibliotheca/browse/thunks/fetchBooks"; +import { useAppDispatch } from "@/store/hooks/useAppDispatch"; +import { ArweaveBook, Book } from "@/features/bibliotheca/types"; +import { AudioBook } from "@/features/bibliotheca/components/AudioBook"; + +const BrowsePage: React.FC = () => { + const dispatch = useAppDispatch(); + const { books, loading, error, hasNext, loadMore, isEmpty } = + useArweaveBooks(); + const [modalBookUrl, setModalBookUrl] = useState(""); + const [isModalOpen, setIsModalOpen] = useState(false); + + // Convert ArweaveBook to Book format for BookCard + const convertToBook = (arweaveBook: ArweaveBook): Book => { + // Try to get content type from data.type or Content-Type tag + let contentType = arweaveBook.data?.type; + if (!contentType) { + const contentTypeTag = arweaveBook.tags?.find( + (tag) => tag.name === "Content-Type" + ); + contentType = contentTypeTag?.value || ""; + } + + return { + id: arweaveBook.id, + type: contentType, + size: arweaveBook.data?.size || null, + timestamp: new Date( + arweaveBook.block.timestamp * 1000 + ).toISOString(), + }; + }; + + // Fetch initial data on mount + useEffect(() => { + if (books.length === 0 && !loading && !error) { + dispatch(fetchBooks({ reset: true })); + } + }, [dispatch, books.length, loading, error]); + + const formatFileSize = (sizeString: string | null) => { + if (!sizeString) return "Unknown size"; + const size = parseInt(sizeString); + if (isNaN(size)) return "Unknown size"; + if (size < 1024 * 1024) { + return `${(size / 1024).toFixed(1)} KB`; + } + return `${(size / (1024 * 1024)).toFixed(1)} MB`; + }; + + // Modal handlers + const handleBookClick = (book: Book) => { + const bookUrl = + book.id.startsWith("blob:") || book.id.includes(".") + ? book.id + : `https://arweave.net/${book.id}`; + setModalBookUrl(bookUrl); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setModalBookUrl(""); + }; + + return ( +
+ {/* Left Sidebar */} +
+
+
+
+ +

From Arweave

+
+

+ Book files from the permanent web, ready to discover + and mint. +

+
+
+ + Permanent storage +
+
+ + Always accessible +
+
+ + Ready to mint +
+
+
+
+ +
+
+
+ +

Create NFTs

+
+

+ Transform any audio into a tradeable NFT on + Alexandria. +

+
+
+

+ Quick steps: +

+
    +
  • • Click mint icon on any book
  • +
  • • Confirm the transaction
  • +
  • • Own the NFT permanently
  • +
+
+
+
+

+ {loading && books.length === 0 ? ( + "Loading files..." + ) : ( + <> + + {books.length} + {" "} + files available + + )} +

+
+
+
+ + {/* Load More */} + {hasNext && ( + + )} +
+ + {/* Book Grid */} +
+ {error && ( +
+ + + {error} + +
+ )} + + {loading && books.length === 0 && ( +
+ + + Loading book files from Arweave... + +
+ )} + + {isEmpty && !loading && ( +
+ +

+ No book files found +

+

+ Try refreshing the page +

+
+ )} + + {books.length > 0 && ( +
+ {books.map((arweaveBook) => { + const bookItem = convertToBook(arweaveBook); + // Format size better + bookItem.size = formatFileSize(bookItem.size); + + return ( + handleBookClick(bookItem)} + actions={ + <> + + + + } + /> + ); + })} +
+ )} +
+ + {/* Book Modal */} + +
+ ); +}; + +export default BrowsePage; diff --git a/src/alex_frontend/bibliotheca/src/pages/HomePage.tsx b/src/alex_frontend/bibliotheca/src/pages/HomePage.tsx new file mode 100644 index 000000000..ee35443a9 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/pages/HomePage.tsx @@ -0,0 +1,452 @@ +import React from "react"; +import { Link } from "@tanstack/react-router"; +import { Button } from "@/lib/components/button"; +import { + BookOpen, + Library, + Coins, + Store, + Upload, + Download, + Archive, + TrendingUp, + Shield, + Globe, + ArrowRight, + Sparkles, + Users, + Zap, + Heart, + Star, + FileText, + BookMarked, + Bookmark, +} from "lucide-react"; + +const HomePage: React.FC = () => { + return ( +
+ {/* Hero Section */} +
+
+
+
+ +

+ Bibliotheca +

+ +
+

+ Part of the Alexandria ecosystem - discover books + and ePub content from Arweave, create your own + digital library, and mint them as NFTs on LBRY. +

+

+ Transform literary content from the permanent web + into tradeable digital assets. +

+
+ + {/* CTA Buttons */} +
+ + + + + + +
+ + {/* What You Can Do */} +
+ +
+ +

+ Discover from Arweave +

+

+ Browse books and ePub content stored + permanently on the Arweave network and mint + them as NFTs +

+
+ + +
+ +

+ Upload & Create +

+

+ Upload your ePub files, PDFs, or create new + literary content to mint as book NFTs +

+
+ + +
+ +

+ Trade on LBRY +

+

+ Buy and sell book NFTs in the Alexandria + marketplace using LBRY tokens +

+
+ +
+
+
+ + {/* How It Works */} +
+
+
+

+ How Bibliotheca Works +

+

+ Powered by Alexandria's infrastructure - connecting + Arweave's permanent storage with LBRY's marketplace + for digital literature. +

+
+ +
+ {/* Discover */} +
+
+ +
+

+ 1. Discover +

+

+ Browse literary content from Arweave's permanent + web. Find books, ePubs, and documents that have + been stored forever on the decentralized + network. +

+
+
+ + Content from Arweave network +
+
+ + Permanently stored literature +
+
+ + Verified book transactions +
+
+
+ + {/* Create or Mint */} +
+
+ +
+

+ 2. Upload or Mint +

+

+ Upload your own ePub files, PDFs, or documents + to create original book NFTs. You can also mint + discovered Arweave books as NFTs for your + collection. +

+
+
+ + Support for ePub & PDF files +
+
+ + Upload your own literature +
+
+ + Mint Arweave books as NFTs +
+
+
+ + {/* Trade on Alexandria */} +
+
+ +
+

+ 3. Trade on Alexandria +

+

+ List your book NFTs on the Alexandria + marketplace and trade with LBRY tokens. Connect + with readers and collectors in the ecosystem. +

+
+
+ + Trade with LBRY tokens +
+
+ + Alexandria ecosystem +
+
+ + Support authors & readers +
+
+
+
+ + {/* Process Flow */} +
+ + + +
+
+
+ + {/* Features & Benefits */} +
+
+
+

+ Why Choose Bibliotheca +

+

+ Built on Alexandria's proven infrastructure, + connecting the permanent web with decentralized + literary commerce. +

+
+ +
+
+ +

+ Arweave Integration +

+

+ Access literary content stored permanently on + Arweave. Discover and mint book transactions + that will never disappear from the web. +

+
+ +
+ +

+ Book Publishing +

+

+ Upload ePub files, PDFs, and documents. Create + your digital library and turn literary works + into valuable NFTs. +

+
+ +
+ +

+ LBRY Marketplace +

+

+ Trade book NFTs using LBRY tokens in the + Alexandria ecosystem. Connect with a community + of readers and authors. +

+
+ +
+ +

+ Permanent Ownership +

+

+ True ownership through blockchain technology. + Your literary NFTs are yours forever, stored on + immutable networks. +

+
+
+
+
+ + {/* Quick Access */} +
+
+
+

+ Ready to Get Started? +

+

+ Choose your path and start your digital library + journey today +

+
+ +
+ +
+ +

+ Browse +

+

+ Discover books and literature from Arweave + network +

+
+ Explore Now → +
+
+ + + +
+ +

+ Upload +

+

+ Upload ePub files and documents to mint as + NFTs +

+
+ Upload Files → +
+
+ + + +
+ +

+ Library +

+

+ Manage your personal book NFT collection +

+
+ View Library → +
+
+ + + +
+ +

+ Shelf +

+

+ Manage your listings and trading activity +

+
+ Open Shelf → +
+
+ + + +
+ +

+ Marketplace +

+

+ Buy and sell book NFTs with LBRY tokens +

+
+ Visit Market → +
+
+ + + +
+ +

+ Reader +

+

+ Read your books with our integrated ePub + reader +

+
+ Start Reading → +
+
+ +
+
+
+ + {/* Final CTA */} +
+
+

+ Join the Alexandria Ecosystem +

+

+ Be part of Alexandria's vision for decentralized + literature. Discover books from the permanent web, + create your digital library, and trade in a truly + decentralized marketplace. +

+
+ + + + + + +
+
+
+
+ ); +}; + +export default HomePage; diff --git a/src/alex_frontend/bibliotheca/src/pages/LibraryPage.tsx b/src/alex_frontend/bibliotheca/src/pages/LibraryPage.tsx new file mode 100644 index 000000000..0e6188471 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/pages/LibraryPage.tsx @@ -0,0 +1,245 @@ +import React, { useEffect, useState } from "react"; +import { Button } from "@/lib/components/button"; +import { Link } from "@tanstack/react-router"; +import { + Archive, + User, + FileAudio, + Download, + ArrowDown, + LoaderPinwheel, +} from "lucide-react"; +import { BookCard } from "@/features/bibliotheca/components/BookCard"; +import { BookModal } from "@/features/bibliotheca/components/BookModal"; +import { SellButton } from "@/features/bibliotheca/components/SellButton"; +import { useUserBookNFTs } from "@/features/bibliotheca/hooks/useUserBookNFTs"; +import { useAppSelector } from "@/store/hooks/useAppSelector"; +import { Book } from "@/features/bibliotheca/types"; + +const BibliothecaLibraryPage: React.FC = () => { + const { user } = useAppSelector((state) => state.auth); + const { books, loading, loadingMore, error, pagination, refreshBookNFTs } = + useUserBookNFTs(); + const [modalBookUrl, setModalBookUrl] = useState(""); + const [isModalOpen, setIsModalOpen] = useState(false); + + // Fetch user's book NFTs when component mounts or user changes + useEffect(() => { + if (user?.principal && books.length === 0 && !loading && !loadingMore) { + console.log( + "[LibraryPage] Fetching user book NFTs for:", + user.principal + ); + refreshBookNFTs(user.principal, 1, false); // First page, not append mode + } + }, [user?.principal]); // Remove other dependencies to prevent loops + + // Load More handler + const handleLoadMore = () => { + if (user?.principal && !loadingMore && pagination.hasMore) { + refreshBookNFTs(user.principal, pagination.page + 1, true); // Next page, append mode + } + }; + + // Modal handlers + const handleBookClick = (book: Book) => { + const bookUrl = + book.id.startsWith("blob:") || book.id.includes(".") + ? book.id + : `https://arweave.net/${book.id}`; + setModalBookUrl(bookUrl); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setModalBookUrl(""); + }; + + return ( +
+ {/* Left Sidebar */} +
+
+
+
+
+ +

+ Your Collection +

+
+ +
+

+ Your personal book NFT collection and uploaded + content. +

+
+
+ + Owned by you +
+
+ + Ready to trade +
+
+ + Download anytime +
+
+
+
+ +
+
+
+ +

Manage Content

+
+

+ Download, share, or list your book NFTs for sale. +

+
+
+

+ Available actions: +

+
    +
  • • Download original files
  • +
  • • Share with others
  • +
  • • List on marketplace
  • +
+
+
+
+

+ + {books.length} + {" "} + of{" "} + + {pagination.totalCount} + {" "} + items +

+ + → View your listed books + +
+
+
+ + {/* Load More */} + {pagination.hasMore && ( + + )} +
+ + {/* Book Grid */} +
+ {loading ? ( +
+
+ +

+ Loading your book NFTs... +

+

+ This may take a few moments +

+
+
+ ) : error ? ( +
+
+

+ Error loading NFTs +

+

+ {error} +

+ +
+
+ ) : books.length === 0 ? ( +
+
+ +

+ No Book NFTs Found +

+

+ You haven't minted any book NFTs yet. +

+
+
+ ) : ( +
+ {books.map((item) => ( + handleBookClick(item)} + actions={ + + } + /> + ))} +
+ )} +
+ + {/* Book Modal */} + +
+ ); +}; + +export default BibliothecaLibraryPage; diff --git a/src/alex_frontend/bibliotheca/src/pages/MarketPage.tsx b/src/alex_frontend/bibliotheca/src/pages/MarketPage.tsx new file mode 100644 index 000000000..37782ae8c --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/pages/MarketPage.tsx @@ -0,0 +1,229 @@ +import React, { useEffect, useState } from "react"; +import { Button } from "@/lib/components/button"; +import { Link } from "@tanstack/react-router"; +import { + Store, + TrendingUp, + Search, + Filter, + ArrowDown, + FileAudio, + LoaderPinwheel, +} from "lucide-react"; +import { BookCard } from "@/features/bibliotheca/components/BookCard"; +import { BookModal } from "@/features/bibliotheca/components/BookModal"; +import { BuyButton } from "@/features/bibliotheca/components/BuyButton"; +import { useMarketBookNFTs } from "@/features/bibliotheca/hooks/useMarketBookNFTs"; +import { MarketBook } from "@/features/bibliotheca/types"; + +const BibliothecaMarketPage: React.FC = () => { + const { + books, + loading, + loadingMore, + error, + pagination, + refreshMarketBookNFTs, + } = useMarketBookNFTs(); + const [modalBookUrl, setModalBookUrl] = useState(""); + const [isModalOpen, setIsModalOpen] = useState(false); + + // Fetch market book NFTs on component mount + useEffect(() => { + refreshMarketBookNFTs(1, 8, false); // First page, not append mode + }, []); + + // Load More handler + const handleLoadMore = () => { + if (!loadingMore && pagination.page < pagination.totalPages) { + refreshMarketBookNFTs(pagination.page + 1, 8, true); // Next page, append mode + } + }; + + // Modal handlers + const handleBookClick = (book: MarketBook) => { + const bookUrl = + book.id.startsWith("blob:") || book.id.includes(".") + ? book.id + : `https://arweave.net/${book.id}`; + setModalBookUrl(bookUrl); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setModalBookUrl(""); + }; + + return ( +
+ {/* Left Sidebar */} +
+
+
+
+ +

Marketplace

+
+

+ Discover and purchase book NFTs from creators + worldwide. +

+
+
+ + Trending content +
+
+ + Search by genre +
+
+ + Filter by price +
+
+
+
+ +
+
+
+ +

Buy & Own

+
+

+ Purchase book NFTs with ALEX or LBRY tokens and own + them forever. +

+
+
+

+ How it works: +

+
    +
  • • Preview before buying
  • +
  • • Secure blockchain purchase
  • +
  • • Instant ownership transfer
  • +
+
+
+
+

+ + {books.length} + {" "} + of{" "} + + {pagination.totalCount} + {" "} + items shown +

+ + → Manage your listings + +
+
+
+ + {/* Load More */} + {pagination.page < pagination.totalPages && ( + + )} +
+ + {/* Book Grid */} +
+ {loading ? ( +
+
+ +

+ Loading marketplace book NFTs... +

+
+
+ ) : error ? ( +
+
+

+ Error loading marketplace +

+

+ {error} +

+ +
+
+ ) : books.length === 0 ? ( +
+
+ +

+ No Items for Sale +

+

+ There are currently no book NFTs listed in the + marketplace. +

+
+
+ ) : ( +
+ {books.map((book) => ( + handleBookClick(book)} + actions={ + + } + /> + ))} +
+ )} +
+ + {/* Book Modal */} + +
+ ); +}; + +export default BibliothecaMarketPage; diff --git a/src/alex_frontend/bibliotheca/src/pages/ShelfPage.tsx b/src/alex_frontend/bibliotheca/src/pages/ShelfPage.tsx new file mode 100644 index 000000000..5887059d2 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/pages/ShelfPage.tsx @@ -0,0 +1,245 @@ +import React, { useEffect, useState } from "react"; +import { Button } from "@/lib/components/button"; +import { + Palette, + Settings, + TrendingUp, + BarChart3, + ArrowDown, + FileAudio, + LoaderPinwheel, +} from "lucide-react"; +import { BookCard } from "@/features/bibliotheca/components/BookCard"; +import { BookModal } from "@/features/bibliotheca/components/BookModal"; +import { EditButton } from "@/features/bibliotheca/components/EditButton"; +import { UnlistButton } from "@/features/bibliotheca/components/UnlistButton"; +import { useShelfBookNFTs } from "@/features/bibliotheca/hooks/useShelfBookNFTs"; +import { useAppSelector } from "@/store/hooks/useAppSelector"; +import { ShelfBook } from "@/features/bibliotheca/types"; + +const BibliothecaShelfPage: React.FC = () => { + const { user } = useAppSelector((state) => state.auth); + const { + books, + loading, + loadingMore, + error, + pagination, + refreshShelfBookNFTs, + } = useShelfBookNFTs(); + const [modalBookUrl, setModalBookUrl] = useState(""); + const [isModalOpen, setIsModalOpen] = useState(false); + + // Fetch user's listed book NFTs on component mount + useEffect(() => { + if (user?.principal) { + refreshShelfBookNFTs(user.principal, 1, 8, false); // First page, not append mode + } + }, [user?.principal]); + + // Load More handler + const handleLoadMore = () => { + if ( + user?.principal && + !loadingMore && + pagination.page < pagination.totalPages + ) { + refreshShelfBookNFTs(user.principal, pagination.page + 1, 8, true); // Next page, append mode + } + }; + + // Modal handlers + const handleBookClick = (book: ShelfBook) => { + const bookUrl = + book.id.startsWith("blob:") || book.id.includes(".") + ? book.id + : `https://arweave.net/${book.id}`; + setModalBookUrl(bookUrl); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setModalBookUrl(""); + }; + + return ( +
+ {/* Left Sidebar */} +
+
+
+
+ +

Creator Studio

+
+

+ Manage your listed book NFTs and marketplace + presence. +

+
+
+ + Track sales +
+
+ + Edit listings +
+
+ + View analytics +
+
+
+
+ +
+
+
+ +

Manage Listings

+
+

+ Edit metadata, pricing, and availability of your + book NFTs. +

+
+
+

+ Available actions: +

+
    +
  • • Edit titles and descriptions
  • +
  • • Update pricing
  • +
  • • Remove from marketplace
  • +
+
+
+
+

+ + {books.length} + {" "} + of{" "} + + {pagination.totalCount} + {" "} + listings shown +

+
+
+
+ + {/* Load More */} + {pagination.page < pagination.totalPages && ( + + )} +
+ + {/* Book Grid */} +
+ {loading ? ( +
+
+ +

+ Loading your listed book NFTs... +

+
+
+ ) : error ? ( +
+
+

+ Error loading listed book NFTs +

+

+ {error} +

+ +
+
+ ) : books.length === 0 ? ( +
+
+ +

+ No Active Listings +

+

+ You don't have any book NFTs listed for sale + yet. +

+
+
+ ) : ( +
+ {books.map((item) => ( + handleBookClick(item)} + actions={ + <> + + + + } + /> + ))} +
+ )} +
+ + {/* Book Modal */} + +
+ ); +}; + +export default BibliothecaShelfPage; diff --git a/src/alex_frontend/bibliotheca/src/pages/UploadPage.tsx b/src/alex_frontend/bibliotheca/src/pages/UploadPage.tsx new file mode 100644 index 000000000..2d590ac92 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/pages/UploadPage.tsx @@ -0,0 +1,266 @@ +import React, { useState, useRef } from "react"; +import { Button } from "@/lib/components/button"; +import { Upload, Mic, X, LoaderPinwheel } from "lucide-react"; +import { Link } from "@tanstack/react-router"; +import { useAppDispatch } from "@/store/hooks/useAppDispatch"; +import { useAppSelector } from "@/store/hooks/useAppSelector"; +import { + setUploadPreview, + clearUploadPreview, +} from "@/features/bibliotheca/bibliothecaSlice"; +import { Book } from "@/features/bibliotheca/types"; +import { BookCard } from "@/features/bibliotheca/components/BookCard"; +import { BookModal } from "@/features/bibliotheca/components/BookModal"; +import { useUploadAndMint } from "@/features/pinax/hooks/useUploadAndMint"; +import { AudioBook } from "@/features/bibliotheca/components/AudioBook"; + +const BibliothecaUploadPage: React.FC = () => { + const [selectedFile, setSelectedFile] = useState(null); + const [bookUrl, setBookUrl] = useState(""); + const [isDragging, setIsDragging] = useState(false); + const [modalBookUrl, setModalBookUrl] = useState(""); + const [isModalOpen, setIsModalOpen] = useState(false); + const fileInputRef = useRef(null); + const dispatch = useAppDispatch(); + const { uploadPreview } = useAppSelector((state) => state.bibliotheca); + const { + uploadAndMint, + isProcessing, + error, + success, + progress, + estimating, + uploading, + minting, + resetUpload, + } = useUploadAndMint(); + + const handleFileSelect = (file: File) => { + // Validate EPUB file only + if ( + !file.type.includes("epub") && + !file.name.toLowerCase().endsWith(".epub") + ) { + alert("Please select an EPUB file (.epub)"); + return; + } + + setSelectedFile(file); + // Create object URL for preview + const url = URL.createObjectURL(file); + setBookUrl(url); + + // Create Book object for Redux + const bookData: Book = { + id: url, // Use object URL as ID for local files + type: file.type, + size: `${(file.size / (1024 * 1024)).toFixed(2)} MB`, + timestamp: new Date().toISOString(), + }; + + // Set book in global state for preview + dispatch(setUploadPreview(bookData)); + }; + + const handleFileInput = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) handleFileSelect(file); + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + const file = e.dataTransfer.files[0]; + if (file) handleFileSelect(file); + }; + + const handleRemoveFile = () => { + if (bookUrl) { + URL.revokeObjectURL(bookUrl); + } + setSelectedFile(null); + setBookUrl(""); + if (fileInputRef.current) fileInputRef.current.value = ""; + // Clear book from global state + dispatch(clearUploadPreview()); + // Reset upload state + resetUpload(); + }; + + const handleUpload = async () => { + if (selectedFile) { + try { + const transactionId = await uploadAndMint(selectedFile); + // Replace blob URL with Arweave transaction URL + if (bookUrl) { + URL.revokeObjectURL(bookUrl); + } + const arweaveUrl = `https://arweave.net/${transactionId}`; + setBookUrl(arweaveUrl); + + // Update the book data with Arweave URL + const bookData: Book = { + id: transactionId, // Use transaction ID as the ID + type: selectedFile.type, + size: `${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB`, + timestamp: new Date().toISOString(), + }; + dispatch(setUploadPreview(bookData)); + } catch (error) { + // Error handling is done in the hook, keep file for retry + } + } + }; + + // Modal handlers + const handleBookClick = () => { + setModalBookUrl(bookUrl); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setModalBookUrl(""); + }; + + return ( +
+
+
+

+ Share your book content with the world +

+
+ + {/* File Upload Area */} +
+
+ + +
+ +
+

+ {selectedFile + ? selectedFile.name + : "Drop your book file here"} +

+

+ or click to browse • EPUB files only +

+
+
+
+
+ + {/* Book Preview */} + {selectedFile && uploadPreview && ( +
+
+

+ {bookUrl.startsWith("blob:") + ? "Preview" + : "Uploaded to Arweave"} +

+ +
+ } + /> +
+ )} + + {/* Error and Success Messages */} + {error && ( +
+

{error}

+
+ )} + + {success && ( +
+

{success}

+
+ )} + + {/* Upload Button */} + {selectedFile && bookUrl.startsWith("blob:") && ( +
+ +
+ )} +
+ + {/* Book Modal */} + +
+ ); +}; + +export default BibliothecaUploadPage; diff --git a/src/alex_frontend/bibliotheca/src/routes/__root.tsx b/src/alex_frontend/bibliotheca/src/routes/__root.tsx new file mode 100644 index 000000000..e72ead57e --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/__root.tsx @@ -0,0 +1,10 @@ +import { createRootRoute } from "@tanstack/react-router"; +import BaseLayout from "./../layout/BaseLayout"; +import RouteFallback from "@/components/fallbacks/RouteFallback"; +import NotFoundPage from "@/pages/NotFoundPage"; + +export const Route = createRootRoute({ + component: BaseLayout, + errorComponent: RouteFallback, + notFoundComponent: NotFoundPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/archive.$principal.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/archive.$principal.lazy.tsx new file mode 100644 index 000000000..60f0bc179 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/archive.$principal.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BibliothecaLibraryPage from "./../../pages/LibraryPage"; + +export const Route = createLazyFileRoute("/_auth/archive/$principal")({ + component: BibliothecaLibraryPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/archive.$principal.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/archive.$principal.tsx new file mode 100644 index 000000000..38b10bf04 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/archive.$principal.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/archive/$principal")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/browse.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/browse.lazy.tsx new file mode 100644 index 000000000..8089b7611 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/browse.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BrowsePage from "./../../pages/BrowsePage"; + +export const Route = createLazyFileRoute("/_auth/browse")({ + component: BrowsePage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/browse.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/browse.tsx new file mode 100644 index 000000000..d171bb90f --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/browse.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/browse")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/library.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/library.lazy.tsx new file mode 100644 index 000000000..145554efe --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/library.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BibliothecaLibraryPage from "./../../pages/LibraryPage"; + +export const Route = createLazyFileRoute("/_auth/library")({ + component: BibliothecaLibraryPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/library.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/library.tsx new file mode 100644 index 000000000..e2e5b346e --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/library.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/library")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/market.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/market.lazy.tsx new file mode 100644 index 000000000..c8840f4bd --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/market.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BibliothecaMarketPage from "./../../pages/MarketPage"; + +export const Route = createLazyFileRoute("/_auth/market")({ + component: BibliothecaMarketPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/market.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/market.tsx new file mode 100644 index 000000000..19cbe869a --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/market.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/market")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/route.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/route.lazy.tsx new file mode 100644 index 000000000..0468f104c --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/route.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import AuthGuard from "@/guards/AuthGuard"; + +export const Route = createLazyFileRoute("/_auth")({ + component: AuthGuard, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/shelf.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/shelf.lazy.tsx new file mode 100644 index 000000000..fa237d38f --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/shelf.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BibliothecaShelfPage from "./../../pages/ShelfPage"; + +export const Route = createLazyFileRoute("/_auth/shelf")({ + component: BibliothecaShelfPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/shelf.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/shelf.tsx new file mode 100644 index 000000000..251067eb8 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/shelf.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/shelf")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/studio.$principal.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/studio.$principal.lazy.tsx new file mode 100644 index 000000000..acf587dd0 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/studio.$principal.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BibliothecaShelfPage from "./../../pages/ShelfPage"; + +export const Route = createLazyFileRoute("/_auth/studio/$principal")({ + component: BibliothecaShelfPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/studio.$principal.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/studio.$principal.tsx new file mode 100644 index 000000000..7076a60c0 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/studio.$principal.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/studio/$principal")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/upload.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/upload.lazy.tsx new file mode 100644 index 000000000..6a21b1812 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/upload.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import BibliothecaUploadPage from "./../../pages/UploadPage"; + +export const Route = createLazyFileRoute("/_auth/upload")({ + component: BibliothecaUploadPage, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/_auth/upload.tsx b/src/alex_frontend/bibliotheca/src/routes/_auth/upload.tsx new file mode 100644 index 000000000..f80bdaa14 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/_auth/upload.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth/upload")({ + loader: () => void 0, +}); diff --git a/src/alex_frontend/bibliotheca/src/routes/index.lazy.tsx b/src/alex_frontend/bibliotheca/src/routes/index.lazy.tsx new file mode 100644 index 000000000..395a90564 --- /dev/null +++ b/src/alex_frontend/bibliotheca/src/routes/index.lazy.tsx @@ -0,0 +1,6 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import HomePage from "./../pages/HomePage"; + +export const Route = createLazyFileRoute("/")({ + component: HomePage, +}); diff --git a/src/alex_frontend/src/routes/index.tsx b/src/alex_frontend/bibliotheca/src/routes/index.tsx similarity index 100% rename from src/alex_frontend/src/routes/index.tsx rename to src/alex_frontend/bibliotheca/src/routes/index.tsx diff --git a/src/alex_frontend/bibliotheca/tailwind.config.js b/src/alex_frontend/bibliotheca/tailwind.config.js new file mode 100644 index 000000000..687050d6a --- /dev/null +++ b/src/alex_frontend/bibliotheca/tailwind.config.js @@ -0,0 +1,194 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./index.tsx", + "./public/**/*.html", + "./src/**/*.{ts,tsx,js,jsx,html}", + "../core/**/*.{ts,tsx,js,jsx}" + ], + darkMode: ["class"], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px' + } + }, + extend: { + fontFamily: { + syne: [ + 'Syne', + 'sans-serif' + ], + 'roboto-condensed': [ + 'Roboto Condensed', + 'sans-serif' + ] + }, + container: { + center: true, + padding: { + default: '1rem', + xs: '2rem', + sm: '2rem', + lg: '1rem' + }, + screens: { + xs: '100%', + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + xxl: '1585px' + } + }, + fontSize: { + tabsheading: '20px', + xxltabsheading: '32px', + xltabsheading: '30px', + lgtabsheading: '28px', + mdtabsheading: '25px', + smtabsheading: '22px', + swapheading: '22px', + xxlswapheading: '40px', + xlswapheading: '35px', + lgswapheading: '30px', + mdswapheading: '26px', + smswapheading: '24px' + }, + colors: { + gray: { + 20: 'rgb(242, 242, 241)', + 30: 'rgb(254, 253, 251)', + 50: 'rgb(253, 252, 249)', + 100: 'rgb(249, 247, 242)', + 200: 'rgb(240, 237, 229)', + 300: 'rgb(224, 220, 208)', + 400: 'rgb(194, 189, 173)', + 500: 'rgb(161, 155, 136)', + 600: 'rgb(127, 121, 104)', + 700: 'rgb(96, 92, 79)', + 800: 'rgb(66, 64, 58)', + 850: 'rgb(60, 58, 53)', + 900: 'rgb(53, 52, 48)', + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + info: { + DEFAULT: 'hsl(var(--info))', + foreground: 'hsl(var(--info-foreground))' + }, + warning: { + DEFAULT: 'hsl(var(--warning))', + foreground: 'hsl(var(--warning-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + constructive: { + DEFAULT: 'hsl(var(--constructive))', + foreground: 'hsl(var(--constructive-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + multycolor: '#FF9900', + brightyellow: '#F6F930', + multygray: '#808080', + lightgray: '#CCCCCC', + radiocolor: '#353535', + swapinput: '#32524D', + swaptext: '#5C5C5C', + swapvalue: '#31524E', + darkgray: '#525252', + white: '#FFFFFF', + black: '#000000', + transparent: 'transparent', + current: 'currentColor', + }, + backgroundColor: { + balancebox: '#3A3630', + sendbtnbg: '#FF3737', + mintbtnbg: '#92FF71', + receive: '#92FF71' + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + borderbox: '44px', + bordertb: '20px' + }, + height: { + circleheight: '32px', + inputbox: '66px' + }, + width: { + circlewidth: '32px' + }, + keyframes: { + 'accordion-down': { + from: { + height: '0' + }, + to: { + height: 'var(--radix-accordion-content-height)' + } + }, + 'accordion-up': { + from: { + height: 'var(--radix-accordion-content-height)' + }, + to: { + height: '0' + } + } + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out' + }, + backgroundImage: {}, + screens: { + xs: '280px' + } + } + }, + variants: { + extend: { + opacity: ["disabled"], + cursor: ["disabled"], + pointerEvents: ["disabled"], + }, + }, + plugins: [ + require("tailwindcss-animate"), + require('tailwind-scrollbar'), + ], +}; \ No newline at end of file diff --git a/src/alex_frontend/src/styles/tailwind.css b/src/alex_frontend/bibliotheca/tailwind.css similarity index 100% rename from src/alex_frontend/src/styles/tailwind.css rename to src/alex_frontend/bibliotheca/tailwind.css diff --git a/src/alex_frontend/bibliotheca/tsconfig.json b/src/alex_frontend/bibliotheca/tsconfig.json new file mode 100644 index 000000000..9d02d56f8 --- /dev/null +++ b/src/alex_frontend/bibliotheca/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["../core/*"] + } + }, + "include": ["src/**/*", "routeTree.gen.ts"], + "exclude": ["node_modules"] +} diff --git a/src/alex_frontend/src/vite-env.d.ts b/src/alex_frontend/bibliotheca/vite-env.d.ts similarity index 100% rename from src/alex_frontend/src/vite-env.d.ts rename to src/alex_frontend/bibliotheca/vite-env.d.ts diff --git a/src/alex_frontend/bibliotheca/webpack.config.js b/src/alex_frontend/bibliotheca/webpack.config.js new file mode 100644 index 000000000..0f163d480 --- /dev/null +++ b/src/alex_frontend/bibliotheca/webpack.config.js @@ -0,0 +1,342 @@ +const path = require("path"); +require("dotenv").config({ path: path.resolve(__dirname, '../../../.env') }); +const webpack = require("webpack"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const TerserPlugin = require("terser-webpack-plugin"); +const CopyPlugin = require("copy-webpack-plugin"); +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const { TanStackRouterWebpack } = require('@tanstack/router-plugin/webpack') +const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); + + +const isDevelopment = process.env.NODE_ENV !== "production"; +// Load the appropriate .env file based on NODE_ENV +if (process.env.NODE_ENV === 'production') { + require("dotenv").config({ path: path.resolve(__dirname, '../../../.env.production') }); +} +const publicUrl = process.env.PUBLIC_URL || ''; + +const frontend_entry = path.join("public", "index.html"); + +module.exports = { + target: "web", + mode: isDevelopment ? "development" : "production", + entry: { + index: path.join(__dirname, "index.tsx"), + }, + devtool: isDevelopment ? "source-map" : false, + optimization: { + minimize: !isDevelopment, + minimizer: [new TerserPlugin({ + terserOptions: { + compress: { + drop_console: false, // Keep console logs for debugging + }, + }, + })], + splitChunks: { + chunks: 'all', + maxInitialRequests: 6, // Allow more initial requests for better parallelization + maxAsyncRequests: 30, // Allow more async requests + minSize: 20000, // Slightly larger minimum size to prevent tiny chunks + maxSize: 244000, // Maximum size to prevent huge chunks + cacheGroups: { + // Critical path modules needed for initial render + critical: { + test: /[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types|redux|react-redux|@reduxjs\/toolkit)[\\/]/, + name: 'critical', + chunks: 'initial', // Only include in initial chunks + priority: 60, + enforce: true, + }, + // TensorFlow and related packages + tensorflow: { + test: /[\\/]node_modules[\\/](@tensorflow|tfjs-core|tfjs-backend-.*|tfjs-converter)[\\/]/, + name: 'tensorflow', + chunks: 'async', // Only load asynchronously + priority: 50, + enforce: true + }, + // NSFWJS package + nsfwjs: { + test: /[\\/]node_modules[\\/]nsfwjs[\\/]/, + name: 'nsfwjs', + chunks: 'async', // Only load asynchronously + priority: 40, + enforce: true + }, + // Common vendor modules + vendors: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + priority: 20, + reuseExistingChunk: true, + }, + // Common application code + commons: { + name: 'commons', + minChunks: 2, // Used in at least 2 chunks + chunks: 'initial', + priority: 10, + reuseExistingChunk: true, + }, + }, + }, + runtimeChunk: 'single', + }, + resolve: { + extensions: [".js", ".ts", ".jsx", ".tsx"], + fallback: { + assert: require.resolve("assert/"), + buffer: require.resolve("buffer/"), + crypto: require.resolve("crypto-browserify"), + events: require.resolve("events/"), + fs: false, + http: require.resolve("stream-http"), + https: require.resolve("https-browserify"), + os: require.resolve("os-browserify"), + path: require.resolve("path-browserify"), + stream: require.resolve("stream-browserify"), + url: require.resolve("url"), + util: require.resolve("util/"), + vm: require.resolve("vm-browserify"), + zlib: require.resolve("browserify-zlib"), + }, + alias: { + stream: "stream-browserify", + "@": path.resolve(__dirname, "../core"), + 'nsfwjs': path.resolve(__dirname, '../../../node_modules/nsfwjs'), + './model_imports/inception_v3': 'null-loader', + './model_imports/mobilenet_v2': 'null-loader', + './model_imports/mobilenet_v2_mid': 'null-loader' + }, + }, + output: { + // filename: "index.js", + // path: path.join(__dirname, "dist"), + filename: '[name].[contenthash].js', + chunkFilename: '[name].[contenthash].js', + path: path.join(__dirname, "dist"), + publicPath: publicUrl + '/', + }, + + module: { + rules: [ + { + test: /\.(png|jpe?g|gif|svg)$/i, + type: 'asset/resource', + }, + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + '@babel/preset-react', + '@babel/preset-typescript' + ], + plugins: [ + isDevelopment && require.resolve('react-refresh/babel') + ].filter(Boolean), + }, + }, + }, + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env', '@babel/preset-react'], + plugins: [ + isDevelopment && require.resolve('react-refresh/babel') + ].filter(Boolean), + }, + }, + }, + { + test: /\.css$/i, + use: ['style-loader', 'css-loader', 'postcss-loader'], + }, + { + test: /\.svg$/, + use: ['@svgr/webpack'], + }, + { + test: /\.wasm$/, + type: "webassembly/async", + }, + { + test: /nsfwjs[\\/]dist[\\/]esm[\\/]models[\\/].*\.(js|json)$/, + use: 'null-loader', + }, + { + test: /[\\/]node_modules[\\/](@tensorflow|tfjs-core|tfjs-backend-.*|tfjs-converter)[\\/]/, + sideEffects: true, + use: [ + { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], + plugins: [ + '@babel/plugin-transform-runtime', + '@babel/plugin-proposal-class-properties' + ] + } + } + ] + } + ], + }, + + plugins: [ + new HtmlWebpackPlugin({ + template: path.join(__dirname, frontend_entry), + cache: false, + }), + new webpack.EnvironmentPlugin({ + ...Object.keys(process.env).filter((key) => { + if (key.includes("CANISTER")) return true; + if (key.includes("DFX")) return true; + if (key.startsWith("ETH_")) return true; + if (key.startsWith("REACT_")) return true; + if (key === "PUBLIC_URL") return true; + return false; + }).reduce((env, key) => { + env[key] = process.env[key]; + return env; + }, {}), + PUBLIC_URL: '' // Default value if not set + }), + new webpack.ProvidePlugin({ + process: "process/browser.js", + Buffer: ["buffer", "Buffer"], + }), + new CopyPlugin({ + patterns: [ + // Explicitly copy files from the 'introduction' directory + { + from: path.resolve(__dirname, "public", "introduction", "index.html"), + to: path.resolve(__dirname, "dist", "introduction", "index.html"), + noErrorOnMissing: true, + transform(content, absoluteFrom) { + console.log(`[CopyPlugin Info] Processing for introduction: ${absoluteFrom}`); + return content; + }, + }, + { + from: path.resolve(__dirname, "public", "introduction", "styles.css"), + to: path.resolve(__dirname, "dist", "introduction", "styles.css"), + noErrorOnMissing: true, + }, + { + from: path.resolve(__dirname, "public", "introduction", "particles.css"), // Assuming this is the correct name + to: path.resolve(__dirname, "dist", "introduction", "particles.css"), + noErrorOnMissing: true, + }, + // Add any other specific files from 'introduction' directory here if needed + + // Copy other assets from the main public directory + { + from: path.resolve(__dirname, "public"), + to: path.resolve(__dirname, "dist"), + globOptions: { + dot: true, // copy dotfiles like .well-known + ignore: [ + // Ignore the root public/index.html (handled by HtmlWebpackPlugin) + path.resolve(__dirname, "public", "index.html"), + // Ignore the entire introduction directory (handled by specific copies above) + path.resolve(__dirname, "public", "introduction") + '/**', + ], + }, + noErrorOnMissing: true, + }, + ], + }), + new webpack.IgnorePlugin({ + resourceRegExp: /^\.\/.*$/, + contextRegExp: /nsfwjs[\\/]dist[\\/]esm[\\/]models[\\/]models[\\/]model_imports[\\/]inception_v3$/, + }), + new webpack.IgnorePlugin({ + resourceRegExp: /^\.\/locale$/, + contextRegExp: /moment$/, + }), + new webpack.DefinePlugin({ + 'require("./model_imports/inception_v3")': '{}', + 'require("./model_imports/mobilenet_v2")': '{}', + 'require("./model_imports/mobilenet_v2_mid")': '{}' + }), + ...(isDevelopment ? [new ReactRefreshWebpackPlugin({ overlay: false })] : []), + TanStackRouterWebpack({ + target: 'react', + autoCodeSplitting: true, + routesDirectory: path.join(__dirname, "src", "routes"), + generatedRouteTree: path.join(__dirname, "routeTree.gen.ts"), + }), + // new BundleAnalyzerPlugin({ + // analyzerMode: 'server', + // analyzerHost: 'localhost', + // analyzerPort: 8888, + // openAnalyzer: true, + // generateStatsFile: true, + // statsFilename: path.join(__dirname, 'bundle-stats-minimal.json'), + // statsOptions: { + // all: false, + // assets: true, + // assetsSort: 'size', + // chunks: true, + // chunkModules: false, + // entrypoints: true, + // hash: true, + // modules: false, + // timings: true, + // errors: true, + // warnings: true, + // }, + // }), + ], + devServer: { + historyApiFallback: true, + proxy: [ + { + context: ["/api"], + target: "http://127.0.0.1:4943", + changeOrigin: true, + pathRewrite: { "^/api": "/api" }, + }, + ], + static: [ + { + directory: path.resolve(__dirname, "public"), + publicPath: '/', + watch: true, + }, + { + directory: path.resolve(__dirname, "dist"), + publicPath: '/', + watch: true, + } + ], + hot: true, + client: { + overlay: false, + }, + watchFiles: [path.resolve(__dirname, "src")], + liveReload: false, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", + "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization" + }, + devMiddleware: { + publicPath: '/', + }, + }, + experiments: { + asyncWebAssembly: true, + }, +}; + diff --git a/src/alex_frontend/src/apps/Modules/AppModules/blinks/SingleTokenView.tsx b/src/alex_frontend/core/apps/Modules/AppModules/blinks/SingleTokenView.tsx similarity index 97% rename from src/alex_frontend/src/apps/Modules/AppModules/blinks/SingleTokenView.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/blinks/SingleTokenView.tsx index b5099e12b..5b6a24f5e 100644 --- a/src/alex_frontend/src/apps/Modules/AppModules/blinks/SingleTokenView.tsx +++ b/src/alex_frontend/core/apps/Modules/AppModules/blinks/SingleTokenView.tsx @@ -1,10 +1,10 @@ import React, { useState, useEffect } from 'react'; import { useParams } from '@tanstack/react-router'; import ContentRenderer from '../safeRender/ContentRenderer'; -import { ContentCard } from '@/apps/Modules/AppModules/contentGrid/Card'; -import { Dialog, DialogContent, DialogTitle } from '@/lib/components/dialog'; +import { ContentCard } from './../contentGrid/Card'; +import { Dialog, DialogContent, DialogTitle } from './../../../../lib/components/dialog'; import { useSelector, useDispatch } from 'react-redux'; -import { RootState, AppDispatch } from '@/store'; +import { RootState, AppDispatch } from './../../../../store'; import { toast } from "sonner"; // import { withdraw_nft } from "@/features/nft/withdraw"; // Keep commented if not used import { Principal } from '@dfinity/principal'; @@ -16,9 +16,9 @@ import { fetchTransactionById } from '../../LibModules/arweaveSearch/api/directA import { ContentService } from '../../LibModules/contentDisplay/services/contentService'; import { setContentData } from '../../shared/state/transactions/transactionSlice'; import { Transaction } from '../../shared/types/queries'; -import { Badge } from "@/lib/components/badge"; -import { Copy, Check, Link, X, Calendar, Info } from "lucide-react"; -import { copyToClipboard } from '@/apps/Modules/AppModules/contentGrid/utils/clipboard'; +import { Badge } from "./../../../../lib/components/badge"; +import { Copy, Check, Link, Calendar, Info } from "lucide-react"; +import { copyToClipboard } from './../contentGrid/utils/clipboard'; import { getNftOwnerInfo, UserInfo } from '../../shared/utils/nftOwner'; import { convertE8sToToken, formatPrincipal, formatBalance } from '../../shared/utils/tokenUtils'; import { createTokenAdapter, determineTokenType, TokenType } from '../../shared/adapters/TokenAdapter'; diff --git a/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/Card.tsx b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/Card.tsx new file mode 100644 index 000000000..101afa259 --- /dev/null +++ b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/Card.tsx @@ -0,0 +1,94 @@ +import React from "react"; +import { Card, CardContent } from "../../../../lib/components/card"; +import { AspectRatio } from "../../../../lib/components/aspect-ratio"; +import { UnifiedCardActions } from "./../../shared/components/UnifiedCardActions/UnifiedCardActions"; +import { useContentCardState } from "./hooks/useContentCardState"; + +interface ContentCardProps { + children: React.ReactNode; + onClick?: () => void; + id?: string; // Arweave ID or NFT Nat ID string + owner?: string; // Arweave owner string (kept for potential future use, but not displayed here) + predictions?: any; + footer?: React.ReactNode; + component?: string; + isFromAssetCanister?: boolean; + parentShelfId?: string; + itemId?: number; + currentShelfId?: string; + initialContentType?: 'Arweave' | 'Nft'; // Specifies the *context* this card is rendered in +} + +export function ContentCard({ + children, + onClick, + id, // Arweave ID or NFT Nat ID string + owner, // Keep owner prop, but don't use it directly here + predictions, + footer, // Keep footer prop + component, + isFromAssetCanister, + parentShelfId, + itemId, + currentShelfId, + initialContentType = 'Arweave' // Default to Arweave context +}: ContentCardProps) { + + // --- Use the Custom Hook --- + const { + finalContentId, + finalContentType, + isOwnedByUser, + ownerPrincipal, + isSafeForMinting + } = useContentCardState({ id, initialContentType, predictions }); + + // --- Rendering --- + return ( + <> + + {/* Action Button - Using updated bookmark design */} + {finalContentId && ( + + )} + + {/* Main content area - Apply onClick here if needed */} + + +
+ {/* Children now include the hover overlay (TransactionDetails) internally */} + {children} +
+ {/* UnifiedCardActions moved outside */} +
+
+ + {/* Footer area (unchanged) */} + {footer && ( +
+ {footer} +
+ )} +
+ + ); +} \ No newline at end of file diff --git a/src/alex_frontend/src/apps/Modules/AppModules/contentGrid/Grid.tsx b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/Grid.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/contentGrid/Grid.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/contentGrid/Grid.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/contentGrid/components/TransactionDetails.tsx b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/components/TransactionDetails.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/contentGrid/components/TransactionDetails.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/contentGrid/components/TransactionDetails.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/contentGrid/hooks/useContentCardState.ts b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/hooks/useContentCardState.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/contentGrid/hooks/useContentCardState.ts rename to src/alex_frontend/core/apps/Modules/AppModules/contentGrid/hooks/useContentCardState.ts diff --git a/src/alex_frontend/src/apps/Modules/AppModules/contentGrid/utils/clipboard.ts b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/utils/clipboard.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/contentGrid/utils/clipboard.ts rename to src/alex_frontend/core/apps/Modules/AppModules/contentGrid/utils/clipboard.ts diff --git a/src/alex_frontend/src/apps/Modules/AppModules/contentGrid/utils/formatters.ts b/src/alex_frontend/core/apps/Modules/AppModules/contentGrid/utils/formatters.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/contentGrid/utils/formatters.ts rename to src/alex_frontend/core/apps/Modules/AppModules/contentGrid/utils/formatters.ts diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentFetcher.tsx b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentFetcher.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentFetcher.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentFetcher.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentRenderer.tsx b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentRenderer.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentRenderer.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentRenderer.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentTypeMap.tsx b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentTypeMap.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentTypeMap.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentTypeMap.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentValidator.tsx b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentValidator.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/ContentValidator.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/ContentValidator.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/SandboxRenderer.tsx b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/SandboxRenderer.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/SandboxRenderer.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/SandboxRenderer.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/fileIcons.tsx b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/fileIcons.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/fileIcons.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/fileIcons.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/safeRender/types/index.ts b/src/alex_frontend/core/apps/Modules/AppModules/safeRender/types/index.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/safeRender/types/index.ts rename to src/alex_frontend/core/apps/Modules/AppModules/safeRender/types/index.ts diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/NsfwSelector.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/NsfwSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/NsfwSelector.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/NsfwSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/SearchForm.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/SearchForm.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/SearchForm.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/SearchForm.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/hooks/useSearchHandlers.ts b/src/alex_frontend/core/apps/Modules/AppModules/search/hooks/useSearchHandlers.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/hooks/useSearchHandlers.ts rename to src/alex_frontend/core/apps/Modules/AppModules/search/hooks/useSearchHandlers.ts diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/selectors/AmountSelector.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/selectors/AmountSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/selectors/AmountSelector.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/selectors/AmountSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/selectors/ArweaveOwnerSelector.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/selectors/ArweaveOwnerSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/selectors/ArweaveOwnerSelector.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/selectors/ArweaveOwnerSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/selectors/ContentCategorySelector.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/selectors/ContentCategorySelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/selectors/ContentCategorySelector.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/selectors/ContentCategorySelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/selectors/ContentTagsSelector.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/selectors/ContentTagsSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/selectors/ContentTagsSelector.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/selectors/ContentTagsSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/search/selectors/DateSelector.tsx b/src/alex_frontend/core/apps/Modules/AppModules/search/selectors/DateSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/search/selectors/DateSelector.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/search/selectors/DateSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/AppModules/shared/components/ShelvesPreloader.tsx b/src/alex_frontend/core/apps/Modules/AppModules/shared/components/ShelvesPreloader.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/AppModules/shared/components/ShelvesPreloader.tsx rename to src/alex_frontend/core/apps/Modules/AppModules/shared/components/ShelvesPreloader.tsx diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/arweaveApi.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/arweaveApi.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/arweaveApi.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/arweaveApi.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/arweaveClient.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/arweaveClient.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/arweaveClient.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/arweaveClient.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/arweaveHelpers.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/arweaveHelpers.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/arweaveHelpers.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/arweaveHelpers.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/directArweaveClient.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/directArweaveClient.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/directArweaveClient.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/directArweaveClient.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/singleTransactionClient.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/singleTransactionClient.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/api/singleTransactionClient.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/api/singleTransactionClient.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/config/arweaveConfig.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/config/arweaveConfig.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/config/arweaveConfig.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/config/arweaveConfig.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/types/tensorflow.ts b/src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/types/tensorflow.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/arweaveSearch/types/tensorflow.ts rename to src/alex_frontend/core/apps/Modules/LibModules/arweaveSearch/types/tensorflow.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/contentDisplay/services/contentService.ts b/src/alex_frontend/core/apps/Modules/LibModules/contentDisplay/services/contentService.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/contentDisplay/services/contentService.ts rename to src/alex_frontend/core/apps/Modules/LibModules/contentDisplay/services/contentService.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/contentDisplay/types.ts b/src/alex_frontend/core/apps/Modules/LibModules/contentDisplay/types.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/contentDisplay/types.ts rename to src/alex_frontend/core/apps/Modules/LibModules/contentDisplay/types.ts diff --git a/src/alex_frontend/src/apps/Modules/LibModules/nftSearch/PrincipalSelector.tsx b/src/alex_frontend/core/apps/Modules/LibModules/nftSearch/PrincipalSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/nftSearch/PrincipalSelector.tsx rename to src/alex_frontend/core/apps/Modules/LibModules/nftSearch/PrincipalSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/LibModules/nftSearch/collectionSelector.tsx b/src/alex_frontend/core/apps/Modules/LibModules/nftSearch/collectionSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/nftSearch/collectionSelector.tsx rename to src/alex_frontend/core/apps/Modules/LibModules/nftSearch/collectionSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/LibModules/nftSearch/index.tsx b/src/alex_frontend/core/apps/Modules/LibModules/nftSearch/index.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/nftSearch/index.tsx rename to src/alex_frontend/core/apps/Modules/LibModules/nftSearch/index.tsx diff --git a/src/alex_frontend/src/apps/Modules/LibModules/nftSearch/librarySearch.tsx b/src/alex_frontend/core/apps/Modules/LibModules/nftSearch/librarySearch.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/nftSearch/librarySearch.tsx rename to src/alex_frontend/core/apps/Modules/LibModules/nftSearch/librarySearch.tsx diff --git a/src/alex_frontend/src/apps/Modules/LibModules/nftSearch/tagSelector.tsx b/src/alex_frontend/core/apps/Modules/LibModules/nftSearch/tagSelector.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/LibModules/nftSearch/tagSelector.tsx rename to src/alex_frontend/core/apps/Modules/LibModules/nftSearch/tagSelector.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/adapters/TokenAdapter.ts b/src/alex_frontend/core/apps/Modules/shared/adapters/TokenAdapter.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/adapters/TokenAdapter.ts rename to src/alex_frontend/core/apps/Modules/shared/adapters/TokenAdapter.ts diff --git a/src/alex_frontend/core/apps/Modules/shared/components/AssetManager.tsx b/src/alex_frontend/core/apps/Modules/shared/components/AssetManager.tsx new file mode 100644 index 000000000..499a835ce --- /dev/null +++ b/src/alex_frontend/core/apps/Modules/shared/components/AssetManager.tsx @@ -0,0 +1,154 @@ +import { useAppDispatch } from "@/store/hooks/useAppDispatch"; +import { useAppSelector } from "@/store/hooks/useAppSelector"; +import React, { useEffect, useState } from "react"; +import { + createAssetCanister, + getCallerAssetCanister, + getAssetList, + syncNfts, + syncProgressInterface, + getCanisterCycles, +} from "../state/assetManager/assetManagerThunks"; +import { LoaderPinwheel } from "lucide-react"; +import { Button } from "@/lib/components/button"; +import { Description, FiltersButton } from "@/apps/Modules/shared/styles"; + +const AssetManager = () => { + const dispatch = useAppDispatch(); + const user = useAppSelector((state) => state.auth); + const nftData = useAppSelector((state) => state.nftData); + const assetManager = useAppSelector((state) => state.assetManager); + const { selectedPrincipals } = useAppSelector((state) => state.library); + const [userAssetCanister, setUserAssetCanister] = useState( + null + ); + const [syncProgress, setSyncProgress] = useState({ + currentItem: "", + progress: 0, // Default progress + totalSynced: 0, + currentProgress: 0, + }); + const createUserAssetCanister = () => { + if (!user.user?.principal) return; + dispatch(createAssetCanister({ userPrincipal: user.user.principal })); + }; + const sync = () => { + console.log("user AssetCanister", assetManager.userAssetCanister); + if (!user.user?.principal || !assetManager.userAssetCanister) return; + setSyncProgress({ + currentItem: "", + progress: 0, + totalSynced: 0, + currentProgress: 0, + }); + dispatch( + syncNfts({ + userPrincipal: user.user.principal, + syncProgress, + setSyncProgress, + userAssetCanister: assetManager.userAssetCanister, + }) + ); + console.log("syncing"); + }; + + useEffect(() => { + if (!assetManager.isLoading) { + console.log("getting caller asset canister"); + dispatch(getCallerAssetCanister()); + } + }, [user.user?.principal]); + + useEffect(() => { + setUserAssetCanister(assetManager.userAssetCanister); + if (assetManager.userAssetCanister) { + dispatch(getAssetList(assetManager.userAssetCanister)); + dispatch(getCanisterCycles(assetManager.userAssetCanister)); + sync(); + } + }, [assetManager.userAssetCanister]); + + return ( +
+ {userAssetCanister === null ? ( +
+ Asset Canister + +
+ ) : ( +
+ Asset Canister +

+ + {assetManager.userAssetCanister} + +

+ +

Cycles ≈ {assetManager.cycles}

+
+ )} + + {syncProgress?.currentItem !== "" && ( +
+

+ Synced :{syncProgress.totalSynced} +

+ + {/* Current Item Being Processed */} +
+ {syncProgress.currentProgress === 100 + ? "Upload Complete" + : `Uploading: ${syncProgress.currentItem}`} +
+ + {/* Current NFT Upload Progress Bar */} +
+
+
+ + {/* Total Synced Progress Bar */} +
+ Total Synced: {syncProgress.totalSynced ?? 0} +
+ +
+
+
+
+ )} +
+ ); +}; + +export default AssetManager; diff --git a/src/alex_frontend/src/apps/Modules/shared/components/AttachedDetailsPanel/AttachedDetailsPanel.tsx b/src/alex_frontend/core/apps/Modules/shared/components/AttachedDetailsPanel/AttachedDetailsPanel.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/AttachedDetailsPanel/AttachedDetailsPanel.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/AttachedDetailsPanel/AttachedDetailsPanel.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/components/AttachedDetailsPanel/NftAppearsInList.tsx b/src/alex_frontend/core/apps/Modules/shared/components/AttachedDetailsPanel/NftAppearsInList.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/AttachedDetailsPanel/NftAppearsInList.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/AttachedDetailsPanel/NftAppearsInList.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/components/ContentTypeToggleGroup.tsx b/src/alex_frontend/core/apps/Modules/shared/components/ContentTypeToggleGroup.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/ContentTypeToggleGroup.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/ContentTypeToggleGroup.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/components/MainContentDisplayModal/MainContentDisplayModal.tsx b/src/alex_frontend/core/apps/Modules/shared/components/MainContentDisplayModal/MainContentDisplayModal.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/MainContentDisplayModal/MainContentDisplayModal.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/MainContentDisplayModal/MainContentDisplayModal.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/components/NftDisplay/NftDisplay.tsx b/src/alex_frontend/core/apps/Modules/shared/components/NftDisplay/NftDisplay.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/NftDisplay/NftDisplay.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/NftDisplay/NftDisplay.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/components/NftDisplay/types.ts b/src/alex_frontend/core/apps/Modules/shared/components/NftDisplay/types.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/NftDisplay/types.ts rename to src/alex_frontend/core/apps/Modules/shared/components/NftDisplay/types.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/components/PrincipalDisplay.tsx b/src/alex_frontend/core/apps/Modules/shared/components/PrincipalDisplay.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/PrincipalDisplay.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/PrincipalDisplay.tsx diff --git a/src/alex_frontend/core/apps/Modules/shared/components/SearchContainer.tsx b/src/alex_frontend/core/apps/Modules/shared/components/SearchContainer.tsx new file mode 100644 index 000000000..3ea919704 --- /dev/null +++ b/src/alex_frontend/core/apps/Modules/shared/components/SearchContainer.tsx @@ -0,0 +1,186 @@ +import React, { + useState, + useEffect, + ReactNode, + useCallback, + useRef, +} from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "@/store"; +import { wipe } from "@/apps/Modules/shared/state/wiper"; +import Grid, { + GridDataSource, +} from "@/apps/Modules/AppModules/contentGrid/Grid"; +import { + PageContainer, + ControlsContainer, + FiltersButton, + SearchButton, + FiltersIcon, + SearchFormContainer, + Title, + Description, + Hint, +} from "../../../Modules/shared/styles"; +import { ArrowUp, LoaderPinwheel, RotateCcw, RotateCw } from "lucide-react"; +import { Button } from "@/lib/components/button"; + +interface SearchContainerProps { + title: string; + description?: string; + hint?: string; + onSearch: (continueFromTimestamp?: number) => Promise | void; + onShowMore?: () => Promise | void; + onCancel?: () => void; + isLoading?: boolean; + topComponent?: ReactNode; + filterComponent?: ReactNode; + showMoreEnabled?: boolean; + dataSource?: GridDataSource; + preserveState?: boolean; +} + +export function SearchContainer({ + title, + description, + hint, + onSearch, + onShowMore, + onCancel, + isLoading = false, + topComponent, + filterComponent, + showMoreEnabled = true, + dataSource, + preserveState = false, +}: SearchContainerProps) { + const dispatch = useDispatch(); + const [isFiltersOpen, setIsFiltersOpen] = useState(true); + const hasContentRef = useRef(false); + + // Select transactions from the appropriate state slice based on dataSource + const transactions = useSelector((state: RootState) => { + // Always use the new unified transactions state + return state.transactions.transactions; + }); + + // Track if content has been loaded to prevent wiping it + if (transactions.length > 0 && !hasContentRef.current) { + hasContentRef.current = true; + } + + const handleSearchClick = useCallback(async () => { + if (!isLoading) { + await onSearch(); + } + }, [isLoading, onSearch, dispatch]); + + const handleResetClick = useCallback(() => { + if (isLoading && onCancel) { + onCancel(); + } else if (onCancel) { + onCancel(); + } + }, [isLoading, onCancel, dispatch]); + + const handleShowMoreClick = useCallback(() => { + if (!isLoading && onShowMore) { + onShowMore(); + } + }, [isLoading, onShowMore]); + + const handleKeyPress = useCallback( + (event: KeyboardEvent) => { + if ( + event.target instanceof HTMLInputElement || + event.target instanceof HTMLTextAreaElement + ) { + return; + } + + if (event.key === "Enter" && !isLoading) { + handleSearchClick(); + } + }, + [isLoading, handleSearchClick] + ); + + useEffect(() => { + document.addEventListener("keypress", handleKeyPress); + return () => { + document.removeEventListener("keypress", handleKeyPress); + + // Only wipe state on unmount if: + // 1. preserveState is false (default behavior) + // 2. AND no content has been loaded + // This prevents wiping state after assets have loaded + if (!preserveState && !hasContentRef.current) { + dispatch(wipe()); + } + }; + }, [handleKeyPress, preserveState, dispatch]); + + return ( + <> + + {title} + {description && {description}} + {hint && {hint}} + {topComponent} + + setIsFiltersOpen(!isFiltersOpen)} + $isOpen={isFiltersOpen} + title="Toggle Filters" + > + {isFiltersOpen ? ( + + ) : ( + + )} + + + {isLoading ? ( + + ) : ( + "Search" + )} + + + + + + {filterComponent && ( + + {filterComponent} + + )} + + + {showMoreEnabled && transactions.length > 0 && ( +
+ +
+ )} + + ); +} diff --git a/src/alex_frontend/src/apps/Modules/shared/components/TensorFlowPreloader.tsx b/src/alex_frontend/core/apps/Modules/shared/components/TensorFlowPreloader.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/TensorFlowPreloader.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/TensorFlowPreloader.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/components/UnifiedCardActions/UnifiedCardActions.tsx b/src/alex_frontend/core/apps/Modules/shared/components/UnifiedCardActions/UnifiedCardActions.tsx similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/components/UnifiedCardActions/UnifiedCardActions.tsx rename to src/alex_frontend/core/apps/Modules/shared/components/UnifiedCardActions/UnifiedCardActions.tsx diff --git a/src/alex_frontend/src/apps/Modules/shared/hooks/getNftData.ts b/src/alex_frontend/core/apps/Modules/shared/hooks/getNftData.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/hooks/getNftData.ts rename to src/alex_frontend/core/apps/Modules/shared/hooks/getNftData.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/hooks/index.ts b/src/alex_frontend/core/apps/Modules/shared/hooks/index.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/hooks/index.ts rename to src/alex_frontend/core/apps/Modules/shared/hooks/index.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/hooks/useNftAppearsIn.ts b/src/alex_frontend/core/apps/Modules/shared/hooks/useNftAppearsIn.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/hooks/useNftAppearsIn.ts rename to src/alex_frontend/core/apps/Modules/shared/hooks/useNftAppearsIn.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/services/contentValidation.ts b/src/alex_frontend/core/apps/Modules/shared/services/contentValidation.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/services/contentValidation.ts rename to src/alex_frontend/core/apps/Modules/shared/services/contentValidation.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/services/nsfwService.ts b/src/alex_frontend/core/apps/Modules/shared/services/nsfwService.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/services/nsfwService.ts rename to src/alex_frontend/core/apps/Modules/shared/services/nsfwService.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/services/nullTensorflow.ts b/src/alex_frontend/core/apps/Modules/shared/services/nullTensorflow.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/services/nullTensorflow.ts rename to src/alex_frontend/core/apps/Modules/shared/services/nullTensorflow.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/services/tensorflowLoader.ts b/src/alex_frontend/core/apps/Modules/shared/services/tensorflowLoader.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/services/tensorflowLoader.ts rename to src/alex_frontend/core/apps/Modules/shared/services/tensorflowLoader.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/services/transactionService.ts b/src/alex_frontend/core/apps/Modules/shared/services/transactionService.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/services/transactionService.ts rename to src/alex_frontend/core/apps/Modules/shared/services/transactionService.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/arweave/arweaveSlice.ts b/src/alex_frontend/core/apps/Modules/shared/state/arweave/arweaveSlice.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/arweave/arweaveSlice.ts rename to src/alex_frontend/core/apps/Modules/shared/state/arweave/arweaveSlice.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/arweave/arweaveThunks.ts b/src/alex_frontend/core/apps/Modules/shared/state/arweave/arweaveThunks.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/arweave/arweaveThunks.ts rename to src/alex_frontend/core/apps/Modules/shared/state/arweave/arweaveThunks.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/assetManager/assetManagerSlice.ts b/src/alex_frontend/core/apps/Modules/shared/state/assetManager/assetManagerSlice.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/assetManager/assetManagerSlice.ts rename to src/alex_frontend/core/apps/Modules/shared/state/assetManager/assetManagerSlice.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/assetManager/assetManagerThunks.ts b/src/alex_frontend/core/apps/Modules/shared/state/assetManager/assetManagerThunks.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/assetManager/assetManagerThunks.ts rename to src/alex_frontend/core/apps/Modules/shared/state/assetManager/assetManagerThunks.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/assetManager/uploadToAssetCanister.tsx b/src/alex_frontend/core/apps/Modules/shared/state/assetManager/uploadToAssetCanister.tsx similarity index 98% rename from src/alex_frontend/src/apps/Modules/shared/state/assetManager/uploadToAssetCanister.tsx rename to src/alex_frontend/core/apps/Modules/shared/state/assetManager/uploadToAssetCanister.tsx index 99c5e8f6c..7566238c3 100644 --- a/src/alex_frontend/src/apps/Modules/shared/state/assetManager/uploadToAssetCanister.tsx +++ b/src/alex_frontend/core/apps/Modules/shared/state/assetManager/uploadToAssetCanister.tsx @@ -95,7 +95,7 @@ async function retryOperation( } } const calculateSHA256 = async (data: Uint8Array): Promise => { - const hashBuffer = await crypto.subtle.digest("SHA-256", data); + const hashBuffer = await crypto.subtle.digest("SHA-256", data as Uint8Array); return new Uint8Array(hashBuffer); }; diff --git a/src/alex_frontend/src/apps/Modules/shared/state/assetManager/utlis.ts b/src/alex_frontend/core/apps/Modules/shared/state/assetManager/utlis.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/assetManager/utlis.ts rename to src/alex_frontend/core/apps/Modules/shared/state/assetManager/utlis.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/content/contentSortUtils.ts b/src/alex_frontend/core/apps/Modules/shared/state/content/contentSortUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/content/contentSortUtils.ts rename to src/alex_frontend/core/apps/Modules/shared/state/content/contentSortUtils.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/librarySearch/librarySlice.ts b/src/alex_frontend/core/apps/Modules/shared/state/librarySearch/librarySlice.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/librarySearch/librarySlice.ts rename to src/alex_frontend/core/apps/Modules/shared/state/librarySearch/librarySlice.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/librarySearch/libraryThunks.ts b/src/alex_frontend/core/apps/Modules/shared/state/librarySearch/libraryThunks.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/librarySearch/libraryThunks.ts rename to src/alex_frontend/core/apps/Modules/shared/state/librarySearch/libraryThunks.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/nftData/nftDataSlice.ts b/src/alex_frontend/core/apps/Modules/shared/state/nftData/nftDataSlice.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/nftData/nftDataSlice.ts rename to src/alex_frontend/core/apps/Modules/shared/state/nftData/nftDataSlice.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/nftData/nftDataThunks.ts b/src/alex_frontend/core/apps/Modules/shared/state/nftData/nftDataThunks.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/nftData/nftDataThunks.ts rename to src/alex_frontend/core/apps/Modules/shared/state/nftData/nftDataThunks.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/transactions/transactionSlice.ts b/src/alex_frontend/core/apps/Modules/shared/state/transactions/transactionSlice.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/transactions/transactionSlice.ts rename to src/alex_frontend/core/apps/Modules/shared/state/transactions/transactionSlice.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/transactions/transactionSortUtils.ts b/src/alex_frontend/core/apps/Modules/shared/state/transactions/transactionSortUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/transactions/transactionSortUtils.ts rename to src/alex_frontend/core/apps/Modules/shared/state/transactions/transactionSortUtils.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/transactions/transactionThunks.ts b/src/alex_frontend/core/apps/Modules/shared/state/transactions/transactionThunks.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/transactions/transactionThunks.ts rename to src/alex_frontend/core/apps/Modules/shared/state/transactions/transactionThunks.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/state/wiper.ts b/src/alex_frontend/core/apps/Modules/shared/state/wiper.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/state/wiper.ts rename to src/alex_frontend/core/apps/Modules/shared/state/wiper.ts diff --git a/src/alex_frontend/src/apps/app/Permasearch/styles.ts b/src/alex_frontend/core/apps/Modules/shared/styles.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Permasearch/styles.ts rename to src/alex_frontend/core/apps/Modules/shared/styles.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/types/content.ts b/src/alex_frontend/core/apps/Modules/shared/types/content.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/types/content.ts rename to src/alex_frontend/core/apps/Modules/shared/types/content.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/types/files.ts b/src/alex_frontend/core/apps/Modules/shared/types/files.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/types/files.ts rename to src/alex_frontend/core/apps/Modules/shared/types/files.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/types/nft.ts b/src/alex_frontend/core/apps/Modules/shared/types/nft.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/types/nft.ts rename to src/alex_frontend/core/apps/Modules/shared/types/nft.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/types/queries.ts b/src/alex_frontend/core/apps/Modules/shared/types/queries.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/types/queries.ts rename to src/alex_frontend/core/apps/Modules/shared/types/queries.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/utils/nftOwner.ts b/src/alex_frontend/core/apps/Modules/shared/utils/nftOwner.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/utils/nftOwner.ts rename to src/alex_frontend/core/apps/Modules/shared/utils/nftOwner.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/utils/principalUtils.ts b/src/alex_frontend/core/apps/Modules/shared/utils/principalUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/utils/principalUtils.ts rename to src/alex_frontend/core/apps/Modules/shared/utils/principalUtils.ts diff --git a/src/alex_frontend/src/apps/Modules/shared/utils/tokenUtils.ts b/src/alex_frontend/core/apps/Modules/shared/utils/tokenUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/Modules/shared/utils/tokenUtils.ts rename to src/alex_frontend/core/apps/Modules/shared/utils/tokenUtils.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/BaseShelfList.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/BaseShelfList.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/BaseShelfList.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/BaseShelfList.tsx diff --git a/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ContentDisplays.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ContentDisplays.tsx new file mode 100644 index 000000000..9333bff42 --- /dev/null +++ b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ContentDisplays.tsx @@ -0,0 +1,126 @@ +import React from "react"; +import { ContentCard } from "@/apps/Modules/AppModules/contentGrid/Card"; +import { Badge } from "@/lib/components/badge"; +import MarkdownRenderer from "@/components/MarkdownRenderer"; + +type ContentDisplayProps = { + owner: string; + onClick: () => void; + parentShelfId?: string; + itemId?: number; + currentShelfId?: string; +}; + +// Display component for blog view markdown +export const BlogMarkdownDisplay = ({ + content, + onClick, +}: { + content: string; + onClick: () => void; +}) => ( +
+ +
+); + +// Display component for shelf content +export const ShelfContentDisplay = ({ + shelfId, + owner, + onClick, + parentShelfId, + itemId, + currentShelfId, +}: ContentDisplayProps & { shelfId: string }) => ( + + + Shelf + + + {shelfId} + +
+ } + > +
+
+
+
+ + + +
+
+ Shelf +
+
+ {shelfId} +
+
+
+
+ +); + +// Display component for markdown content +export const MarkdownContentDisplay = ({ + content, + owner, + onClick, + parentShelfId, + itemId, + currentShelfId, +}: ContentDisplayProps & { content: string }) => { + const preview = + content.length > 30 ? `${content.substring(0, 30)}...` : content; + + return ( + +
+
+
+ +
+
+
+
+ ); +}; diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/NftDisplay.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/NftDisplay.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/NftDisplay.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/NftDisplay.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfBlogView.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfBlogView.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfBlogView.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfBlogView.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfCard.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfCard.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfCard.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfCard.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfContentCard.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfContentCard.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfContentCard.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfContentCard.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfContentModal.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfContentModal.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfContentModal.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfContentModal.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfDetailView.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfDetailView.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfDetailView.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfDetailView.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfEmptyView.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfEmptyView.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfEmptyView.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfEmptyView.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfGridView.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfGridView.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfGridView.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfGridView.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfViewControls.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfViewControls.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfViewControls.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfViewControls.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfViewHeader.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfViewHeader.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/ShelfViewHeader.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/ShelfViewHeader.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/components/index.ts b/src/alex_frontend/core/apps/app/Perpetua/features/cards/components/index.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/components/index.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/components/index.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/hooks/index.ts b/src/alex_frontend/core/apps/app/Perpetua/features/cards/hooks/index.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/hooks/index.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/hooks/index.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/hooks/useNftData.ts b/src/alex_frontend/core/apps/app/Perpetua/features/cards/hooks/useNftData.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/hooks/useNftData.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/hooks/useNftData.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/utils/ShelfViewUtils.ts b/src/alex_frontend/core/apps/app/Perpetua/features/cards/utils/ShelfViewUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/utils/ShelfViewUtils.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/utils/ShelfViewUtils.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/cards/utils/itemUtils.ts b/src/alex_frontend/core/apps/app/Perpetua/features/cards/utils/itemUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/cards/utils/itemUtils.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/cards/utils/itemUtils.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/following/components/FollowedTagsList.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/following/components/FollowedTagsList.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/following/components/FollowedTagsList.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/following/components/FollowedTagsList.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/following/components/FollowedUserBadge.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/following/components/FollowedUserBadge.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/following/components/FollowedUserBadge.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/following/components/FollowedUserBadge.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/following/components/FollowedUsersList.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/following/components/FollowedUsersList.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/following/components/FollowedUsersList.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/following/components/FollowedUsersList.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/following/hooks/useFollowStatus.ts b/src/alex_frontend/core/apps/app/Perpetua/features/following/hooks/useFollowStatus.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/following/hooks/useFollowStatus.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/following/hooks/useFollowStatus.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ItemReorderManager.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ItemReorderManager.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ItemReorderManager.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ItemReorderManager.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ReorderableContainer.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ReorderableContainer.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ReorderableContainer.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ReorderableContainer.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ReorderableGrid.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ReorderableGrid.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ReorderableGrid.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ReorderableGrid.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ReorderableList.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ReorderableList.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/ReorderableList.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/ReorderableList.tsx diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/index.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/index.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/components/index.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/components/index.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/index.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/index.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/index.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/index.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useDragAndDrop.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useDragAndDrop.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useDragAndDrop.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useDragAndDrop.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useItemReordering.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useItemReordering.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useItemReordering.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useItemReordering.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useReorderable.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useReorderable.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useReorderable.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useReorderable.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useShelfReordering.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useShelfReordering.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/hooks/useShelfReordering.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/hooks/useShelfReordering.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/utils/createReorderAdapter.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/utils/createReorderAdapter.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/utils/createReorderAdapter.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/utils/createReorderAdapter.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/utils/reorderUtils.ts b/src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/utils/reorderUtils.ts similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shared/reordering/utils/reorderUtils.ts rename to src/alex_frontend/core/apps/app/Perpetua/features/shared/reordering/utils/reorderUtils.ts diff --git a/src/alex_frontend/src/apps/app/Perpetua/features/shelf-information/components/ShelfInformationDialog.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/shelf-information/components/ShelfInformationDialog.tsx similarity index 100% rename from src/alex_frontend/src/apps/app/Perpetua/features/shelf-information/components/ShelfInformationDialog.tsx rename to src/alex_frontend/core/apps/app/Perpetua/features/shelf-information/components/ShelfInformationDialog.tsx diff --git a/src/alex_frontend/core/apps/app/Perpetua/features/shelf-management/components/InlineItemCreator.tsx b/src/alex_frontend/core/apps/app/Perpetua/features/shelf-management/components/InlineItemCreator.tsx new file mode 100644 index 000000000..d2be8a864 --- /dev/null +++ b/src/alex_frontend/core/apps/app/Perpetua/features/shelf-management/components/InlineItemCreator.tsx @@ -0,0 +1,446 @@ +import React, { useState, useMemo, useEffect, useCallback } from "react"; +import { Button } from "@/lib/components/button"; +import { Label } from "@/lib/components/label"; +import { Textarea } from "@/lib/components/textarea"; +import { ShelfPublic, Item } from "@/../../declarations/perpetua/perpetua.did"; +import { X } from "lucide-react"; +import { useAppSelector } from "@/store/hooks/useAppSelector"; +import { useAppDispatch } from "@/store/hooks/useAppDispatch"; +import { + selectUserShelves, + selectSelectedShelf, + NormalizedShelf, +} from "@/apps/app/Perpetua/state/perpetuaSlice"; +import { toast } from "sonner"; +import { loadShelves } from "@/apps/app/Perpetua/state"; +import { useIdentity } from "@/lib/ic-use-identity"; +import AppCard from "@/components/AppCard"; +import { findApp } from "@/config/apps"; +import { usePerpetua } from "@/hooks/actors"; + +// Define the character limit constant +const MAX_MARKDOWN_LENGTH = 1000; + +type ContentType = "Markdown" | "Nft" | "Shelf"; + +interface InlineItemCreatorProps { + onSubmit: (content: string, type: "Markdown" | "Shelf") => Promise; + onCancel: () => void; + shelves?: ShelfPublic[] | NormalizedShelf[]; + shelf: any; +} + +const InlineItemCreator: React.FC = ({ + onSubmit, + onCancel, + shelves: propShelves, + shelf, +}) => { + // Core state + const [content, setContent] = useState(""); + const [type, setType] = useState("Markdown"); + const [isSubmitting, setIsSubmitting] = useState(false); + + // Shelf-specific state + const [selectedShelfId, setSelectedShelfId] = useState(""); + + // Selectors and hooks + const dispatch = useAppDispatch(); + const { identity } = useIdentity(); + const { actor } = usePerpetua(); + + // Only fetch shelves data when needed (when on Shelf tab) + const allShelves = useAppSelector( + type === "Shelf" ? selectUserShelves : () => [] + ); + const currentShelf = useAppSelector( + type === "Shelf" ? selectSelectedShelf : () => null + ); + + // Find the specific apps we need + const alexandrianApp = findApp("Alexandrian"); + const permasearchApp = findApp("Permasearch"); + const pinaxApp = findApp("Pinax"); + + // Reset form on type changes + useEffect(() => { + setContent(""); + setSelectedShelfId(""); + }, [type]); + + // Fetch shelves when the Shelf tab is selected - only on initial tab selection + useEffect(() => { + if (!actor) return; + if (type === "Shelf" && identity) { + try { + const principal = identity.getPrincipal().toString(); + // Dispatch action with a stable reference + const action = loadShelves({ + actor, + principal, + params: { offset: 0, limit: 20 }, + }); + dispatch(action); + } catch (err) { + console.error("Error loading shelves:", err); + } + } + }, [actor, type, identity]); // explicitly exclude dispatch + + // Filter available shelves that can be added as items + const availableShelves = useMemo(() => { + // Only process when on Shelf tab + if (type !== "Shelf") return []; + + // Use shelves provided as props if available + if (propShelves) { + return propShelves; + } + + // Get shelves from Redux + const userShelves = allShelves; + + // If we don't have any shelves or current shelf, return empty array + if (!userShelves || userShelves.length === 0) { + return []; + } + + // If no current shelf selected, show all shelves + if (!currentShelf) { + return userShelves; + } + + // Get IDs of shelves that are already added as items to current shelf + const shelvesInCurrentShelf = new Set(); + + if (currentShelf.items) { + currentShelf.items.forEach(([, item]: [number, Item]) => { + if (item.content && "Shelf" in item.content) { + shelvesInCurrentShelf.add(item.content.Shelf); + } + }); + } + + // Filter out: + // 1. The current shelf itself + // 2. Shelves that are already added as items + return userShelves.filter( + (shelf) => + shelf.shelf_id !== currentShelf.shelf_id && + !shelvesInCurrentShelf.has(shelf.shelf_id) + ); + }, [ + type, + allShelves?.length, // Use .length for dependency if allShelves itself is stable + currentShelf?.shelf_id, + currentShelf?.items, // Consider deep comparison or more specific dependencies if items structure changes often + propShelves, + ]); + + // Submit the current content as an item - memoized to prevent rerenders + const handleSubmit = useCallback(async () => { + // Explicitly handle only Markdown and Shelf types for submission + if (type === "Nft") { + console.warn( + "handleSubmit called on Nft tab, which should not happen." + ); + return; // Do nothing if on the Nft tab + } + + try { + setIsSubmitting(true); + + const finalContent = type === "Shelf" ? selectedShelfId : content; + + if (!finalContent) { + toast.error( + `Please ${type === "Markdown" ? "enter" : "select"} ${type.toLowerCase()} content` + ); + setIsSubmitting(false); // Release submitting state if validation fails + return; + } + + if (type === "Markdown") { + // Optimistic update for Markdown + const tempUiId = `optimistic_${Date.now()}`; // Generate a temporary ID + dispatch({ + type: "perpetua/optimisticMarkdownAddPending", // Replace with your actual action type + payload: { + shelfId: shelf.shelf_id, // Assuming shelf prop has shelf_id + item: { + type: "Markdown", + content: finalContent, + tempUiId: tempUiId, + // You might want to add other temporary fields like a timestamp or pending status + }, + }, + }); + } + + await onSubmit(finalContent, type as "Markdown" | "Shelf"); + // If onSubmit is successful, it should ideally dispatch an action to finalize the optimistic update, + // replacing the temporary item with the real one from the backend, using the tempUiId. + + setContent(""); + setSelectedShelfId(""); + // Toast message might need adjustment based on optimistic success vs. actual success + // For now, we keep it as is, assuming onSubmit handles its own success/error feedback + // or that the optimistic add is usually fast and reliable. + // toast.success(`Added ${type.toLowerCase()} content to shelf`); // Consider moving this or making it conditional + } catch (error: any) { + const errorMessage = + error?.message || + "You can't add a shelf that has this shelf inside it."; + toast.error(errorMessage); + // If an error occurs, dispatch an action to roll back the optimistic update for Markdown + if (type === "Markdown") { + // Assuming you have a tempUiId from the optimistic add step, you'd use it here. + // This part requires the tempUiId to be available in this catch block. + // For simplicity, this example doesn't pass tempUiId to the catch block, + // but in a real implementation, you'd need to handle this. + // A more robust way would be for onSubmit to handle its own rollback action dispatch + // upon failure, referencing the tempUiId if it was involved in an optimistic update. + // dispatch({ + // type: "perpetua/optimisticMarkdownAddRollback", // Replace with your actual rollback action + // payload: { shelfId: shelf.shelf_id, tempUiId: /* tempUiId from above */ }, + // }); + } + } finally { + setIsSubmitting(false); + } + }, [type, selectedShelfId, content, onSubmit, dispatch, shelf?.shelf_id]); // Added dispatch and shelf.shelf_id + + // Set content handler - memoized + const handleSetContent = useCallback( + (e: React.ChangeEvent) => { + setContent(e.target.value); + }, + [] + ); + + // Set shelf ID handler - memoized + const handleSetShelfId = useCallback( + (e: React.ChangeEvent) => { + setSelectedShelfId(e.target.value); + }, + [] + ); + + // Toggle content type handler + const handleTypeChange = useCallback((newType: ContentType) => { + setType(newType); + }, []); + + // Memoize the tab buttons to prevent unnecessary rerenders + const contentTypeTabs = useMemo( + () => ( +
+ {(["Markdown", "Nft", "Shelf"] as ContentType[]).map( + (contentType) => ( + + ) + )} +
+ ), + [type, handleTypeChange] + ); + + // Lazily render only the active form to prevent wasted renders + const renderActiveForm = () => { + switch (type) { + case "Markdown": + return ( +
+
+ +