From fdd1cbbfd596e40a3af6bfee658f9d36fd1fb468 Mon Sep 17 00:00:00 2001 From: bernard-ng Date: Sat, 8 Nov 2025 12:58:40 +0200 Subject: [PATCH] style(biome): using biome for format, lint, check --- .husky/pre-commit | 2 +- .vscode/settings.json | 18 +- apps/api-legacy/api/bruno.json | 9 +- apps/api-legacy/composer.json | 270 ++++---- apps/crawler/package.json | 10 +- apps/crawler/tsconfig.json | 2 +- apps/dashboard/app/globals.css | 4 +- apps/dashboard/app/layout.tsx | 12 +- apps/dashboard/app/page.tsx | 20 +- apps/dashboard/components.json | 26 +- apps/dashboard/next.config.ts | 4 +- apps/dashboard/package.json | 26 +- apps/dashboard/tsconfig.json | 12 +- apps/mobile-legacy/app.json | 68 +- apps/mobile-legacy/babel.config.js | 12 +- apps/mobile-legacy/commitlint.config.js | 42 +- apps/mobile-legacy/eas.json | 8 +- apps/mobile-legacy/eslint.config.js | 56 -- .../mobile-legacy/ios/Podfile.properties.json | 10 +- .../AppIcon.appiconset/Contents.json | 6 +- .../ios/basango/Images.xcassets/Contents.json | 6 +- .../Contents.json | 10 +- .../SplashScreenLogo.imageset/Contents.json | 12 +- apps/mobile-legacy/package.json | 104 +-- apps/mobile-legacy/src/api/client.ts | 152 ++--- apps/mobile-legacy/src/api/endpoint.ts | 62 +- .../api/request/feed-management/article.ts | 21 +- .../api/request/feed-management/bookmark.ts | 34 +- .../api/request/feed-management/comment.ts | 8 +- .../src/api/request/feed-management/source.ts | 27 +- .../api/request/identity-and-access/login.ts | 6 +- .../request/identity-and-access/password.ts | 12 +- .../request/identity-and-access/register.ts | 4 +- .../src/api/schema/feed-management/article.ts | 72 +-- .../api/schema/feed-management/bookmark.ts | 32 +- .../src/api/schema/feed-management/comment.ts | 16 +- .../src/api/schema/feed-management/source.ts | 80 +-- .../api/schema/identity-and-access/login.ts | 30 +- .../schema/identity-and-access/password.ts | 68 +- .../schema/identity-and-access/register.ts | 32 +- apps/mobile-legacy/src/api/shared.ts | 229 ++++--- .../src/app/(authed)/(tabs)/_layout.tsx | 146 +++-- .../app/(authed)/(tabs)/account/_layout.tsx | 2 +- .../src/app/(authed)/(tabs)/account/index.tsx | 40 +- .../(tabs)/account/settings/index.tsx | 46 +- .../src/app/(authed)/(tabs)/articles/[id].tsx | 133 ++-- .../app/(authed)/(tabs)/articles/_layout.tsx | 2 +- .../app/(authed)/(tabs)/articles/index.tsx | 69 +- .../app/(authed)/(tabs)/articles/trending.tsx | 51 +- .../app/(authed)/(tabs)/bookmarks/[id].tsx | 18 +- .../app/(authed)/(tabs)/bookmarks/_layout.tsx | 2 +- .../app/(authed)/(tabs)/bookmarks/index.tsx | 55 +- .../app/(authed)/(tabs)/sources/[name].tsx | 14 +- .../app/(authed)/(tabs)/sources/_layout.tsx | 2 +- .../src/app/(authed)/(tabs)/sources/index.tsx | 18 +- .../src/app/(authed)/_layout.tsx | 16 +- .../src/app/(unauthed)/_layout.tsx | 16 +- .../src/app/(unauthed)/password-request.tsx | 98 +-- .../src/app/(unauthed)/signin.tsx | 118 ++-- .../src/app/(unauthed)/signup.tsx | 116 ++-- .../src/app/(unauthed)/welcome.tsx | 54 +- apps/mobile-legacy/src/app/+not-found.tsx | 46 +- apps/mobile-legacy/src/app/_layout.tsx | 39 +- apps/mobile-legacy/src/app/index.tsx | 14 +- .../illustrations/BookmarkIllustration.tsx | 610 +++++++++--------- .../src/hooks/use-flattened-items.ts | 30 +- .../src/hooks/use-relative-time.ts | 91 ++- .../src/providers/auth-provider.tsx | 134 ++-- .../src/providers/fonts-loader-provider.tsx | 55 +- .../src/providers/network-provider.tsx | 43 +- .../src/providers/root-providers.tsx | 26 +- .../src/providers/tamagui-config-provider.tsx | 2 +- .../src/providers/tanstack-query-provider.tsx | 5 +- apps/mobile-legacy/src/store/auth.ts | 34 +- .../src/ui/components/AppIcon.tsx | 24 +- .../src/ui/components/LoadingView.tsx | 15 +- .../content/article/ArticleCategoryPill.tsx | 8 +- .../content/article/ArticleCoverImage.tsx | 18 +- .../content/article/ArticleList.tsx | 142 ++-- .../content/article/ArticleMagazineCard.tsx | 54 +- .../content/article/ArticleOverviewCard.tsx | 52 +- .../content/article/ArticleSkeleton.tsx | 180 +++--- .../content/article/ArticleTextOnlyCard.tsx | 52 +- .../ui/components/content/article/index.tsx | 2 +- .../content/bookmark/BookmarkCard.tsx | 64 +- .../content/bookmark/BookmarkEmptyState.tsx | 18 +- .../content/bookmark/BookmarkList.tsx | 80 +-- .../content/bookmark/CreateBookmarkSheet.tsx | 169 +++-- .../content/bookmark/UpdateBookmarkSheet.tsx | 185 +++--- .../content/source/SourceFollowButton.tsx | 96 +-- .../components/content/source/SourceList.tsx | 54 +- .../content/source/SourceOverviewCard.tsx | 110 ++-- .../content/source/SourceProfileImage.tsx | 40 +- .../content/source/SourceReferencePill.tsx | 48 +- .../content/source/SourceSkeleton.tsx | 108 ++-- .../src/ui/components/content/source/index.ts | 6 +- .../src/ui/components/controls/BackButton.tsx | 32 +- .../src/ui/components/controls/IconButton.tsx | 28 +- .../ui/components/controls/SubmitButton.tsx | 32 +- .../components/controls/forms/EmailInput.tsx | 30 +- .../ui/components/controls/forms/Input.tsx | 123 ++-- .../controls/forms/PasswordInput.tsx | 51 +- .../ui/components/controls/forms/Switch.tsx | 44 +- .../ui/components/controls/forms/TextArea.tsx | 78 +-- .../components/controls/forms/TextInput.tsx | 22 +- .../ui/components/controls/forms/index.tsx | 8 +- .../controls/forms/withController.tsx | 68 +- .../ui/components/layout/ScreenHeading.tsx | 86 +-- .../ui/components/layout/ScreenSection.tsx | 57 +- .../src/ui/components/layout/ScreenView.tsx | 101 +-- .../src/ui/components/typography/Caption.tsx | 12 +- .../src/ui/components/typography/Display.tsx | 12 +- .../src/ui/components/typography/Heading.tsx | 12 +- .../src/ui/components/typography/Text.tsx | 4 +- .../src/ui/components/typography/index.ts | 2 +- apps/mobile-legacy/tamagui.config.ts | 4 +- apps/mobile-legacy/themes.ts | 199 +++--- apps/mobile-legacy/tsconfig.json | 23 +- apps/mobile/app.json | 44 +- apps/mobile/app/index.tsx | 2 +- apps/mobile/package.json | 28 +- apps/mobile/tsconfig.json | 17 +- biome.json | 81 ++- bun.lock | 277 +------- package.json | 97 ++- packages/db/drizzle.config.ts | 6 +- packages/db/package.json | 27 +- packages/db/src/client.ts | 26 +- packages/db/src/queries/articles.ts | 118 ++-- packages/db/src/queries/bookmarks.ts | 10 +- packages/db/src/queries/sources.ts | 66 +- packages/db/src/queries/users.ts | 4 +- packages/db/src/schema.ts | 126 ++-- packages/db/src/utils/pagination.ts | 4 +- packages/db/tsconfig.json | 8 +- packages/logger/package.json | 27 +- packages/logger/src/index.ts | 6 +- packages/logger/tsconfig.json | 6 +- .../{typescript-config => tsconfig}/base.json | 4 +- .../nextjs.json | 14 +- .../package.json | 4 +- .../react-library.json | 6 +- packages/ui/components.json | 26 +- packages/ui/postcss.config.mjs | 2 +- packages/ui/src/components/button.tsx | 72 +-- packages/ui/src/lib/utils.ts | 6 +- packages/ui/src/package.json | 24 +- packages/ui/src/styles/global.css | 218 +++---- packages/ui/tsconfig.json | 6 +- packages/ui/tsconfig.lint.json | 6 +- tsconfig.json | 2 +- turbo.json | 24 +- 152 files changed, 3737 insertions(+), 3989 deletions(-) delete mode 100644 apps/mobile-legacy/eslint.config.js rename packages/{typescript-config => tsconfig}/base.json (94%) rename packages/{typescript-config => tsconfig}/nextjs.json (70%) rename packages/{typescript-config => tsconfig}/package.json (71%) rename packages/{typescript-config => tsconfig}/react-library.json (80%) diff --git a/.husky/pre-commit b/.husky/pre-commit index 62eae82..cff4587 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -bun run check-types && bun run check && bun run lint:check +bun run typecheck && bun run lint diff --git a/.vscode/settings.json b/.vscode/settings.json index 9265382..362a023 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "editor.defaultFormatter": "biomejs.biome", "[javascript]": { "editor.defaultFormatter": "biomejs.biome" }, @@ -10,21 +9,20 @@ "editor.defaultFormatter": "biomejs.biome" }, "editor.codeActionsOnSave": { - "quickfix.biome": "explicit", "source.organizeImports.biome": "explicit", - "source.fixAll": "explicit", - "source.organizeImports": "explicit", - "source.sortMembers": "explicit" + "source.fixAll.biome": "explicit" }, + "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true, + "files.autoSave": "onFocusChange", + "search.exclude": { + "**/node_modules": true + }, + "terminal.integrated.localEchoStyle": "dim", "typescript.enablePromptUseWorkspaceTsdk": true, - "typescript.tsdk": "node_modules/typescript/lib", "typescript.preferences.autoImportFileExcludePatterns": [ "next/router.d.ts", "next/dist/client/router.d.ts" ], - "terminal.integrated.localEchoStyle": "dim", - "search.exclude": { - "**/node_modules": true - } + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/apps/api-legacy/api/bruno.json b/apps/api-legacy/api/bruno.json index c15b8c0..fc9f340 100644 --- a/apps/api-legacy/api/bruno.json +++ b/apps/api-legacy/api/bruno.json @@ -1,9 +1,6 @@ { - "version": "1", + "ignore": ["node_modules", ".git"], "name": "basango", "type": "collection", - "ignore": [ - "node_modules", - ".git" - ] -} \ No newline at end of file + "version": "1" +} diff --git a/apps/api-legacy/composer.json b/apps/api-legacy/composer.json index b0b27a2..8e86331 100644 --- a/apps/api-legacy/composer.json +++ b/apps/api-legacy/composer.json @@ -1,141 +1,135 @@ { - "type": "project", - "license": "proprietary", - "minimum-stability": "stable", - "prefer-stable": true, - "require": { - "php": ">=8.4", - "ext-ctype": "*", - "ext-dom": "*", - "ext-iconv": "*", - "cweagans/composer-patches": "^1.7.3", - "doctrine/dbal": "^3.9.4", - "doctrine/doctrine-bundle": "^2.13.2", - "doctrine/doctrine-migrations-bundle": "^3.4.1", - "doctrine/orm": "^3.3.1", - "geoip2/geoip2": "^3.1", - "gesdinet/jwt-refresh-token-bundle": "^1.4", - "knplabs/knp-paginator-bundle": "^6.7", - "league/csv": "^9.21", - "lexik/jwt-authentication-bundle": "^3.1", - "martin-georgiev/postgresql-for-doctrine": "^3.5", - "matomo/device-detector": "^6.4", - "phpdocumentor/reflection-docblock": "^5.6", - "phpstan/phpdoc-parser": "^2.1", - "sentry/sentry-symfony": "^5.2", - "symfony/console": "7.2.*", - "symfony/css-selector": "7.2.*", - "symfony/dom-crawler": "7.2.*", - "symfony/dotenv": "7.2.*", - "symfony/flex": "^2.4.7", - "symfony/framework-bundle": "7.2.*", - "symfony/http-client": "7.2.*", - "symfony/mailer": "7.2.*", - "symfony/messenger": "7.2.*", - "symfony/monolog-bundle": "^3.10", - "symfony/property-access": "7.2.*", - "symfony/property-info": "7.2.*", - "symfony/runtime": "7.2.*", - "symfony/security-bundle": "7.2.*", - "symfony/serializer": "7.2.*", - "symfony/stopwatch": "7.2.*", - "symfony/twig-bundle": "7.2.*", - "symfony/uid": "7.2.*", - "symfony/validator": "7.2.*", - "symfony/yaml": "7.2.*", - "twig/extra-bundle": "^2.12|^3.19", - "twig/twig": "^2.12|^3.19", - "webmozart/assert": "^1.11" - }, - "require-dev": { - "behat/behat": "^3.22", - "deptrac/deptrac": "^2.0.7", - "doctrine/doctrine-fixtures-bundle": "^4.1", - "friends-of-behat/symfony-extension": "^2.6", - "phpstan/phpstan": "^2.1.12", - "phpstan/phpstan-doctrine": "^2.0", - "phpstan/phpstan-symfony": "^2.0", - "phpunit/phpunit": "^12.1.1", - "rector/rector": "^2.0.12", - "shipmonk/composer-dependency-analyser": "^1.8.2", - "symfony/maker-bundle": "^1.62.1", - "symfony/web-profiler-bundle": "7.2.*", - "symplify/easy-coding-standard": "^12.1.13", - "tomasvotruba/class-leak": "^1.2.7" - }, - "config": { - "allow-plugins": { - "php-http/discovery": true, - "symfony/flex": true, - "symfony/runtime": true, - "cweagans/composer-patches": true - }, - "sort-packages": true - }, - "autoload": { - "psr-4": { - "Basango\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "replace": { - "symfony/polyfill-ctype": "*", - "symfony/polyfill-iconv": "*", - "symfony/polyfill-php72": "*", - "symfony/polyfill-php73": "*", - "symfony/polyfill-php74": "*", - "symfony/polyfill-php80": "*", - "symfony/polyfill-php81": "*" - }, - "scripts": { - "auto-scripts": { - "cache:clear": "symfony-cmd", - "assets:install %PUBLIC_DIR%": "symfony-cmd" - }, - "post-install-cmd": [ - "@auto-scripts" - ], - "post-update-cmd": [ - "@auto-scripts" - ], - "app:cs": [ - "./vendor/bin/ecs check", - "bin/console lint:yaml config --parse-tags", - "bin/console lint:twig templates", - "bin/console lint:container", - "./vendor/bin/phpstan analyse --memory-limit=-1 --configuration=phpstan.dist.neon", - "./vendor/bin/rector --dry-run" - ], - "app:tests": [ - "APP_ENV=test ./vendor/bin/phpunit" - ], - "app:behat": [ - "APP_ENV=test bin/console doctrine:database:create", - "APP_ENV=test bin/console doctrine:migration:migrate --no-interaction --allow-no-migration --all-or-nothing", - "APP_ENV=test ./vendor/bin/behat --format=progress --no-interaction" - ], - "app:env": [ - "APP_RUNTIME_ENV=prod bin/console secrets:decrypt-to-local --force", - "bin/console dotenv:dump prod" - ] - }, - "conflict": { - "symfony/symfony": "*" - }, - "extra": { - "symfony": { - "allow-contrib": false, - "require": "7.2.*" - }, - "patches": { - "symfony/monolog-bundle": { - "support telegram topic in configuration": "./patches/monolog-telegram-configuration.patch", - "support telegram topic in extension": "./patches/monolog-telegram-extension.patch" - } - } + "autoload": { + "psr-4": { + "Basango\\": "src/" } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "config": { + "allow-plugins": { + "cweagans/composer-patches": true, + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "sort-packages": true + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "patches": { + "symfony/monolog-bundle": { + "support telegram topic in configuration": "./patches/monolog-telegram-configuration.patch", + "support telegram topic in extension": "./patches/monolog-telegram-extension.patch" + } + }, + "symfony": { + "allow-contrib": false, + "require": "7.2.*" + } + }, + "license": "proprietary", + "minimum-stability": "stable", + "prefer-stable": true, + "replace": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*" + }, + "require": { + "cweagans/composer-patches": "^1.7.3", + "doctrine/dbal": "^3.9.4", + "doctrine/doctrine-bundle": "^2.13.2", + "doctrine/doctrine-migrations-bundle": "^3.4.1", + "doctrine/orm": "^3.3.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-iconv": "*", + "geoip2/geoip2": "^3.1", + "gesdinet/jwt-refresh-token-bundle": "^1.4", + "knplabs/knp-paginator-bundle": "^6.7", + "league/csv": "^9.21", + "lexik/jwt-authentication-bundle": "^3.1", + "martin-georgiev/postgresql-for-doctrine": "^3.5", + "matomo/device-detector": "^6.4", + "php": ">=8.4", + "phpdocumentor/reflection-docblock": "^5.6", + "phpstan/phpdoc-parser": "^2.1", + "sentry/sentry-symfony": "^5.2", + "symfony/console": "7.2.*", + "symfony/css-selector": "7.2.*", + "symfony/dom-crawler": "7.2.*", + "symfony/dotenv": "7.2.*", + "symfony/flex": "^2.4.7", + "symfony/framework-bundle": "7.2.*", + "symfony/http-client": "7.2.*", + "symfony/mailer": "7.2.*", + "symfony/messenger": "7.2.*", + "symfony/monolog-bundle": "^3.10", + "symfony/property-access": "7.2.*", + "symfony/property-info": "7.2.*", + "symfony/runtime": "7.2.*", + "symfony/security-bundle": "7.2.*", + "symfony/serializer": "7.2.*", + "symfony/stopwatch": "7.2.*", + "symfony/twig-bundle": "7.2.*", + "symfony/uid": "7.2.*", + "symfony/validator": "7.2.*", + "symfony/yaml": "7.2.*", + "twig/extra-bundle": "^2.12|^3.19", + "twig/twig": "^2.12|^3.19", + "webmozart/assert": "^1.11" + }, + "require-dev": { + "behat/behat": "^3.22", + "deptrac/deptrac": "^2.0.7", + "doctrine/doctrine-fixtures-bundle": "^4.1", + "friends-of-behat/symfony-extension": "^2.6", + "phpstan/phpstan": "^2.1.12", + "phpstan/phpstan-doctrine": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpunit/phpunit": "^12.1.1", + "rector/rector": "^2.0.12", + "shipmonk/composer-dependency-analyser": "^1.8.2", + "symfony/maker-bundle": "^1.62.1", + "symfony/web-profiler-bundle": "7.2.*", + "symplify/easy-coding-standard": "^12.1.13", + "tomasvotruba/class-leak": "^1.2.7" + }, + "scripts": { + "app:behat": [ + "APP_ENV=test bin/console doctrine:database:create", + "APP_ENV=test bin/console doctrine:migration:migrate --no-interaction --allow-no-migration --all-or-nothing", + "APP_ENV=test ./vendor/bin/behat --format=progress --no-interaction" + ], + "app:cs": [ + "./vendor/bin/ecs check", + "bin/console lint:yaml config --parse-tags", + "bin/console lint:twig templates", + "bin/console lint:container", + "./vendor/bin/phpstan analyse --memory-limit=-1 --configuration=phpstan.dist.neon", + "./vendor/bin/rector --dry-run" + ], + "app:env": [ + "APP_RUNTIME_ENV=prod bin/console secrets:decrypt-to-local --force", + "bin/console dotenv:dump prod" + ], + "app:tests": ["APP_ENV=test ./vendor/bin/phpunit"], + "auto-scripts": { + "assets:install %PUBLIC_DIR%": "symfony-cmd", + "cache:clear": "symfony-cmd" + }, + "post-install-cmd": ["@auto-scripts"], + "post-update-cmd": ["@auto-scripts"] + }, + "type": "project" } diff --git a/apps/crawler/package.json b/apps/crawler/package.json index d954f83..4cd2ea9 100644 --- a/apps/crawler/package.json +++ b/apps/crawler/package.json @@ -1,6 +1,4 @@ { - "name": "@basango/crawler", - "private": true, "dependencies": { "@basango/logger": "workspace:*", "@devscast/config": "^1.0.3", @@ -16,16 +14,18 @@ "@types/turndown": "^5.0.6", "vitest": "^4.0.7" }, + "name": "@basango/crawler", + "private": true, "scripts": { + "clean": "rm -rf .turbo node_modules", "crawler:async": "bun run src/scripts/queue.ts", "crawler:sync": "bun run src/scripts/crawl.ts", "crawler:worker": "bun run src/scripts/worker.ts", - "clean": "rm -rf .turbo node_modules", "format": "biome format --write .", "lint": "biome check .", "lint:fix": "biome check --write .", - "typecheck": "tsc --noEmit", - "test": "vitest --run" + "test": "vitest --run", + "typecheck": "tsc --noEmit" }, "type": "module" } diff --git a/apps/crawler/tsconfig.json b/apps/crawler/tsconfig.json index 68c0060..d14326b 100644 --- a/apps/crawler/tsconfig.json +++ b/apps/crawler/tsconfig.json @@ -5,7 +5,7 @@ "@/*": ["./src/*"] } }, - "extends": "@basango/typescript-config/base.json", + "extends": "@basango/tsconfig/base.json", "include": ["src"], "references": [] } diff --git a/apps/dashboard/app/globals.css b/apps/dashboard/app/globals.css index a2dc41e..4655bcd 100644 --- a/apps/dashboard/app/globals.css +++ b/apps/dashboard/app/globals.css @@ -20,7 +20,7 @@ } body { - background: var(--background); - color: var(--foreground); font-family: Arial, Helvetica, sans-serif; + color: var(--foreground); + background: var(--background); } diff --git a/apps/dashboard/app/layout.tsx b/apps/dashboard/app/layout.tsx index f7fa87e..918a068 100644 --- a/apps/dashboard/app/layout.tsx +++ b/apps/dashboard/app/layout.tsx @@ -3,18 +3,18 @@ import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; const geistSans = Geist({ - variable: "--font-geist-sans", subsets: ["latin"], + variable: "--font-geist-sans", }); const geistMono = Geist_Mono({ - variable: "--font-geist-mono", subsets: ["latin"], + variable: "--font-geist-mono", }); export const metadata: Metadata = { - title: "Create Next App", description: "Generated by create next app", + title: "Create Next App", }; export default function RootLayout({ @@ -24,11 +24,7 @@ export default function RootLayout({ }>) { return ( - - {children} - + {children} ); } diff --git a/apps/dashboard/app/page.tsx b/apps/dashboard/app/page.tsx index 295f8fd..8c463b8 100644 --- a/apps/dashboard/app/page.tsx +++ b/apps/dashboard/app/page.tsx @@ -5,12 +5,12 @@ export default function Home() {
Next.js logo

@@ -19,15 +19,15 @@ export default function Home() {

Looking for a starting point or more instructions? Head over to{" "} Templates {" "} or the{" "} Learning {" "} @@ -38,23 +38,23 @@ export default function Home() { Vercel logomark Deploy Now Documentation diff --git a/apps/dashboard/components.json b/apps/dashboard/components.json index e8fe6ac..64644b3 100644 --- a/apps/dashboard/components.json +++ b/apps/dashboard/components.json @@ -1,20 +1,20 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "../../packages/ui/src/styles/globals.css", - "baseColor": "neutral", - "cssVariables": true - }, - "iconLibrary": "lucide", "aliases": { "components": "@/components", "hooks": "@/hooks", "lib": "@/lib", - "utils": "@basango/ui/lib/utils", - "ui": "@basango/ui/components" - } + "ui": "@basango/ui/components", + "utils": "@basango/ui/lib/utils" + }, + "iconLibrary": "lucide", + "rsc": true, + "style": "new-york", + "tailwind": { + "baseColor": "neutral", + "config": "", + "css": "../../packages/ui/src/styles/globals.css", + "cssVariables": true + }, + "tsx": true } diff --git a/apps/dashboard/next.config.ts b/apps/dashboard/next.config.ts index 938dbfc..212c17e 100644 --- a/apps/dashboard/next.config.ts +++ b/apps/dashboard/next.config.ts @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ["@basango/ui"], -} +}; -export default nextConfig +export default nextConfig; diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 485c5e8..30d4bf4 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -1,23 +1,23 @@ { - "name": "@basango/dashboard", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint" - }, "dependencies": { + "next": "catalog:", "react": "catalog:", - "react-dom": "catalog:", - "next": "catalog:" + "react-dom": "catalog:" }, "devDependencies": { - "typescript": "catalog:", + "@tailwindcss/postcss": "^4", "@types/bun": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4" + "tailwindcss": "^4", + "typescript": "catalog:" + }, + "name": "@basango/dashboard", + "private": true, + "scripts": { + "build": "next build", + "dev": "next dev", + "lint": "eslint", + "start": "next start" } } diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index cd8a37e..31bf14d 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -1,5 +1,4 @@ { - "extends": "@basango/typescript-config/nextjs.json", "compilerOptions": { "baseUrl": ".", "paths": { @@ -12,12 +11,7 @@ } ] }, - "include": [ - "next-env.d.ts", - "next.config.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": ["node_modules"] + "exclude": ["node_modules"], + "extends": "@basango/tsconfig/nextjs.json", + "include": ["next-env.d.ts", "next.config.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"] } diff --git a/apps/mobile-legacy/app.json b/apps/mobile-legacy/app.json index fc39fc9..82adf55 100644 --- a/apps/mobile-legacy/app.json +++ b/apps/mobile-legacy/app.json @@ -1,40 +1,43 @@ { "expo": { - "name": "basango", - "slug": "basango", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./src/assets/images/logo.png", - "scheme": "basango", - "userInterfaceStyle": "automatic", - "newArchEnabled": true, - "githubUrl": "https://github.com/bernard-ng/basango", - "ios": { - "supportsTablet": true, - "bundleIdentifier": "dev.ngandu.basango" - }, "android": { "adaptiveIcon": { - "foregroundImage": "./src/assets/images/logo.png", "backgroundColor": "#ffffff", + "foregroundImage": "./src/assets/images/logo.png", "package": "dev.ngandu.basango" }, "package": "dev.ngandu.basango" }, - "web": { - "bundler": "metro", - "output": "static", - "favicon": "./src/assets/images/logo.png" + "experiments": { + "typedRoutes": true }, + "extra": { + "eas": { + "projectId": "57281e7a-46e3-4aac-8715-5165fa0bf560" + }, + "router": { + "origin": false + } + }, + "githubUrl": "https://github.com/bernard-ng/basango", + "icon": "./src/assets/images/logo.png", + "ios": { + "bundleIdentifier": "dev.ngandu.basango", + "supportsTablet": true + }, + "name": "basango", + "newArchEnabled": true, + "orientation": "portrait", + "owner": "bernard-ng", "plugins": [ "expo-router", [ "expo-splash-screen", { + "backgroundColor": "#ffffff", "image": "./src/assets/images/logo.png", "imageWidth": 200, - "resizeMode": "contain", - "backgroundColor": "#ffffff" + "resizeMode": "contain" } ], "expo-build-properties", @@ -42,23 +45,20 @@ [ "@sentry/react-native/expo", { - "url": "https://glitchtip.devscast.tech/", + "organization": "devscast-software", "project": "basango", - "organization": "devscast-software" + "url": "https://glitchtip.devscast.tech/" } ] ], - "experiments": { - "typedRoutes": true - }, - "extra": { - "router": { - "origin": false - }, - "eas": { - "projectId": "57281e7a-46e3-4aac-8715-5165fa0bf560" - } - }, - "owner": "bernard-ng" + "scheme": "basango", + "slug": "basango", + "userInterfaceStyle": "automatic", + "version": "1.0.0", + "web": { + "bundler": "metro", + "favicon": "./src/assets/images/logo.png", + "output": "static" + } } } diff --git a/apps/mobile-legacy/babel.config.js b/apps/mobile-legacy/babel.config.js index b21c172..9ee4747 100644 --- a/apps/mobile-legacy/babel.config.js +++ b/apps/mobile-legacy/babel.config.js @@ -1,7 +1,7 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: [["babel-preset-expo", { jsxRuntime: "automatic" }]], - plugins: ["react-native-reanimated/plugin"], - }; +module.exports = (api) => { + api.cache(true); + return { + plugins: ["react-native-reanimated/plugin"], + presets: [["babel-preset-expo", { jsxRuntime: "automatic" }]], + }; }; diff --git a/apps/mobile-legacy/commitlint.config.js b/apps/mobile-legacy/commitlint.config.js index ef93365..3185047 100644 --- a/apps/mobile-legacy/commitlint.config.js +++ b/apps/mobile-legacy/commitlint.config.js @@ -1,23 +1,23 @@ module.exports = { - extends: ["@commitlint/config-conventional"], - rules: { - "type-enum": [ - 2, - "always", - [ - "feat", // New feature - "fix", // Bug fix - "docs", // Documentation only changes - "style", // Changes that do not affect the meaning of the code (white-space, formatting, etc) - "refactor", // Code changes that neither fixes a bug nor adds a feature - "perf", // Performance improvements - "test", // Adding missing tests or correcting existing tests - "build", // Changes that affect the build system or external dependencies - "ci", // Changes to CI configuration files and scripts - "chore", // Other changes that don't modify src or test files - "revert", // Reverts a previous commit - ], - ], - "subject-case": [2, "never", ["sentence-case", "start-case", "pascal-case", "upper-case"]], - }, + extends: ["@commitlint/config-conventional"], + rules: { + "subject-case": [2, "never", ["sentence-case", "start-case", "pascal-case", "upper-case"]], + "type-enum": [ + 2, + "always", + [ + "feat", // New feature + "fix", // Bug fix + "docs", // Documentation only changes + "style", // Changes that do not affect the meaning of the code (white-space, formatting, etc) + "refactor", // Code changes that neither fixes a bug nor adds a feature + "perf", // Performance improvements + "test", // Adding missing tests or correcting existing tests + "build", // Changes that affect the build system or external dependencies + "ci", // Changes to CI configuration files and scripts + "chore", // Other changes that don't modify src or test files + "revert", // Reverts a previous commit + ], + ], + }, }; diff --git a/apps/mobile-legacy/eas.json b/apps/mobile-legacy/eas.json index a1a498e..b7967bb 100644 --- a/apps/mobile-legacy/eas.json +++ b/apps/mobile-legacy/eas.json @@ -1,8 +1,4 @@ { - "cli": { - "version": ">= 16.3.1", - "appVersionSource": "remote" - }, "build": { "development": { "developmentClient": true, @@ -15,6 +11,10 @@ "autoIncrement": true } }, + "cli": { + "appVersionSource": "remote", + "version": ">= 16.3.1" + }, "submit": { "production": {} } diff --git a/apps/mobile-legacy/eslint.config.js b/apps/mobile-legacy/eslint.config.js deleted file mode 100644 index 6d5fe64..0000000 --- a/apps/mobile-legacy/eslint.config.js +++ /dev/null @@ -1,56 +0,0 @@ -const { defineConfig } = require("eslint/config"); -const expoConfig = require("eslint-config-expo/flat"); -const prettierPlugin = require("eslint-plugin-prettier"); -const unusedImportsPlugin = require("eslint-plugin-unused-imports"); - -module.exports = defineConfig([ - expoConfig, - { - plugins: { - prettier: prettierPlugin, - "unused-imports": unusedImportsPlugin, - }, - rules: { - "import/default": "off", - "react/prop-types": "off", - "react/react-in-jsx-scope": "off", - "import/named": "off", - "import/namespace": "error", - "import/export": "error", - "no-unused-vars": "off", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { - vars: "all", - varsIgnorePattern: "^_", - args: "after-used", - argsIgnorePattern: "^_", - }, - ], - "import/order": [ - "error", - { - groups: ["builtin", "external", "internal"], - pathGroups: [ - { - pattern: "react", - group: "external", - position: "before", - }, - ], - pathGroupsExcludedImportTypes: ["react"], - "newlines-between": "always", - alphabetize: { - order: "asc", - caseInsensitive: true, - }, - }, - ], - "prettier/prettier": "error", - }, - }, - { - ignores: ["dist/*"], - }, -]); diff --git a/apps/mobile-legacy/ios/Podfile.properties.json b/apps/mobile-legacy/ios/Podfile.properties.json index 5f99b5d..4f49b45 100644 --- a/apps/mobile-legacy/ios/Podfile.properties.json +++ b/apps/mobile-legacy/ios/Podfile.properties.json @@ -1,8 +1,8 @@ { - "expo.jsEngine": "hermes", - "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", - "newArchEnabled": "true", - "apple.extraPods": "[]", "apple.ccacheEnabled": "false", - "apple.privacyManifestAggregationEnabled": "true" + "apple.extraPods": "[]", + "apple.privacyManifestAggregationEnabled": "true", + "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", + "expo.jsEngine": "hermes", + "newArchEnabled": "true" } diff --git a/apps/mobile-legacy/ios/basango/Images.xcassets/AppIcon.appiconset/Contents.json b/apps/mobile-legacy/ios/basango/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c..4da364a 100644 --- a/apps/mobile-legacy/ios/basango/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/apps/mobile-legacy/ios/basango/Images.xcassets/AppIcon.appiconset/Contents.json @@ -8,7 +8,7 @@ } ], "info": { - "version": 1, - "author": "expo" + "author": "expo", + "version": 1 } -} \ No newline at end of file +} diff --git a/apps/mobile-legacy/ios/basango/Images.xcassets/Contents.json b/apps/mobile-legacy/ios/basango/Images.xcassets/Contents.json index ed285c2..0b946fb 100644 --- a/apps/mobile-legacy/ios/basango/Images.xcassets/Contents.json +++ b/apps/mobile-legacy/ios/basango/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { - "info" : { - "version" : 1, - "author" : "expo" + "info": { + "author": "expo", + "version": 1 } } diff --git a/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenBackground.colorset/Contents.json b/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenBackground.colorset/Contents.json index 15f02ab..c640711 100644 --- a/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenBackground.colorset/Contents.json +++ b/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenBackground.colorset/Contents.json @@ -2,19 +2,19 @@ "colors": [ { "color": { + "color-space": "srgb", "components": { "alpha": "1.000", "blue": "1.00000000000000", "green": "1.00000000000000", "red": "1.00000000000000" - }, - "color-space": "srgb" + } }, "idiom": "universal" } ], "info": { - "version": 1, - "author": "expo" + "author": "expo", + "version": 1 } -} \ No newline at end of file +} diff --git a/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenLogo.imageset/Contents.json b/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenLogo.imageset/Contents.json index f65c008..f5ad720 100644 --- a/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenLogo.imageset/Contents.json +++ b/apps/mobile-legacy/ios/basango/Images.xcassets/SplashScreenLogo.imageset/Contents.json @@ -1,23 +1,23 @@ { "images": [ { - "idiom": "universal", "filename": "image.png", + "idiom": "universal", "scale": "1x" }, { - "idiom": "universal", "filename": "image@2x.png", + "idiom": "universal", "scale": "2x" }, { - "idiom": "universal", "filename": "image@3x.png", + "idiom": "universal", "scale": "3x" } ], "info": { - "version": 1, - "author": "expo" + "author": "expo", + "version": 1 } -} \ No newline at end of file +} diff --git a/apps/mobile-legacy/package.json b/apps/mobile-legacy/package.json index 518c874..49d7359 100644 --- a/apps/mobile-legacy/package.json +++ b/apps/mobile-legacy/package.json @@ -1,49 +1,4 @@ { - "name": "drc-news", - "main": "expo-router/entry", - "version": "1.0.0", - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "test": "jest --watchAll", - "============= EAS BUILD =============": "", - "build:ios:dev": "eas build --profile development --platform ios", - "build:ios:sim": "eas build --profile dev-sim --platform ios", - "build:ios:prev": "eas build --profile preview --platform ios", - "build:ios:e2e": "eas build --profile ios-e2e --platform ios", - "build:android:dev": "eas build --profile development --platform android", - "build:android:sim": "eas build --profile dev-sim --platform android", - "build:android:prev": "eas build --profile preview --platform android", - "build:android:e2e": "eas build --profile android-e2e --platform android", - "build:android:prod": "eas build --profile production --platform android", - "build:ios:prod": "eas build --profile production --platform ios", - "===================== EAS SUBMIT =====================": "", - "eas:android:submit": "eas submit -p android --profile production", - "eas:ios:submit": "eas submit -p ios --profile production", - "=========== CODE STYLE ============": "", - "check-types": "tsc --noEmit", - "check": "prettier src --check", - "format": "prettier src --write", - "lint:check": "eslint src --debug", - "lint:fix": "eslint src --fix", - "============= HUSKY =============": "", - "prepare": "husky", - "commit": "cz", - "============= MISCELLANEOUS =============": "", - "delete:dstore": "find -name '.DS_Store' -type f -delete" - }, - "lint-staged": { - "*.ts": [ - "prettier --write", - "eslint --fix" - ], - "*.tsx": [ - "prettier --write", - "eslint --fix" - ] - }, "commitlint": { "extends": [ "@commitlint/config-conventional" @@ -54,12 +9,6 @@ "path": "cz-conventional-changelog" } }, - "jest": { - "preset": "jest-expo" - }, - "overrides": { - "globals": "14.0.0" - }, "dependencies": { "@expo-google-fonts/inter": "^0.3.0", "@expo/vector-icons": "^14.0.2", @@ -132,5 +81,56 @@ "react-test-renderer": "18.3.1", "typescript": "^5.3.3" }, - "private": true + "jest": { + "preset": "jest-expo" + }, + "lint-staged": { + "*.ts": [ + "prettier --write", + "eslint --fix" + ], + "*.tsx": [ + "prettier --write", + "eslint --fix" + ] + }, + "main": "expo-router/entry", + "name": "drc-news", + "overrides": { + "globals": "14.0.0" + }, + "private": true, + "scripts": { + "=========== CODE STYLE ============": "", + "============= EAS BUILD =============": "", + "============= HUSKY =============": "", + "============= MISCELLANEOUS =============": "", + "===================== EAS SUBMIT =====================": "", + "android": "expo run:android", + "build:android:dev": "eas build --profile development --platform android", + "build:android:e2e": "eas build --profile android-e2e --platform android", + "build:android:prev": "eas build --profile preview --platform android", + "build:android:prod": "eas build --profile production --platform android", + "build:android:sim": "eas build --profile dev-sim --platform android", + "build:ios:dev": "eas build --profile development --platform ios", + "build:ios:e2e": "eas build --profile ios-e2e --platform ios", + "build:ios:prev": "eas build --profile preview --platform ios", + "build:ios:prod": "eas build --profile production --platform ios", + "build:ios:sim": "eas build --profile dev-sim --platform ios", + "check": "prettier src --check", + "check-types": "tsc --noEmit", + "commit": "cz", + "delete:dstore": "find -name '.DS_Store' -type f -delete", + "eas:android:submit": "eas submit -p android --profile production", + "eas:ios:submit": "eas submit -p ios --profile production", + "format": "prettier src --write", + "ios": "expo run:ios", + "lint:check": "eslint src --debug", + "lint:fix": "eslint src --fix", + "prepare": "husky", + "start": "expo start", + "test": "jest --watchAll", + "web": "expo start --web" + }, + "version": "1.0.0" } diff --git a/apps/mobile-legacy/src/api/client.ts b/apps/mobile-legacy/src/api/client.ts index b058eaa..508173d 100644 --- a/apps/mobile-legacy/src/api/client.ts +++ b/apps/mobile-legacy/src/api/client.ts @@ -5,113 +5,113 @@ import { clearTokens, getAccessToken, getRefreshToken, setTokens } from "@/store const endpoint = process.env.EXPO_PUBLIC_API_URL!; const client: AxiosInstance = axios.create({ - baseURL: endpoint, - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + baseURL: endpoint, + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, }); let isAuthTokenRefreshing = false; let failedRequestsQueue: ((token: string) => void)[] = []; const processFailedRequestsQueue = (token: string) => { - failedRequestsQueue.forEach(callback => callback(token)); - failedRequestsQueue = []; + failedRequestsQueue.forEach((callback) => callback(token)); + failedRequestsQueue = []; }; // Wait for 120 seconds before timing out -axios.interceptors.request.use(config => { - config.timeout = 120_000; - return config; +axios.interceptors.request.use((config) => { + config.timeout = 120_000; + return config; }); // Add the Authorization header to all requests -client.interceptors.request.use(async config => { - const token = await getAccessToken(); - if (token) { - config.headers["Authorization"] = `Bearer ${token}`; - } +client.interceptors.request.use(async (config) => { + const token = await getAccessToken(); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } - return config; + return config; }); // Handle 401 errors and refresh the token client.interceptors.response.use( - response => response, - async error => { - const originalRequest = error.config; - const status = error.response?.status; + (response) => response, + async (error) => { + const originalRequest = error.config; + const status = error.response?.status; - if (status === 401 && !originalRequest._retry) { - originalRequest._retry = true; + if (status === 401 && !originalRequest._retry) { + originalRequest._retry = true; - if (isAuthTokenRefreshing) { - return new Promise(resolve => { - failedRequestsQueue.push((token: string) => { - originalRequest.headers["Authorization"] = `Bearer ${token}`; - resolve(client(originalRequest)); - }); - }); - } + if (isAuthTokenRefreshing) { + return new Promise((resolve) => { + failedRequestsQueue.push((token: string) => { + originalRequest.headers.Authorization = `Bearer ${token}`; + resolve(client(originalRequest)); + }); + }); + } - isAuthTokenRefreshing = true; + isAuthTokenRefreshing = true; - try { - const refreshToken = await getRefreshToken(); - if (!refreshToken) { - await clearTokens(); - return Promise.reject(error); - } - - const response = await axios.post(`${endpoint}/token/refresh`, { - refresh_token: refreshToken, - } as RefreshTokenPayload); - - const updatedToken = response.data.token; - await setTokens(updatedToken, refreshToken); - processFailedRequestsQueue(updatedToken); - - originalRequest.headers["Authorization"] = `Bearer ${updatedToken}`; - return client(originalRequest); - } catch (error) { - await clearTokens(); - return Promise.reject(error); - } finally { - isAuthTokenRefreshing = false; - } + try { + const refreshToken = await getRefreshToken(); + if (!refreshToken) { + await clearTokens(); + return Promise.reject(error); } + const response = await axios.post(`${endpoint}/token/refresh`, { + refresh_token: refreshToken, + } as RefreshTokenPayload); + + const updatedToken = response.data.token; + await setTokens(updatedToken, refreshToken); + processFailedRequestsQueue(updatedToken); + + originalRequest.headers.Authorization = `Bearer ${updatedToken}`; + return client(originalRequest); + } catch (error) { + await clearTokens(); return Promise.reject(error); + } finally { + isAuthTokenRefreshing = false; + } } + + return Promise.reject(error); + }, ); if (__DEV__) { - // Log HTTP requests and responses - client.interceptors.request.use( - async config => { - console.log("HTTP REQUEST", { - baseURL: config.baseURL, - url: config.url, - data: config.data, - }); + // Log HTTP requests and responses + client.interceptors.request.use( + async (config) => { + console.log("HTTP REQUEST", { + baseURL: config.baseURL, + data: config.data, + url: config.url, + }); - return config; - }, - error => console.log(JSON.stringify(error)) - ); + return config; + }, + (error) => console.log(JSON.stringify(error)), + ); - client.interceptors.response.use( - response => { - console.log("HTTP RESPONSE", { - stats: response.status, - data: response.data, - }); + client.interceptors.response.use( + (response) => { + console.log("HTTP RESPONSE", { + data: response.data, + stats: response.status, + }); - return response; - }, - error => console.log(JSON.stringify(error)) - ); + return response; + }, + (error) => console.log(JSON.stringify(error)), + ); } export default client; diff --git a/apps/mobile-legacy/src/api/endpoint.ts b/apps/mobile-legacy/src/api/endpoint.ts index c422e2b..b467d09 100644 --- a/apps/mobile-legacy/src/api/endpoint.ts +++ b/apps/mobile-legacy/src/api/endpoint.ts @@ -1,33 +1,33 @@ export const endpoint = { - feedManagement: { - addArticleToBookmark: (bookmarkId: string, articleId: string) => - `feed/bookmarks/${bookmarkId}/articles/${articleId}`, - addCommentToArticle: (articleId: string) => `/feed/articles/${articleId}/comments`, - createBookmark: `/feed/bookmarks`, - deleteBookmark: (bookmarkId: string) => `/feed/bookmarks/${bookmarkId}`, - followSource: (sourceId: string) => `/feed/sources/${sourceId}/follow`, - getArticleCommentList: (articleId: string) => `/feed/articles/${articleId}/comments`, - getArticleDetails: (articleId: string) => `/feed/articles/${articleId}`, - getArticleOverviewList: `/feed/articles`, - getBookmarkList: `/feed/bookmarks`, - getBookmarkedArticlesList: (bookmarkId: string) => `/feed/bookmarks/${bookmarkId}/articles`, - getSourceArticleOverviewList: (sourceId: string) => `/feed/sources/${sourceId}/articles`, - getSourceDetails: (sourceId: string) => `/feed/sources/${sourceId}`, - getSourceOverviewList: `/feed/sources`, - removeArticleFromBookmark: (bookmarkId: string, articleId: string) => - `/feed/bookmarks/${bookmarkId}/articles/${articleId}`, - unfollowSource: (sourceId: string) => `/feed/sources/${sourceId}/unfollow`, - updateBookmark: (bookmarkId: string) => `/feed/bookmarks/${bookmarkId}`, - }, - identityAndAccess: { - login: "/login_check", - logout: "/token/invalidate", - register: "/register", - getUserProfile: "/me", - requestPassword: "/password/request", - confirmAccount: (token: string) => `/account/confirm/${token}`, - resetPassword: (token: string) => `/password/reset/${token}`, - unlockAccount: (token: string) => `/account/unlock/${token}`, - updatePassword: "/password/update", - }, + feedManagement: { + addArticleToBookmark: (bookmarkId: string, articleId: string) => + `feed/bookmarks/${bookmarkId}/articles/${articleId}`, + addCommentToArticle: (articleId: string) => `/feed/articles/${articleId}/comments`, + createBookmark: `/feed/bookmarks`, + deleteBookmark: (bookmarkId: string) => `/feed/bookmarks/${bookmarkId}`, + followSource: (sourceId: string) => `/feed/sources/${sourceId}/follow`, + getArticleCommentList: (articleId: string) => `/feed/articles/${articleId}/comments`, + getArticleDetails: (articleId: string) => `/feed/articles/${articleId}`, + getArticleOverviewList: `/feed/articles`, + getBookmarkedArticlesList: (bookmarkId: string) => `/feed/bookmarks/${bookmarkId}/articles`, + getBookmarkList: `/feed/bookmarks`, + getSourceArticleOverviewList: (sourceId: string) => `/feed/sources/${sourceId}/articles`, + getSourceDetails: (sourceId: string) => `/feed/sources/${sourceId}`, + getSourceOverviewList: `/feed/sources`, + removeArticleFromBookmark: (bookmarkId: string, articleId: string) => + `/feed/bookmarks/${bookmarkId}/articles/${articleId}`, + unfollowSource: (sourceId: string) => `/feed/sources/${sourceId}/unfollow`, + updateBookmark: (bookmarkId: string) => `/feed/bookmarks/${bookmarkId}`, + }, + identityAndAccess: { + confirmAccount: (token: string) => `/account/confirm/${token}`, + getUserProfile: "/me", + login: "/login_check", + logout: "/token/invalidate", + register: "/register", + requestPassword: "/password/request", + resetPassword: (token: string) => `/password/reset/${token}`, + unlockAccount: (token: string) => `/account/unlock/${token}`, + updatePassword: "/password/update", + }, }; diff --git a/apps/mobile-legacy/src/api/request/feed-management/article.ts b/apps/mobile-legacy/src/api/request/feed-management/article.ts index 6775960..5b2c545 100644 --- a/apps/mobile-legacy/src/api/request/feed-management/article.ts +++ b/apps/mobile-legacy/src/api/request/feed-management/article.ts @@ -1,19 +1,30 @@ import { endpoint } from "@/api/endpoint"; import { Article, ArticleOverview, TrendingArticle } from "@/api/schema/feed-management/article"; -import { ArticleFilters, useGetQuery, usePaginatedInfiniteQuery, usePaginatedQuery } from "@/api/shared"; +import { + ArticleFilters, + useGetQuery, + usePaginatedInfiniteQuery, + usePaginatedQuery, +} from "@/api/shared"; export const useArticleTrendingList = (filters: ArticleFilters = {}) => { - return usePaginatedQuery("/feed/trending", filters); + return usePaginatedQuery("/feed/trending", filters); }; export const useArticleDetails = (articleId: string) => { - return useGetQuery

(endpoint.feedManagement.getArticleDetails(articleId)); + return useGetQuery
(endpoint.feedManagement.getArticleDetails(articleId)); }; export const useArticleOverviewList = (filters: ArticleFilters = {}) => { - return usePaginatedQuery(endpoint.feedManagement.getArticleOverviewList, filters); + return usePaginatedQuery( + endpoint.feedManagement.getArticleOverviewList, + filters, + ); }; export const useInfiniteArticleOverviewList = (filters: ArticleFilters = {}) => { - return usePaginatedInfiniteQuery(endpoint.feedManagement.getArticleOverviewList, filters); + return usePaginatedInfiniteQuery( + endpoint.feedManagement.getArticleOverviewList, + filters, + ); }; diff --git a/apps/mobile-legacy/src/api/request/feed-management/bookmark.ts b/apps/mobile-legacy/src/api/request/feed-management/bookmark.ts index 6d901c8..420498c 100644 --- a/apps/mobile-legacy/src/api/request/feed-management/bookmark.ts +++ b/apps/mobile-legacy/src/api/request/feed-management/bookmark.ts @@ -1,34 +1,44 @@ import { endpoint } from "@/api/endpoint"; -import { Bookmark, BookmarkedArticle, BookmarkPayload } from "@/api/schema/feed-management/bookmark"; -import { ArticleFilters, useDeleteQuery, usePaginatedInfiniteQuery, usePostQuery, usePutQuery } from "@/api/shared"; +import { + Bookmark, + BookmarkedArticle, + BookmarkPayload, +} from "@/api/schema/feed-management/bookmark"; +import { + ArticleFilters, + useDeleteQuery, + usePaginatedInfiniteQuery, + usePostQuery, + usePutQuery, +} from "@/api/shared"; export const useCreateBookmark = () => { - return usePostQuery(endpoint.feedManagement.createBookmark); + return usePostQuery(endpoint.feedManagement.createBookmark); }; export const useUpdateBookmark = (bookmarkId: string) => { - return usePutQuery(endpoint.feedManagement.updateBookmark(bookmarkId)); + return usePutQuery(endpoint.feedManagement.updateBookmark(bookmarkId)); }; export const useDeleteBookmark = (bookmarkId: string) => { - return useDeleteQuery(endpoint.feedManagement.deleteBookmark(bookmarkId)); + return useDeleteQuery(endpoint.feedManagement.deleteBookmark(bookmarkId)); }; export const useAddArticleToBookmark = (bookmarkId: string, articleId: string) => { - return usePostQuery(endpoint.feedManagement.addArticleToBookmark(bookmarkId, articleId)); + return usePostQuery(endpoint.feedManagement.addArticleToBookmark(bookmarkId, articleId)); }; export const useRemoveArticleFromBookmark = (bookmarkId: string, articleId: string) => { - return useDeleteQuery(endpoint.feedManagement.removeArticleFromBookmark(bookmarkId, articleId)); + return useDeleteQuery(endpoint.feedManagement.removeArticleFromBookmark(bookmarkId, articleId)); }; export const useBookmarkList = (filters: ArticleFilters = {}) => { - return usePaginatedInfiniteQuery(endpoint.feedManagement.getBookmarkList, filters); + return usePaginatedInfiniteQuery(endpoint.feedManagement.getBookmarkList, filters); }; export const useBookmarkedArticlesList = (bookmarkId: string, filters: ArticleFilters = {}) => { - return usePaginatedInfiniteQuery( - endpoint.feedManagement.getBookmarkedArticlesList(bookmarkId), - filters - ); + return usePaginatedInfiniteQuery( + endpoint.feedManagement.getBookmarkedArticlesList(bookmarkId), + filters, + ); }; diff --git a/apps/mobile-legacy/src/api/request/feed-management/comment.ts b/apps/mobile-legacy/src/api/request/feed-management/comment.ts index cadbe5e..724d7ff 100644 --- a/apps/mobile-legacy/src/api/request/feed-management/comment.ts +++ b/apps/mobile-legacy/src/api/request/feed-management/comment.ts @@ -3,13 +3,15 @@ import { Comment, CommentPayload } from "@/api/schema/feed-management/comment"; import { useDeleteQuery, usePaginatedInfiniteQuery, usePostQuery } from "@/api/shared"; export const useArticleCommentList = (articleId: string) => { - return usePaginatedInfiniteQuery(endpoint.feedManagement.getArticleCommentList(articleId)); + return usePaginatedInfiniteQuery( + endpoint.feedManagement.getArticleCommentList(articleId), + ); }; export const useAddCommentToArticle = (articleId: string) => { - return usePostQuery(endpoint.feedManagement.addCommentToArticle(articleId)); + return usePostQuery(endpoint.feedManagement.addCommentToArticle(articleId)); }; export const useRemoveCommentFromArticle = (articleId: string, commentId: string) => { - return useDeleteQuery(endpoint.feedManagement.removeArticleFromBookmark(articleId, commentId)); + return useDeleteQuery(endpoint.feedManagement.removeArticleFromBookmark(articleId, commentId)); }; diff --git a/apps/mobile-legacy/src/api/request/feed-management/source.ts b/apps/mobile-legacy/src/api/request/feed-management/source.ts index 8a442c8..2fa8af7 100644 --- a/apps/mobile-legacy/src/api/request/feed-management/source.ts +++ b/apps/mobile-legacy/src/api/request/feed-management/source.ts @@ -1,27 +1,36 @@ import { endpoint } from "@/api/endpoint"; import { ArticleOverview } from "@/api/schema/feed-management/article"; import { SourceDetails, SourceOverview } from "@/api/schema/feed-management/source"; -import { ArticleFilters, useDeleteQuery, useGetQuery, usePaginatedInfiniteQuery, usePostQuery } from "@/api/shared"; +import { + ArticleFilters, + useDeleteQuery, + useGetQuery, + usePaginatedInfiniteQuery, + usePostQuery, +} from "@/api/shared"; export const useSourceDetails = (sourceId: string) => { - return useGetQuery(endpoint.feedManagement.getSourceDetails(sourceId)); + return useGetQuery(endpoint.feedManagement.getSourceDetails(sourceId)); }; export const useSourceOverviewList = (filters: ArticleFilters = {}) => { - return usePaginatedInfiniteQuery(endpoint.feedManagement.getSourceOverviewList, filters); + return usePaginatedInfiniteQuery( + endpoint.feedManagement.getSourceOverviewList, + filters, + ); }; export const useSourceArticleOverviewList = (sourceId: string, filters: ArticleFilters = {}) => { - return usePaginatedInfiniteQuery( - endpoint.feedManagement.getSourceArticleOverviewList(sourceId), - filters - ); + return usePaginatedInfiniteQuery( + endpoint.feedManagement.getSourceArticleOverviewList(sourceId), + filters, + ); }; export const useFollowSource = (sourceId: string) => { - return usePostQuery(endpoint.feedManagement.followSource(sourceId)); + return usePostQuery(endpoint.feedManagement.followSource(sourceId)); }; export const useUnfollowSource = (sourceId: string) => { - return useDeleteQuery(endpoint.feedManagement.unfollowSource(sourceId)); + return useDeleteQuery(endpoint.feedManagement.unfollowSource(sourceId)); }; diff --git a/apps/mobile-legacy/src/api/request/identity-and-access/login.ts b/apps/mobile-legacy/src/api/request/identity-and-access/login.ts index 71c18f7..802cf3f 100644 --- a/apps/mobile-legacy/src/api/request/identity-and-access/login.ts +++ b/apps/mobile-legacy/src/api/request/identity-and-access/login.ts @@ -3,13 +3,13 @@ import { LoginPayload, LoginResponse } from "@/api/schema/identity-and-access/lo import { useGetQuery, usePostQuery } from "@/api/shared"; export const useLogin = () => { - return usePostQuery(endpoint.identityAndAccess.login); + return usePostQuery(endpoint.identityAndAccess.login); }; export const useLogout = () => { - return usePostQuery(endpoint.identityAndAccess.logout); + return usePostQuery(endpoint.identityAndAccess.logout); }; export const useUnlockAccount = (token: string) => { - return useGetQuery(endpoint.identityAndAccess.unlockAccount(token)); + return useGetQuery(endpoint.identityAndAccess.unlockAccount(token)); }; diff --git a/apps/mobile-legacy/src/api/request/identity-and-access/password.ts b/apps/mobile-legacy/src/api/request/identity-and-access/password.ts index be22a56..6f33b48 100644 --- a/apps/mobile-legacy/src/api/request/identity-and-access/password.ts +++ b/apps/mobile-legacy/src/api/request/identity-and-access/password.ts @@ -1,19 +1,19 @@ import { endpoint } from "@/api/endpoint"; import { - RequestPasswordPayload, - ResetPasswordPayload, - UpdatePasswordPayload, + RequestPasswordPayload, + ResetPasswordPayload, + UpdatePasswordPayload, } from "@/api/schema/identity-and-access/password"; import { usePostQuery, usePutQuery } from "@/api/shared"; export const usePasswordForgotten = () => { - return usePostQuery(endpoint.identityAndAccess.requestPassword); + return usePostQuery(endpoint.identityAndAccess.requestPassword); }; export const usePasswordReset = (token: string) => { - return usePostQuery(endpoint.identityAndAccess.resetPassword(token)); + return usePostQuery(endpoint.identityAndAccess.resetPassword(token)); }; export const usePasswordUpdate = () => { - return usePutQuery(endpoint.identityAndAccess.updatePassword); + return usePutQuery(endpoint.identityAndAccess.updatePassword); }; diff --git a/apps/mobile-legacy/src/api/request/identity-and-access/register.ts b/apps/mobile-legacy/src/api/request/identity-and-access/register.ts index a5258f3..528e371 100644 --- a/apps/mobile-legacy/src/api/request/identity-and-access/register.ts +++ b/apps/mobile-legacy/src/api/request/identity-and-access/register.ts @@ -3,9 +3,9 @@ import { RegisterPayload } from "@/api/schema/identity-and-access/register"; import { useGetQuery, usePostQuery } from "@/api/shared"; export const useRegister = () => { - return usePostQuery(endpoint.identityAndAccess.register); + return usePostQuery(endpoint.identityAndAccess.register); }; export const useConfirmAccount = (token: string) => { - return useGetQuery(endpoint.identityAndAccess.confirmAccount(token)); + return useGetQuery(endpoint.identityAndAccess.confirmAccount(token)); }; diff --git a/apps/mobile-legacy/src/api/schema/feed-management/article.ts b/apps/mobile-legacy/src/api/schema/feed-management/article.ts index 450b99e..e890afb 100644 --- a/apps/mobile-legacy/src/api/schema/feed-management/article.ts +++ b/apps/mobile-legacy/src/api/schema/feed-management/article.ts @@ -1,45 +1,45 @@ import { SourceReference } from "@/api/schema/feed-management/source"; export type ArticleOverview = { - id: string; - title: string; - link: string; - categories: string[]; - excerpt: string; - source: SourceReference; - publishedAt: string; - image?: string; - readingTime: number; - bookmarked: boolean; + id: string; + title: string; + link: string; + categories: string[]; + excerpt: string; + source: SourceReference; + publishedAt: string; + image?: string; + readingTime: number; + bookmarked: boolean; }; export type Article = { - id: string; - title: string; - link: string; - categories: string[]; - body: string; - source: SourceReference; - hash: string; - credibility: { - bias: "neutral" | "slightly" | "partisan" | "extreme"; - reliability: "trusted" | "reliable" | "average" | "unreliable" | "low_trust"; - transparency: "low" | "medium" | "high"; - }; - sentiment: "negative" | "positive" | "neutral"; - metadata?: { - title?: string; - description?: string; - image?: string; - video?: string; - audio?: string; - locale?: string; - }; - readingTime: number; - publishedAt: string; - crawledAt: string; - updatedAt: string; - bookmarked: boolean; + id: string; + title: string; + link: string; + categories: string[]; + body: string; + source: SourceReference; + hash: string; + credibility: { + bias: "neutral" | "slightly" | "partisan" | "extreme"; + reliability: "trusted" | "reliable" | "average" | "unreliable" | "low_trust"; + transparency: "low" | "medium" | "high"; + }; + sentiment: "negative" | "positive" | "neutral"; + metadata?: { + title?: string; + description?: string; + image?: string; + video?: string; + audio?: string; + locale?: string; + }; + readingTime: number; + publishedAt: string; + crawledAt: string; + updatedAt: string; + bookmarked: boolean; }; export type TrendingArticle = ArticleOverview; diff --git a/apps/mobile-legacy/src/api/schema/feed-management/bookmark.ts b/apps/mobile-legacy/src/api/schema/feed-management/bookmark.ts index 85113bc..9689796 100644 --- a/apps/mobile-legacy/src/api/schema/feed-management/bookmark.ts +++ b/apps/mobile-legacy/src/api/schema/feed-management/bookmark.ts @@ -3,28 +3,28 @@ import Joi from "joi"; import { ArticleOverview } from "@/api/schema/feed-management/article"; export type BookmarkPayload = { - name: string; - description?: string; - isPublic: boolean; + name: string; + description?: string; + isPublic: boolean; }; export type Bookmark = { - id: string; - name: string; - createdAt: string; - description?: string; - articlesCount: number; - isPublic: boolean; - updatedAt?: string; + id: string; + name: string; + createdAt: string; + description?: string; + articlesCount: number; + isPublic: boolean; + updatedAt?: string; }; export type BookmarkedArticle = ArticleOverview; export const BookmarkPayloadSchema = Joi.object({ - name: Joi.string().required().messages({ - "string.empty": "Le nom est requis", - "any.required": "Le nom est requis", - }), - description: Joi.string().optional(), - isPublic: Joi.boolean().optional(), + description: Joi.string().optional(), + isPublic: Joi.boolean().optional(), + name: Joi.string().required().messages({ + "any.required": "Le nom est requis", + "string.empty": "Le nom est requis", + }), }); diff --git a/apps/mobile-legacy/src/api/schema/feed-management/comment.ts b/apps/mobile-legacy/src/api/schema/feed-management/comment.ts index ca6b4ad..e30ee8a 100644 --- a/apps/mobile-legacy/src/api/schema/feed-management/comment.ts +++ b/apps/mobile-legacy/src/api/schema/feed-management/comment.ts @@ -1,14 +1,14 @@ export type Comment = { + id: string; + content: string; + user: { id: string; - content: string; - user: { - id: string; - name: string; - }; - sentiment: "positive" | "neutral" | "negative"; - createdAt: string; + name: string; + }; + sentiment: "positive" | "neutral" | "negative"; + createdAt: string; }; export type CommentPayload = { - content: string; + content: string; }; diff --git a/apps/mobile-legacy/src/api/schema/feed-management/source.ts b/apps/mobile-legacy/src/api/schema/feed-management/source.ts index 66f737f..1c02d57 100644 --- a/apps/mobile-legacy/src/api/schema/feed-management/source.ts +++ b/apps/mobile-legacy/src/api/schema/feed-management/source.ts @@ -1,54 +1,54 @@ export type SourceReference = { - id: string; - name: string; - displayName?: string; - image: string; - url: string; + id: string; + name: string; + displayName?: string; + image: string; + url: string; }; export type SourceOverview = { - id: string; - name: string; - displayName?: string; - image: string; - url: string; - followed: boolean; + id: string; + name: string; + displayName?: string; + image: string; + url: string; + followed: boolean; }; export type CategoryShare = { - name: string; - count: number; - percentage: number; + name: string; + count: number; + percentage: number; }; export type PublicationEntry = { - date: string; - count: number; + date: string; + count: number; }; export type SourceDetails = { - id: string; - name: string; - url: string; - credibility: { - bias: "neutral" | "slightly" | "partisan" | "extreme"; - reliability: "trusted" | "reliable" | "average" | "unreliable" | "low_trust"; - transparency: "low" | "medium" | "high"; - }; - publicationGraph: { - items: PublicationEntry[]; - total: number; - }; - categoryShares: { - items: CategoryShare[]; - total: number; - }; - articlesCount: number; - crawledAt: string; - displayName?: string; - description?: string; - updatedAt?: string; - metadataAvailable: number; - followed: boolean; - image: string; + id: string; + name: string; + url: string; + credibility: { + bias: "neutral" | "slightly" | "partisan" | "extreme"; + reliability: "trusted" | "reliable" | "average" | "unreliable" | "low_trust"; + transparency: "low" | "medium" | "high"; + }; + publicationGraph: { + items: PublicationEntry[]; + total: number; + }; + categoryShares: { + items: CategoryShare[]; + total: number; + }; + articlesCount: number; + crawledAt: string; + displayName?: string; + description?: string; + updatedAt?: string; + metadataAvailable: number; + followed: boolean; + image: string; }; diff --git a/apps/mobile-legacy/src/api/schema/identity-and-access/login.ts b/apps/mobile-legacy/src/api/schema/identity-and-access/login.ts index 94a37e1..8348558 100644 --- a/apps/mobile-legacy/src/api/schema/identity-and-access/login.ts +++ b/apps/mobile-legacy/src/api/schema/identity-and-access/login.ts @@ -1,31 +1,31 @@ import Joi from "joi"; export type LoginPayload = { - username: string; - password: string; + username: string; + password: string; }; export type LoginResponse = { - token: string; - refresh_token: string; + token: string; + refresh_token: string; }; export type RefreshTokenPayload = { - refresh_token: string; + refresh_token: string; }; export type RefreshTokenResponse = { - token: string; + token: string; }; export const LoginPayloadSchema = Joi.object({ - username: Joi.string().required().messages({ - "string.empty": "L'email est requis", - "any.required": "L'email est requis", - }), - password: Joi.string().min(4).required().messages({ - "string.empty": "Le mot de passe est requis", - "string.min": "Le mot de passe doit comporter au moins 4 caractères", - "any.required": "Le mot de passe est requis", - }), + password: Joi.string().min(4).required().messages({ + "any.required": "Le mot de passe est requis", + "string.empty": "Le mot de passe est requis", + "string.min": "Le mot de passe doit comporter au moins 4 caractères", + }), + username: Joi.string().required().messages({ + "any.required": "L'email est requis", + "string.empty": "L'email est requis", + }), }); diff --git a/apps/mobile-legacy/src/api/schema/identity-and-access/password.ts b/apps/mobile-legacy/src/api/schema/identity-and-access/password.ts index 85faf03..ead279e 100644 --- a/apps/mobile-legacy/src/api/schema/identity-and-access/password.ts +++ b/apps/mobile-legacy/src/api/schema/identity-and-access/password.ts @@ -1,53 +1,53 @@ import Joi from "joi"; export type RequestPasswordPayload = { - email: string; + email: string; }; export type ResetPasswordPayload = { - password: string; - confirm: string; + password: string; + confirm: string; }; export type UpdatePasswordPayload = { - current: string; - password: string; - confirm: string; + current: string; + password: string; + confirm: string; }; export const RequestPasswordPayloadSchema = Joi.object({ - email: Joi.string().required().messages({ - "string.empty": "L'email est requis", - "any.required": "L'email est requis", - }), + email: Joi.string().required().messages({ + "any.required": "L'email est requis", + "string.empty": "L'email est requis", + }), }); export const ResetPasswordPayloadSchema = Joi.object({ - password: Joi.string().min(6).required().messages({ - "string.empty": "Le mot de passe est requis", - "string.min": "Le mot de passe doit comporter au moins 6 caractères", - "any.required": "Le mot de passe est requis", - }), - confirm: Joi.string().valid(Joi.ref("password")).required().messages({ - "any.only": "Les mots de passe ne correspondent pas", - "string.empty": "La confirmation du mot de passe est requise", - "any.required": "La confirmation du mot de passe est requise", - }), + confirm: Joi.string().valid(Joi.ref("password")).required().messages({ + "any.only": "Les mots de passe ne correspondent pas", + "any.required": "La confirmation du mot de passe est requise", + "string.empty": "La confirmation du mot de passe est requise", + }), + password: Joi.string().min(6).required().messages({ + "any.required": "Le mot de passe est requis", + "string.empty": "Le mot de passe est requis", + "string.min": "Le mot de passe doit comporter au moins 6 caractères", + }), }); export const UpdatePasswordPayloadSchema = Joi.object({ - current: Joi.string().required().messages({ - "string.empty": "Le mot de passe actuel est requis", - "any.required": "Le mot de passe actuel est requis", - }), - password: Joi.string().min(6).required().messages({ - "string.empty": "Le nouveau mot de passe est requis", - "string.min": "Le nouveau mot de passe doit comporter au moins 6 caractères", - "any.required": "Le nouveau mot de passe est requis", - }), - confirm: Joi.string().valid(Joi.ref("password")).required().messages({ - "any.only": "Les mots de passe ne correspondent pas", - "string.empty": "La confirmation du nouveau mot de passe est requise", - "any.required": "La confirmation du nouveau mot de passe est requise", - }), + confirm: Joi.string().valid(Joi.ref("password")).required().messages({ + "any.only": "Les mots de passe ne correspondent pas", + "any.required": "La confirmation du nouveau mot de passe est requise", + "string.empty": "La confirmation du nouveau mot de passe est requise", + }), + current: Joi.string().required().messages({ + "any.required": "Le mot de passe actuel est requis", + "string.empty": "Le mot de passe actuel est requis", + }), + password: Joi.string().min(6).required().messages({ + "any.required": "Le nouveau mot de passe est requis", + "string.empty": "Le nouveau mot de passe est requis", + "string.min": "Le nouveau mot de passe doit comporter au moins 6 caractères", + }), }); diff --git a/apps/mobile-legacy/src/api/schema/identity-and-access/register.ts b/apps/mobile-legacy/src/api/schema/identity-and-access/register.ts index a97fc90..d3268cf 100644 --- a/apps/mobile-legacy/src/api/schema/identity-and-access/register.ts +++ b/apps/mobile-legacy/src/api/schema/identity-and-access/register.ts @@ -1,23 +1,23 @@ import Joi from "joi"; export type RegisterPayload = { - name: string; - email: string; - password: string; + name: string; + email: string; + password: string; }; export const RegisterPayloadSchema = Joi.object({ - name: Joi.string().required().messages({ - "string.empty": "Le nom est requis", - "any.required": "Le nom est requis", - }), - email: Joi.string().required().messages({ - "string.empty": "L'email est requis", - "any.required": "L'email est requis", - }), - password: Joi.string().min(6).required().messages({ - "string.empty": "Le mot de passe est requis", - "string.min": "Le mot de passe doit comporter au moins 4 caractères", - "any.required": "Le mot de passe est requis", - }), + email: Joi.string().required().messages({ + "any.required": "L'email est requis", + "string.empty": "L'email est requis", + }), + name: Joi.string().required().messages({ + "any.required": "Le nom est requis", + "string.empty": "Le nom est requis", + }), + password: Joi.string().min(6).required().messages({ + "any.required": "Le mot de passe est requis", + "string.empty": "Le mot de passe est requis", + "string.min": "Le mot de passe doit comporter au moins 4 caractères", + }), }); diff --git a/apps/mobile-legacy/src/api/shared.ts b/apps/mobile-legacy/src/api/shared.ts index ece1da2..d7cdad5 100644 --- a/apps/mobile-legacy/src/api/shared.ts +++ b/apps/mobile-legacy/src/api/shared.ts @@ -1,150 +1,167 @@ -import { skipToken, useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + skipToken, + useInfiniteQuery, + useMutation, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import { AxiosError } from "axios"; import qs from "qs"; import client from "@/api/client"; export type PaginationFilters = { - page?: number; - limit?: number; - lastId?: string; + page?: number; + limit?: number; + lastId?: string; }; export type ArticleFilters = PaginationFilters & { - dateRange?: { - start: number; - end: number; - }; - search?: string; - sortDirection?: "asc" | "desc"; + dateRange?: { + start: number; + end: number; + }; + search?: string; + sortDirection?: "asc" | "desc"; }; export type PaginationInfo = { - current: number; - limit: number; - lastId?: string; - offset: number; + current: number; + limit: number; + lastId?: string; + offset: number; }; export type ClientDetailErrorResponse = { - type: string; - title: string; - detail: string; - status: number; + type: string; + title: string; + detail: string; + status: number; }; export type ClientErrorResponse = { - code: string; - message: string; + code: string; + message: string; }; export type ErrorResponse = AxiosError; export type PaginatedResponse = { - items: TItem[]; - pagination: PaginationInfo; + items: TItem[]; + pagination: PaginationInfo; }; -export const safeMessage = (error: AxiosError | Error): string => { - if (error instanceof AxiosError && error.response) { - const response = error.response.data; +export const safeMessage = ( + error: AxiosError | Error, +): string => { + if (error instanceof AxiosError && error.response) { + const response = error.response.data; - if ("message" in response) { - return response.message; - } else if ("detail" in response) { - return response.detail; - } + if ("message" in response) { + return response.message; + } else if ("detail" in response) { + return response.detail; } + } - return "Une erreur est survenue"; + return "Une erreur est survenue"; }; -export const usePaginatedInfiniteQuery = (endpoint: string, filters: PaginationFilters = {}) => { - return useInfiniteQuery, ErrorResponse>({ - initialData: undefined, - initialPageParam: null, - queryKey: [endpoint, filters], - queryFn: async ({ pageParam = null }) => { - const query = qs.stringify({ ...filters, lastId: pageParam }, { skipNulls: true }); - const url = `${endpoint}?${query}`; - const response = await client.get>(url); - return response.data; - }, - getNextPageParam: (lastPage: PaginatedResponse) => { - const { lastId } = lastPage.pagination; - return lastId ? lastId : null; - }, - staleTime: 1_000 * 60 * 10, - }); +export const usePaginatedInfiniteQuery = ( + endpoint: string, + filters: PaginationFilters = {}, +) => { + return useInfiniteQuery, ErrorResponse>({ + getNextPageParam: (lastPage: PaginatedResponse) => { + const { lastId } = lastPage.pagination; + return lastId ? lastId : null; + }, + initialData: undefined, + initialPageParam: null, + queryFn: async ({ pageParam = null }) => { + const query = qs.stringify({ ...filters, lastId: pageParam }, { skipNulls: true }); + const url = `${endpoint}?${query}`; + const response = await client.get>(url); + return response.data; + }, + queryKey: [endpoint, filters], + staleTime: 1_000 * 60 * 10, + }); }; export const usePaginatedQuery = (endpoint: string, filters: PaginationFilters = {}) => { - return useQuery, ErrorResponse>({ - queryKey: [endpoint, filters], - queryFn: async (): Promise> => { - const query = qs.stringify({ ...filters, lastId: null }, { skipNulls: true }); - const url = `${endpoint}?${query}`; - const response = await client.get>(url); - return response.data; - }, - staleTime: 1_000 * 60 * 10, - }); + return useQuery, ErrorResponse>({ + queryFn: async (): Promise> => { + const query = qs.stringify({ ...filters, lastId: null }, { skipNulls: true }); + const url = `${endpoint}?${query}`; + const response = await client.get>(url); + return response.data; + }, + queryKey: [endpoint, filters], + staleTime: 1_000 * 60 * 10, + }); }; export const useGetQuery = (endpoint: string, enabled: boolean = true) => { - return useQuery({ - queryKey: [endpoint], - queryFn: enabled - ? async (): Promise => { - const response = await client.get(endpoint); - return response.data; - } - : skipToken, - staleTime: 1_000 * 60 * 10, - }); + return useQuery({ + queryFn: enabled + ? async (): Promise => { + const response = await client.get(endpoint); + return response.data; + } + : skipToken, + queryKey: [endpoint], + staleTime: 1_000 * 60 * 10, + }); }; -export const usePostQuery = (endpoint: string, keys: string[] = []) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (data: TPayload): Promise => { - const response = await client.post(endpoint, data); - return response.data; - }, - onSuccess: async () => { - for (const key of keys) { - await queryClient.invalidateQueries({ queryKey: [key] }); - } - }, - }); +export const usePostQuery = ( + endpoint: string, + keys: string[] = [], +) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (data: TPayload): Promise => { + const response = await client.post(endpoint, data); + return response.data; + }, + onSuccess: async () => { + for (const key of keys) { + await queryClient.invalidateQueries({ queryKey: [key] }); + } + }, + }); }; -export const usePutQuery = (endpoint: string, keys: string[] = []) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (data: TPayload): Promise => { - const response = await client.put(endpoint, data); - return response.data; - }, - onSuccess: async () => { - for (const key of keys) { - await queryClient.invalidateQueries({ queryKey: [key] }); - } - }, - }); +export const usePutQuery = ( + endpoint: string, + keys: string[] = [], +) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (data: TPayload): Promise => { + const response = await client.put(endpoint, data); + return response.data; + }, + onSuccess: async () => { + for (const key of keys) { + await queryClient.invalidateQueries({ queryKey: [key] }); + } + }, + }); }; export const useDeleteQuery = (endpoint: string, keys: string[] = []) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (): Promise => { - const response = await client.delete(endpoint); - return response.data; - }, - onSuccess: async () => { - for (const key of keys) { - await queryClient.invalidateQueries({ queryKey: [key] }); - } - }, - }); + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (): Promise => { + const response = await client.delete(endpoint); + return response.data; + }, + onSuccess: async () => { + for (const key of keys) { + await queryClient.invalidateQueries({ queryKey: [key] }); + } + }, + }); }; diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/_layout.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/_layout.tsx index 0a7047a..1878fc2 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/_layout.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/_layout.tsx @@ -1,82 +1,80 @@ -import React from "react"; - import { BookMarked, Globe, Home, User } from "@tamagui/lucide-icons"; import { Tabs } from "expo-router"; import { useColorScheme } from "react-native"; import { Paragraph } from "tamagui"; export default function TabLayout() { - const colorScheme = useColorScheme(); + const colorScheme = useColorScheme(); - return ( - - ( - - Actualités - - ), - tabBarIcon: ({ color, size }) => , - }} - /> - ( - - Sources - - ), - tabBarIcon: ({ color, size }) => , - }} - /> - ( - - Signets - - ), - tabBarIcon: ({ color, size }) => , - }} - /> - ( - - Profil - - ), - tabBarIcon: ({ color, size }) => , - }} - /> - - ); + return ( + + , + tabBarLabel: ({ color }) => ( + + Actualités + + ), + }} + /> + , + tabBarLabel: ({ color }) => ( + + Sources + + ), + }} + /> + , + tabBarLabel: ({ color }) => ( + + Signets + + ), + }} + /> + , + tabBarLabel: ({ color }) => ( + + Profil + + ), + }} + /> + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/account/_layout.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/account/_layout.tsx index 73ffdbf..ef76f39 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/account/_layout.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/account/_layout.tsx @@ -1,5 +1,5 @@ import { Stack } from "expo-router"; export default function Layout() { - return ; + return ; } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/account/index.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/account/index.tsx index 2d4317a..82816fc 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/account/index.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/account/index.tsx @@ -5,26 +5,26 @@ import { Label, ListItem, ScrollView, Separator, YGroup } from "tamagui"; import { ScreenView } from "@/ui/components/layout"; export default function Index() { - const router = useRouter(); + const router = useRouter(); - return ( - - + return ( + + - - - - - router.push("/account/settings")} - icon={Settings} - iconAfter={ChevronRight} - title="Settings" - /> - - - - - - ); + + + + + router.push("/account/settings")} + title="Settings" + /> + + + + + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/account/settings/index.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/account/settings/index.tsx index 9e17bdb..e96e781 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/account/settings/index.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/account/settings/index.tsx @@ -6,30 +6,30 @@ import { useAuth } from "@/providers/auth-provider"; import { ScreenView } from "@/ui/components/layout"; export default function Index() { - const authState = useAuth(); - const { mutate, isPending } = useLogout(); + const authState = useAuth(); + const { mutate, isPending } = useLogout(); - const handleLogout = async () => { - mutate(undefined, { - onSuccess: () => authState.logout(), - onError: () => authState.logout(), - }); - }; + const handleLogout = async () => { + mutate(undefined, { + onError: () => authState.logout(), + onSuccess: () => authState.logout(), + }); + }; - return ( - - + return ( + + - - - - - ); + + + + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/[id].tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/[id].tsx index 0c133bd..0172919 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/[id].tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/[id].tsx @@ -12,79 +12,84 @@ import { ArticleCategoryPill, ArticleCoverImage } from "@/ui/components/content/ import { SourceReferencePill } from "@/ui/components/content/source"; import { BackButton } from "@/ui/components/controls/BackButton"; import { IconButton } from "@/ui/components/controls/IconButton"; -import { ScreenView } from "@/ui/components/layout"; import { LoadingView } from "@/ui/components/LoadingView"; +import { ScreenView } from "@/ui/components/layout"; import { Caption, Text } from "@/ui/components/typography"; export default function ArticleDetails() { - const router = useRouter(); - const { id } = useLocalSearchParams(); - const { data, isLoading, error } = useArticleDetails(id as string); - const article: Article | undefined = data ?? undefined; - const relativeTime = useRelativeTime(article?.publishedAt); + const router = useRouter(); + const { id } = useLocalSearchParams(); + const { data, isLoading, error } = useArticleDetails(id as string); + const article: Article | undefined = data ?? undefined; + const relativeTime = useRelativeTime(article?.publishedAt); - const handleReadIntegrality = async () => { - await WebBrowser.openBrowserAsync(article!.link); - }; + const handleReadIntegrality = async () => { + await WebBrowser.openBrowserAsync(article!.link); + }; - if (error) { - Toast.show({ - type: "error", - text1: "Erreur", - text2: safeMessage(error), - }); - router.replace("/(authed)/(tabs)/articles"); - } + if (error) { + Toast.show({ + text1: "Erreur", + text2: safeMessage(error), + type: "error", + }); + router.replace("/(authed)/(tabs)/articles"); + } - if (isLoading || article === undefined) { - return ; - } + if (isLoading || article === undefined) { + return ; + } - return ( - - router.dismissTo("/(authed)/(tabs)/articles")} />} - trailingActions={ - <> - {}} icon={} /> - {}} icon={} /> - {}} icon={} /> - - } + return ( + + router.dismissTo("/(authed)/(tabs)/articles")} />} + trailingActions={ + <> + } onPress={() => {}} /> + } onPress={() => {}} /> + } onPress={() => {}} /> + + } + /> + + + {article.metadata?.image && ( + - - - {article.metadata?.image && ( - - )} - - - - {article.categories.map((category, index) => ( - - ))} - -
- {article.title} -
+ )} +
+ + + {article.categories.map((category, index) => ( + + ))} + +
+ {article.title} +
- - - - {relativeTime} - - {article.readingTime} minutes de lecture - - + + + + {relativeTime} + + {article.readingTime} minutes de lecture + + - - {article.body.trim()} - -
- -
-
- ); + + {article.body.trim()} + + + + +
+ ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/_layout.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/_layout.tsx index 73ffdbf..ef76f39 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/_layout.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/_layout.tsx @@ -1,5 +1,5 @@ import { Stack } from "expo-router"; export default function Layout() { - return ; + return ; } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/index.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/index.tsx index 57e0c49..fb5c8ff 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/index.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/index.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { ScrollView, YStack } from "tamagui"; import { useArticleOverviewList } from "@/api/request/feed-management/article"; @@ -13,39 +11,42 @@ import { ScreenView } from "@/ui/components/layout"; import { Heading } from "@/ui/components/typography"; export default function Index() { - const { data: articles, isLoading: articlesLoading } = useArticleOverviewList({ limit: 10 }); - const { data: sources, isLoading: sourcesLoading } = useSourceOverviewList(); - const articleOverviews: ArticleOverview[] = useFlattenedItems(articles); - const sourcesOverviews: SourceOverview[] = useFlattenedItems(sources); + const { data: articles, isLoading: articlesLoading } = useArticleOverviewList({ limit: 10 }); + const { data: sources, isLoading: sourcesLoading } = useSourceOverviewList(); + const articleOverviews: ArticleOverview[] = useFlattenedItems(articles); + const sourcesOverviews: SourceOverview[] = useFlattenedItems(sources); - return ( - - Actualités - - - - + return ( + + Actualités + + + + - {articlesLoading && } - {!articlesLoading && ( - - )} - - - + {articlesLoading && } + {!articlesLoading && ( + + )} + + + - {sourcesLoading && } - {!sourcesLoading && ( - - )} - - - - - ); + {sourcesLoading && } + {!sourcesLoading && ( + + )} + + + + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/trending.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/trending.tsx index dd727f1..fd84583 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/trending.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/articles/trending.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { useRouter } from "expo-router"; import { useInfiniteArticleOverviewList } from "@/api/request/feed-management/article"; @@ -10,31 +8,30 @@ import { BackButton } from "@/ui/components/controls/BackButton"; import { ScreenView } from "@/ui/components/layout"; export default function Trending() { - const router = useRouter(); - const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, refetch } = useInfiniteArticleOverviewList( - { limit: 20 } - ); - const articles: TrendingArticle[] = useFlattenedItems(data); + const router = useRouter(); + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, refetch } = + useInfiniteArticleOverviewList({ limit: 20 }); + const articles: TrendingArticle[] = useFlattenedItems(data); - return ( - - router.dismissTo("/(authed)/(tabs)/articles")} />} - title="Actualités" - /> + return ( + + router.dismissTo("/(authed)/(tabs)/articles")} />} + title="Actualités" + /> - {isLoading && } - {!isLoading && ( - - )} - - ); + {isLoading && } + {!isLoading && ( + + )} + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/[id].tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/[id].tsx index 42de48c..25cef7a 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/[id].tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/[id].tsx @@ -5,14 +5,14 @@ import { ScreenView } from "@/ui/components/layout"; import { Heading } from "@/ui/components/typography"; export default function Details() { - const { id } = useLocalSearchParams(); - // const { data, isLoading } = useBookmarkedArticlesList(id as string); - // const articles: BookmarkedArticle[] = useFlattenedItems(data); + const { id } = useLocalSearchParams(); + // const { data, isLoading } = useBookmarkedArticlesList(id as string); + // const articles: BookmarkedArticle[] = useFlattenedItems(data); - return ( - - Bookmark Infos - {id} - - ); + return ( + + Bookmark Infos + {id} + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/_layout.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/_layout.tsx index 73ffdbf..ef76f39 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/_layout.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/_layout.tsx @@ -1,5 +1,5 @@ import { Stack } from "expo-router"; export default function Layout() { - return ; + return ; } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/index.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/index.tsx index 7a53c97..981318b 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/index.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/bookmarks/index.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { Plus, Search } from "@tamagui/lucide-icons"; import { YStack } from "tamagui"; @@ -8,35 +6,36 @@ import { Bookmark } from "@/api/schema/feed-management/bookmark"; import { useFlattenedItems } from "@/hooks/use-flattened-items"; import { BookmarkList } from "@/ui/components/content/bookmark"; import { IconButton } from "@/ui/components/controls/IconButton"; -import { ScreenView } from "@/ui/components/layout"; import { LoadingView } from "@/ui/components/LoadingView"; +import { ScreenView } from "@/ui/components/layout"; export default function Index() { - const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, refetch } = useBookmarkList(); - const bookmarks: Bookmark[] = useFlattenedItems(data); + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, refetch } = + useBookmarkList(); + const bookmarks: Bookmark[] = useFlattenedItems(data); - return ( - - {}} icon={} />} - trailingActions={ {}} icon={} />} - /> + return ( + + } onPress={() => {}} />} + title="Bookmarks" + trailingActions={} onPress={() => {}} />} + /> - - {isLoading && } - {!isLoading && ( - - )} - - - ); + + {isLoading && } + {!isLoading && ( + + )} + + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/[name].tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/[name].tsx index ab09f1e..da3c2d1 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/[name].tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/[name].tsx @@ -4,12 +4,12 @@ import { ScreenView } from "@/ui/components/layout"; import { Heading, Text } from "@/ui/components/typography"; export default function SourceDetails() { - const { name } = useLocalSearchParams(); + const { name } = useLocalSearchParams(); - return ( - - Source Details - {name} - - ); + return ( + + Source Details + {name} + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/_layout.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/_layout.tsx index 73ffdbf..ef76f39 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/_layout.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/_layout.tsx @@ -1,5 +1,5 @@ import { Stack } from "expo-router"; export default function Layout() { - return ; + return ; } diff --git a/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/index.tsx b/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/index.tsx index 272a345..1f5ebe3 100644 --- a/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/index.tsx +++ b/apps/mobile-legacy/src/app/(authed)/(tabs)/sources/index.tsx @@ -5,15 +5,15 @@ import { SourceList, SourceSkeletonList } from "@/ui/components/content/source"; import { ScreenView } from "@/ui/components/layout"; export default function Sources() { - const { data, isLoading } = useSourceOverviewList(); - const sources: SourceOverview[] = useFlattenedItems(data); + const { data, isLoading } = useSourceOverviewList(); + const sources: SourceOverview[] = useFlattenedItems(data); - return ( - - + return ( + + - {isLoading && } - {!isLoading && } - - ); + {isLoading && } + {!isLoading && } + + ); } diff --git a/apps/mobile-legacy/src/app/(authed)/_layout.tsx b/apps/mobile-legacy/src/app/(authed)/_layout.tsx index 5269578..21df812 100644 --- a/apps/mobile-legacy/src/app/(authed)/_layout.tsx +++ b/apps/mobile-legacy/src/app/(authed)/_layout.tsx @@ -3,15 +3,15 @@ import { Redirect, Stack } from "expo-router"; import { useAuth } from "@/providers/auth-provider"; export default function AuthedLayout() { - const auth = useAuth(); + const auth = useAuth(); - if (!auth.isReady) { - return null; - } + if (!auth.isReady) { + return null; + } - if (!auth.isLoggedIn) { - return ; - } + if (!auth.isLoggedIn) { + return ; + } - return ; + return ; } diff --git a/apps/mobile-legacy/src/app/(unauthed)/_layout.tsx b/apps/mobile-legacy/src/app/(unauthed)/_layout.tsx index a524f81..b12aa2d 100644 --- a/apps/mobile-legacy/src/app/(unauthed)/_layout.tsx +++ b/apps/mobile-legacy/src/app/(unauthed)/_layout.tsx @@ -3,15 +3,15 @@ import { Redirect, Stack } from "expo-router"; import { useAuth } from "@/providers/auth-provider"; export default function AuthedLayout() { - const auth = useAuth(); + const auth = useAuth(); - if (!auth.isReady) { - return null; - } + if (!auth.isReady) { + return null; + } - if (auth.isLoggedIn) { - return ; - } + if (auth.isLoggedIn) { + return ; + } - return ; + return ; } diff --git a/apps/mobile-legacy/src/app/(unauthed)/password-request.tsx b/apps/mobile-legacy/src/app/(unauthed)/password-request.tsx index a249d18..fb7cf32 100644 --- a/apps/mobile-legacy/src/app/(unauthed)/password-request.tsx +++ b/apps/mobile-legacy/src/app/(unauthed)/password-request.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { joiResolver } from "@hookform/resolvers/joi"; import { Link, useRouter } from "expo-router"; import { useForm } from "react-hook-form"; @@ -7,7 +5,10 @@ import Toast from "react-native-toast-message"; import { YStack } from "tamagui"; import { usePasswordForgotten } from "@/api/request/identity-and-access/password"; -import { RequestPasswordPayload, RequestPasswordPayloadSchema } from "@/api/schema/identity-and-access/password"; +import { + RequestPasswordPayload, + RequestPasswordPayloadSchema, +} from "@/api/schema/identity-and-access/password"; import { ErrorResponse, safeMessage } from "@/api/shared"; import { FormEmailInput } from "@/ui/components/controls/forms"; import { SubmitButton } from "@/ui/components/controls/SubmitButton"; @@ -15,55 +16,56 @@ import { ScreenView } from "@/ui/components/layout"; import { Heading, Text } from "@/ui/components/typography"; export default function PasswordRequest() { - const { mutate, isPending } = usePasswordForgotten(); - const router = useRouter(); + const { mutate, isPending } = usePasswordForgotten(); + const router = useRouter(); - const { control, handleSubmit, formState } = useForm({ - resolver: joiResolver(RequestPasswordPayloadSchema), - }); + const { control, handleSubmit, formState } = useForm({ + resolver: joiResolver(RequestPasswordPayloadSchema), + }); - const onSubmit = (data: RequestPasswordPayload) => { - mutate(data, { - onSuccess: () => { - Toast.show({ - text1: "Succès", - text2: "Un mail avec les instructions vous a été envoyé", - type: "success", - }); - router.push("/(unauthed)/signin"); - }, - onError: (error: ErrorResponse) => { - Toast.show({ - text1: "Erreur de connexion", - text2: safeMessage(error), - type: "error", - }); - }, + const onSubmit = (data: RequestPasswordPayload) => { + mutate(data, { + onError: (error: ErrorResponse) => { + Toast.show({ + text1: "Erreur de connexion", + text2: safeMessage(error), + type: "error", }); - }; + }, + onSuccess: () => { + Toast.show({ + text1: "Succès", + text2: "Un mail avec les instructions vous a été envoyé", + type: "success", + }); + router.push("/(unauthed)/signin"); + }, + }); + }; - return ( - - - - Mot de passe oublié ? - - Veuillez entrer votre adresse e-mail pour recevoir un lien de réinitialisation de mot de passe. - - + return ( + + + + Mot de passe oublié ? + + Veuillez entrer votre adresse e-mail pour recevoir un lien de réinitialisation de mot de + passe. + + - + - - Vous avez pas de compte ? Se connecter - - - - - ); + + Vous avez pas de compte ? Se connecter + + + + + ); } diff --git a/apps/mobile-legacy/src/app/(unauthed)/signin.tsx b/apps/mobile-legacy/src/app/(unauthed)/signin.tsx index 176517b..a137a74 100644 --- a/apps/mobile-legacy/src/app/(unauthed)/signin.tsx +++ b/apps/mobile-legacy/src/app/(unauthed)/signin.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { joiResolver } from "@hookform/resolvers/joi"; import { Link, useRouter } from "expo-router"; import { useForm } from "react-hook-form"; @@ -7,7 +5,11 @@ import Toast from "react-native-toast-message"; import { YStack } from "tamagui"; import { useLogin } from "@/api/request/identity-and-access/login"; -import { LoginPayload, LoginPayloadSchema, LoginResponse } from "@/api/schema/identity-and-access/login"; +import { + LoginPayload, + LoginPayloadSchema, + LoginResponse, +} from "@/api/schema/identity-and-access/login"; import { ErrorResponse, safeMessage } from "@/api/shared"; import { useAuth } from "@/providers/auth-provider"; import { FormEmailInput, FormPasswordInput } from "@/ui/components/controls/forms"; @@ -16,66 +18,66 @@ import { ScreenView } from "@/ui/components/layout"; import { Caption, Heading, Text } from "@/ui/components/typography"; export default function SignIn() { - const { mutate, isPending } = useLogin(); - const auth = useAuth(); - const router = useRouter(); + const { mutate, isPending } = useLogin(); + const auth = useAuth(); + const router = useRouter(); - if (auth.isLoggedIn) { - router.replace("/(authed)/(tabs)/articles"); - } + if (auth.isLoggedIn) { + router.replace("/(authed)/(tabs)/articles"); + } - const { control, handleSubmit, formState } = useForm({ - resolver: joiResolver(LoginPayloadSchema), - }); + const { control, handleSubmit, formState } = useForm({ + resolver: joiResolver(LoginPayloadSchema), + }); - const onSubmit = (data: LoginPayload) => { - mutate(data, { - onSuccess: async (data: LoginResponse) => { - auth.login(data.token, data.refresh_token); - Toast.show({ text1: "Connexion réussie", type: "success" }); - }, - onError: (error: ErrorResponse) => { - Toast.show({ - text1: "Erreur de connexion", - text2: safeMessage(error), - type: "error", - }); - }, + const onSubmit = (data: LoginPayload) => { + mutate(data, { + onError: (error: ErrorResponse) => { + Toast.show({ + text1: "Erreur de connexion", + text2: safeMessage(error), + type: "error", }); - }; + }, + onSuccess: async (data: LoginResponse) => { + auth.login(data.token, data.refresh_token); + Toast.show({ text1: "Connexion réussie", type: "success" }); + }, + }); + }; - return ( - - - - Connexion - Bienvenue sur Basango, la plateforme d'actualités intelligente - + return ( + + + + Connexion + Bienvenue sur Basango, la plateforme d'actualités intelligente + - - - - - - Mot de passe oublié ? - - - + + + + + + Mot de passe oublié ? + + + - - En continuant, vous acceptez les conditions d'utilisation de Basango et reconnaissez avoir lu - notre politique de confidentialité. - - - Vous n'avez pas de compte ? Créer un compte - - - - - ); + + En continuant, vous acceptez les conditions d'utilisation de Basango et reconnaissez + avoir lu notre politique de confidentialité. + + + Vous n'avez pas de compte ? Créer un compte + + + + + ); } diff --git a/apps/mobile-legacy/src/app/(unauthed)/signup.tsx b/apps/mobile-legacy/src/app/(unauthed)/signup.tsx index 02fd6b1..18aeb52 100644 --- a/apps/mobile-legacy/src/app/(unauthed)/signup.tsx +++ b/apps/mobile-legacy/src/app/(unauthed)/signup.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { joiResolver } from "@hookform/resolvers/joi"; import { User } from "@tamagui/lucide-icons"; import { Link, useRouter } from "expo-router"; @@ -16,66 +14,66 @@ import { ScreenView } from "@/ui/components/layout"; import { Caption, Heading, Text } from "@/ui/components/typography"; export default function SingUp() { - const router = useRouter(); - const { mutate, isPending } = useRegister(); + const router = useRouter(); + const { mutate, isPending } = useRegister(); - const { control, handleSubmit, formState } = useForm({ - resolver: joiResolver(RegisterPayloadSchema), - }); + const { control, handleSubmit, formState } = useForm({ + resolver: joiResolver(RegisterPayloadSchema), + }); - const onSubmit = (data: RegisterPayload) => { - mutate(data, { - onSuccess: () => { - Toast.show({ - text1: "Félicitations !", - text2: "les détails de votre compte vous ont été envoyés par e-mail.", - type: "success", - }); - router.replace("/(unauthed)/signin"); - }, - onError: (error: ErrorResponse) => { - Toast.show({ - text1: "Erreur", - text2: safeMessage(error), - type: "error", - }); - }, + const onSubmit = (data: RegisterPayload) => { + mutate(data, { + onError: (error: ErrorResponse) => { + Toast.show({ + text1: "Erreur", + text2: safeMessage(error), + type: "error", }); - }; + }, + onSuccess: () => { + Toast.show({ + text1: "Félicitations !", + text2: "les détails de votre compte vous ont été envoyés par e-mail.", + type: "success", + }); + router.replace("/(unauthed)/signin"); + }, + }); + }; - return ( - - - - Inscription - Rejoignez la communauté Basango et restez informé des dernières actualités - + return ( + + + + Inscription + Rejoignez la communauté Basango et restez informé des dernières actualités + - - - - - - - En continuant, vous acceptez les conditions d'utilisation de Basango et reconnaissez avoir lu - notre politique de confidentialité. - - - Vous avez un compte ? Connectez-vous - - - - - ); + + + + + + + En continuant, vous acceptez les conditions d'utilisation de Basango et reconnaissez + avoir lu notre politique de confidentialité. + + + Vous avez un compte ? Connectez-vous + + + + + ); } diff --git a/apps/mobile-legacy/src/app/(unauthed)/welcome.tsx b/apps/mobile-legacy/src/app/(unauthed)/welcome.tsx index e14c3b5..2b9a34e 100644 --- a/apps/mobile-legacy/src/app/(unauthed)/welcome.tsx +++ b/apps/mobile-legacy/src/app/(unauthed)/welcome.tsx @@ -6,34 +6,34 @@ import { ScreenView } from "@/ui/components/layout"; import { Caption, Display, Text } from "@/ui/components/typography"; export default function Welcome() { - const router = useRouter(); + const router = useRouter(); - return ( - - - - - Bienvenue sur Basango - - La première plateforme d'actualités intelligente qui vous aide à rester informé sur - congolaise et internationale. - - + return ( + + + + + Bienvenue sur Basango + + La première plateforme d'actualités intelligente qui vous aide à rester informé sur + congolaise et internationale. + + - - - - Ouvrir un compte - - + + + + Ouvrir un compte + + - - En continuant, vous acceptez les conditions d'utilisation de Basango et reconnaissez avoir lu - notre politique de confidentialité. - - - - ); + + En continuant, vous acceptez les conditions d'utilisation de Basango et reconnaissez + avoir lu notre politique de confidentialité. + + + + ); } diff --git a/apps/mobile-legacy/src/app/+not-found.tsx b/apps/mobile-legacy/src/app/+not-found.tsx index 2be952e..bff11e6 100644 --- a/apps/mobile-legacy/src/app/+not-found.tsx +++ b/apps/mobile-legacy/src/app/+not-found.tsx @@ -6,28 +6,28 @@ import { ScreenView } from "@/ui/components/layout"; import { Heading, Text } from "@/ui/components/typography"; export default function NotFoundScreen() { - return ( - - - - - - - - - Une erreur s'est produite - - - Nous avons une difficulté à charger la page que vous recherchez. - - + return ( + + + + + + + + + Une erreur s'est produite + + + Nous avons une difficulté à charger la page que vous recherchez. + + - - Recommencer - - - - - - ); + + Recommencer + + + + + + ); } diff --git a/apps/mobile-legacy/src/app/_layout.tsx b/apps/mobile-legacy/src/app/_layout.tsx index 0a4f33a..2dd39c3 100644 --- a/apps/mobile-legacy/src/app/_layout.tsx +++ b/apps/mobile-legacy/src/app/_layout.tsx @@ -1,7 +1,6 @@ -import React from "react"; - import * as Sentry from "@sentry/react-native"; import { Stack } from "expo-router"; +import React from "react"; import { useColorScheme } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Toast from "react-native-toast-message"; @@ -12,28 +11,28 @@ import { RootProviders } from "@/providers/root-providers"; export { ErrorBoundary } from "expo-router"; Sentry.init({ - dsn: process.env.EXPO_PUBLIC_SENTRY_DSN, - sendDefaultPii: true, - debug: __DEV__, - tracesSampleRate: 1.0, - tracePropagationTargets: [/.*?/], - spotlight: __DEV__, + debug: __DEV__, + dsn: process.env.EXPO_PUBLIC_SENTRY_DSN, + sendDefaultPii: true, + spotlight: __DEV__, + tracePropagationTargets: [/.*?/], + tracesSampleRate: 1.0, }); function RootLayout() { - const colorScheme = useColorScheme(); - const insets = useSafeAreaInsets(); + const colorScheme = useColorScheme(); + const insets = useSafeAreaInsets(); - return ( - - - - - - - - - ); + return ( + + + + + + + + + ); } export default Sentry.wrap(RootLayout); diff --git a/apps/mobile-legacy/src/app/index.tsx b/apps/mobile-legacy/src/app/index.tsx index 108aa32..ff00fb1 100644 --- a/apps/mobile-legacy/src/app/index.tsx +++ b/apps/mobile-legacy/src/app/index.tsx @@ -3,11 +3,15 @@ import { Redirect } from "expo-router"; import { useAuth } from "@/providers/auth-provider"; export default function Index() { - const auth = useAuth(); + const auth = useAuth(); - if (!auth.isReady) { - return null; - } + if (!auth.isReady) { + return null; + } - return auth.isLoggedIn ? : ; + return auth.isLoggedIn ? ( + + ) : ( + + ); } diff --git a/apps/mobile-legacy/src/assets/illustrations/BookmarkIllustration.tsx b/apps/mobile-legacy/src/assets/illustrations/BookmarkIllustration.tsx index d0c9dc5..34a86ea 100644 --- a/apps/mobile-legacy/src/assets/illustrations/BookmarkIllustration.tsx +++ b/apps/mobile-legacy/src/assets/illustrations/BookmarkIllustration.tsx @@ -1,5 +1,3 @@ -import * as React from "react"; - import Svg, { Circle, G, Path, Rect, SvgProps } from "react-native-svg"; /** @@ -8,302 +6,314 @@ import Svg, { Circle, G, Path, Rect, SvgProps } from "react-native-svg"; * @constructor */ export default function BookmarkIllustration(props: SvgProps) { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/apps/mobile-legacy/src/hooks/use-flattened-items.ts b/apps/mobile-legacy/src/hooks/use-flattened-items.ts index fd59b7b..4b9e5ce 100644 --- a/apps/mobile-legacy/src/hooks/use-flattened-items.ts +++ b/apps/mobile-legacy/src/hooks/use-flattened-items.ts @@ -1,26 +1,26 @@ import { useMemo } from "react"; interface Page { - items: T[]; + items: T[]; } interface PaginatedResult { - pages?: Page[]; - items?: T[]; + pages?: Page[]; + items?: T[]; } export const useFlattenedItems = (data: PaginatedResult | undefined | null): T[] => { - return useMemo((): T[] => { - if (!data) { - return []; - } + return useMemo((): T[] => { + if (!data) { + return []; + } - if (data.pages && Array.isArray(data.pages) && data.pages.length > 0) { - return data.pages.flatMap(page => page.items || []); - } else if (data.items && Array.isArray(data.items)) { - return data.items; - } else { - return []; - } - }, [data]); + if (data.pages && Array.isArray(data.pages) && data.pages.length > 0) { + return data.pages.flatMap((page) => page.items || []); + } else if (data.items && Array.isArray(data.items)) { + return data.items; + } else { + return []; + } + }, [data]); }; diff --git a/apps/mobile-legacy/src/hooks/use-relative-time.ts b/apps/mobile-legacy/src/hooks/use-relative-time.ts index 6d09007..03e8f73 100644 --- a/apps/mobile-legacy/src/hooks/use-relative-time.ts +++ b/apps/mobile-legacy/src/hooks/use-relative-time.ts @@ -1,61 +1,60 @@ -import { useEffect, useState } from "react"; - import { formatDistanceToNowStrict, Locale } from "date-fns"; import { fr } from "date-fns/locale"; +import { useEffect, useState } from "react"; export const useRelativeTime = ( - dateInput: string | Date | number | null | undefined, - options?: { - addSuffix?: boolean; - unit?: "second" | "minute" | "hour" | "day" | "month" | "year"; - locale?: Locale; - roundingMethod?: "floor" | "ceil" | "round"; - includeSeconds?: boolean; - }, - updateInterval: number = 60000 + dateInput: string | Date | number | null | undefined, + options?: { + addSuffix?: boolean; + unit?: "second" | "minute" | "hour" | "day" | "month" | "year"; + locale?: Locale; + roundingMethod?: "floor" | "ceil" | "round"; + includeSeconds?: boolean; + }, + updateInterval: number = 60000, ): string => { - const [relativeTime, setRelativeTime] = useState(""); + const [relativeTime, setRelativeTime] = useState(""); - useEffect(() => { - if (dateInput === null || dateInput === undefined) { - setRelativeTime(""); - return; - } + useEffect(() => { + if (dateInput === null || dateInput === undefined) { + setRelativeTime(""); + return; + } - const date = new Date(dateInput); + const date = new Date(dateInput); - // Check if the date is valid - if (isNaN(date.getTime())) { - setRelativeTime("Invalid Date"); - return; - } + // Check if the date is valid + if (Number.isNaN(date.getTime())) { + setRelativeTime("Invalid Date"); + return; + } - const updateTime = () => { - // Default options if none provided, ensures suffix is added - const effectiveOptions = { - locale: fr, - addSuffix: true, - ...options, - }; + const updateTime = () => { + // Default options if none provided, ensures suffix is added + const effectiveOptions = { + addSuffix: true, + locale: fr, + ...options, + }; - try { - const formattedTime = formatDistanceToNowStrict(date, effectiveOptions); - setRelativeTime(formattedTime); - } catch (error) { - console.error("Error formatting relative time:", error); - setRelativeTime(dateInput.toString()); // Handle potential errors during formatting - } - }; + try { + const formattedTime = formatDistanceToNowStrict(date, effectiveOptions); + setRelativeTime(formattedTime); + } catch (error) { + console.error("Error formatting relative time:", error); + setRelativeTime(dateInput.toString()); // Handle potential errors during formatting + } + }; - // Initial update - updateTime(); + // Initial update + updateTime(); - // Set up interval for periodic updates - const intervalId = setInterval(updateTime, updateInterval); + // Set up interval for periodic updates + const intervalId = setInterval(updateTime, updateInterval); - // Clean up the interval when the component unmounts or dateInput changes - return () => clearInterval(intervalId); - }, [dateInput, options, updateInterval]); + // Clean up the interval when the component unmounts or dateInput changes + return () => clearInterval(intervalId); + }, [dateInput, options, updateInterval]); - return relativeTime; + return relativeTime; }; diff --git a/apps/mobile-legacy/src/providers/auth-provider.tsx b/apps/mobile-legacy/src/providers/auth-provider.tsx index d365bb5..5aa7d82 100644 --- a/apps/mobile-legacy/src/providers/auth-provider.tsx +++ b/apps/mobile-legacy/src/providers/auth-provider.tsx @@ -1,86 +1,88 @@ +import { SplashScreen, useRouter } from "expo-router"; import React, { createContext, useContext, useEffect, useState } from "react"; -import { useRouter, SplashScreen } from "expo-router"; - -import { clearTokens, setTokens, getAccessToken, getRefreshToken } from "@/store/auth"; +import { clearTokens, getAccessToken, getRefreshToken, setTokens } from "@/store/auth"; SplashScreen.preventAutoHideAsync(); type AuthState = { - isReady: boolean; - isLoggedIn: boolean; - login: (accessToken: string, refreshToken: string) => void; - logout: () => void; - accessToken: string | null; - refreshToken: string | null; + isReady: boolean; + isLoggedIn: boolean; + login: (accessToken: string, refreshToken: string) => void; + logout: () => void; + accessToken: string | null; + refreshToken: string | null; }; const AuthContext = createContext({ - isReady: false, - isLoggedIn: false, - login: () => {}, - logout: () => {}, - accessToken: null, - refreshToken: null, + accessToken: null, + isLoggedIn: false, + isReady: false, + login: () => {}, + logout: () => {}, + refreshToken: null, }); export function useAuth() { - return useContext(AuthContext); + return useContext(AuthContext); } export function AuthProvider({ children }: React.PropsWithChildren) { - const [isReady, setIsReady] = useState(false); - const [accessToken, setAccessToken] = useState(null); - const [refreshToken, setRefreshToken] = useState(null); - const router = useRouter(); + const [isReady, setIsReady] = useState(false); + const [accessToken, setAccessToken] = useState(null); + const [refreshToken, setRefreshToken] = useState(null); + const router = useRouter(); - const isLoggedIn = !!(accessToken && refreshToken); + const isLoggedIn = !!(accessToken && refreshToken); - const login = (access: string, refresh: string) => { - setAccessToken(access); - setRefreshToken(refresh); - setTokens(access, refresh); - router.replace("/(authed)/(tabs)/articles"); + const login = (access: string, refresh: string) => { + setAccessToken(access); + setRefreshToken(refresh); + setTokens(access, refresh); + router.replace("/(authed)/(tabs)/articles"); + }; + + const logout = () => { + setAccessToken(null); + setRefreshToken(null); + clearTokens(); + router.replace("/signin"); + }; + + useEffect(() => { + const loadTokens = async () => { + try { + const [storedAccess, storedRefresh] = await Promise.all([ + getAccessToken(), + getRefreshToken(), + ]); + + if (storedAccess && storedRefresh) { + setAccessToken(storedAccess); + setRefreshToken(storedRefresh); + } + } catch (error) { + console.error("Unable to retrieve auth tokens", error); + } finally { + setIsReady(true); + await SplashScreen.hideAsync(); + } }; + loadTokens(); + }, []); - const logout = () => { - setAccessToken(null); - setRefreshToken(null); - clearTokens(); - router.replace("/signin"); - }; - - useEffect(() => { - const loadTokens = async () => { - try { - const [storedAccess, storedRefresh] = await Promise.all([getAccessToken(), getRefreshToken()]); - - if (storedAccess && storedRefresh) { - setAccessToken(storedAccess); - setRefreshToken(storedRefresh); - } - } catch (error) { - console.error("Unable to retrieve auth tokens", error); - } finally { - setIsReady(true); - await SplashScreen.hideAsync(); - } - }; - loadTokens(); - }, []); - - return ( - - {children} - - ); + return ( + + {children} + + ); } diff --git a/apps/mobile-legacy/src/providers/fonts-loader-provider.tsx b/apps/mobile-legacy/src/providers/fonts-loader-provider.tsx index 3060351..3ca164c 100644 --- a/apps/mobile-legacy/src/providers/fonts-loader-provider.tsx +++ b/apps/mobile-legacy/src/providers/fonts-loader-provider.tsx @@ -1,7 +1,23 @@ +import { + Inter_100Thin, + Inter_200ExtraLight, + Inter_300Light, + Inter_400Regular, + Inter_500Medium, + Inter_600SemiBold, + Inter_700Bold, + Inter_800ExtraBold, + Inter_900Black, + useFonts, +} from "@expo-google-fonts/inter"; +import { SplashScreen } from "expo-router"; import type React from "react"; import { useEffect } from "react"; -import { +SplashScreen.preventAutoHideAsync(); + +export const FontsLoaderProvider = ({ children }: React.PropsWithChildren) => { + const [fontsLoaded, fontError] = useFonts({ Inter_100Thin, Inter_200ExtraLight, Inter_300Light, @@ -11,34 +27,17 @@ import { Inter_700Bold, Inter_800ExtraBold, Inter_900Black, - useFonts, -} from "@expo-google-fonts/inter"; -import { SplashScreen } from "expo-router"; + }); -SplashScreen.preventAutoHideAsync(); - -export const FontsLoaderProvider = ({ children }: React.PropsWithChildren) => { - const [fontsLoaded, fontError] = useFonts({ - Inter_100Thin, - Inter_200ExtraLight, - Inter_300Light, - Inter_400Regular, - Inter_500Medium, - Inter_600SemiBold, - Inter_700Bold, - Inter_800ExtraBold, - Inter_900Black, - }); - - useEffect(() => { - if (fontsLoaded || fontError) { - SplashScreen.hideAsync(); - } - }, [fontsLoaded, fontError]); - - if (!fontsLoaded && !fontError) { - return null; + useEffect(() => { + if (fontsLoaded || fontError) { + SplashScreen.hideAsync(); } + }, [fontsLoaded, fontError]); - return <>{children}; + if (!fontsLoaded && !fontError) { + return null; + } + + return <>{children}; }; diff --git a/apps/mobile-legacy/src/providers/network-provider.tsx b/apps/mobile-legacy/src/providers/network-provider.tsx index 73f8b0d..cc8199a 100644 --- a/apps/mobile-legacy/src/providers/network-provider.tsx +++ b/apps/mobile-legacy/src/providers/network-provider.tsx @@ -1,42 +1,41 @@ -import React, { createContext, useContext, useEffect, useState } from "react"; - import * as Network from "expo-network"; import { NetworkStateEvent } from "expo-network"; +import React, { createContext, useContext, useEffect, useState } from "react"; type NetworkState = { - isConnected: boolean; + isConnected: boolean; }; const NetworkContext = createContext({ - isConnected: true, + isConnected: true, }); export const useNetwork = () => useContext(NetworkContext); export const NetworkProvider = ({ children }: React.PropsWithChildren) => { - const [isConnected, setIsConnected] = useState(true); + const [isConnected, setIsConnected] = useState(true); - useEffect(() => { - let subscription: { remove: () => any }; + useEffect(() => { + let subscription: { remove: () => any }; - const subscribeToNetworkChanges = async () => { - const state = await Network.getNetworkStateAsync(); - updateConnection(state); + const subscribeToNetworkChanges = async () => { + const state = await Network.getNetworkStateAsync(); + updateConnection(state); - subscription = Network.addNetworkStateListener(updateConnection); - }; + subscription = Network.addNetworkStateListener(updateConnection); + }; - const updateConnection = (state: NetworkStateEvent) => { - const connected = state.isConnected && state.isInternetReachable === true; - setIsConnected(connected === undefined ? false : connected); - }; + const updateConnection = (state: NetworkStateEvent) => { + const connected = state.isConnected && state.isInternetReachable === true; + setIsConnected(connected === undefined ? false : connected); + }; - subscribeToNetworkChanges(); + subscribeToNetworkChanges(); - return () => { - subscription && subscription.remove(); - }; - }, []); + return () => { + subscription?.remove(); + }; + }, []); - return {children}; + return {children}; }; diff --git a/apps/mobile-legacy/src/providers/root-providers.tsx b/apps/mobile-legacy/src/providers/root-providers.tsx index b9f312a..f2ca6a8 100644 --- a/apps/mobile-legacy/src/providers/root-providers.tsx +++ b/apps/mobile-legacy/src/providers/root-providers.tsx @@ -10,17 +10,17 @@ import { TamaguiConfigProvider } from "@/providers/tamagui-config-provider"; import { TanstackQueryProvider } from "@/providers/tanstack-query-provider"; export const RootProviders = ({ children }: React.PropsWithChildren) => ( - - - - - - - {children} - - - - - - + + + + + + + {children} + + + + + + ); diff --git a/apps/mobile-legacy/src/providers/tamagui-config-provider.tsx b/apps/mobile-legacy/src/providers/tamagui-config-provider.tsx index a0b7709..76df54f 100644 --- a/apps/mobile-legacy/src/providers/tamagui-config-provider.tsx +++ b/apps/mobile-legacy/src/providers/tamagui-config-provider.tsx @@ -5,5 +5,5 @@ import { TamaguiProvider } from "tamagui"; import { config } from "~/tamagui.config"; export const TamaguiConfigProvider = ({ children }: React.PropsWithChildren) => ( - {children} + {children} ); diff --git a/apps/mobile-legacy/src/providers/tanstack-query-provider.tsx b/apps/mobile-legacy/src/providers/tanstack-query-provider.tsx index 83eae1b..7a10949 100644 --- a/apps/mobile-legacy/src/providers/tanstack-query-provider.tsx +++ b/apps/mobile-legacy/src/providers/tanstack-query-provider.tsx @@ -1,9 +1,8 @@ -import type React from "react"; - import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type React from "react"; export const queryClient = new QueryClient(); export const TanstackQueryProvider = ({ children }: React.PropsWithChildren) => ( - {children} + {children} ); diff --git a/apps/mobile-legacy/src/store/auth.ts b/apps/mobile-legacy/src/store/auth.ts index ad71463..9febd98 100644 --- a/apps/mobile-legacy/src/store/auth.ts +++ b/apps/mobile-legacy/src/store/auth.ts @@ -4,24 +4,24 @@ export const getAccessToken = () => SecureStore.getItemAsync("user_access_token" export const getRefreshToken = () => SecureStore.getItemAsync("user_refresh_token"); export const setTokens = async (access: string, refresh: string) => { - try { - await Promise.all([ - SecureStore.setItemAsync("user_access_token", access), - SecureStore.setItemAsync("user_refresh_token", refresh), - ]); - } catch (error) { - console.log(access, refresh); - console.error("Unable to save auth tokens", error); - } + try { + await Promise.all([ + SecureStore.setItemAsync("user_access_token", access), + SecureStore.setItemAsync("user_refresh_token", refresh), + ]); + } catch (error) { + console.log(access, refresh); + console.error("Unable to save auth tokens", error); + } }; export const clearTokens = async () => { - try { - await Promise.all([ - SecureStore.deleteItemAsync("user_access_token"), - SecureStore.deleteItemAsync("user_refresh_token"), - ]); - } catch (error) { - console.error("Unable to clear auth tokens", error); - } + try { + await Promise.all([ + SecureStore.deleteItemAsync("user_access_token"), + SecureStore.deleteItemAsync("user_refresh_token"), + ]); + } catch (error) { + console.error("Unable to clear auth tokens", error); + } }; diff --git a/apps/mobile-legacy/src/ui/components/AppIcon.tsx b/apps/mobile-legacy/src/ui/components/AppIcon.tsx index 574871d..9326652 100644 --- a/apps/mobile-legacy/src/ui/components/AppIcon.tsx +++ b/apps/mobile-legacy/src/ui/components/AppIcon.tsx @@ -1,20 +1,20 @@ import { Image } from "tamagui"; type AppLogoProps = { - width?: number; - height?: number; + width?: number; + height?: number; }; export const AppIcon = (props: AppLogoProps) => { - const { width = 80, height = 80 } = props; + const { width = 80, height = 80 } = props; - return ( - - ); + return ( + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/LoadingView.tsx b/apps/mobile-legacy/src/ui/components/LoadingView.tsx index 4faa99d..7bfb953 100644 --- a/apps/mobile-legacy/src/ui/components/LoadingView.tsx +++ b/apps/mobile-legacy/src/ui/components/LoadingView.tsx @@ -4,8 +4,15 @@ import { View } from "tamagui"; import { Caption } from "@/ui/components/typography"; export const LoadingView = () => ( - - - Chargement... - + + + Chargement... + ); diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleCategoryPill.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleCategoryPill.tsx index cecbbc4..f00f648 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleCategoryPill.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleCategoryPill.tsx @@ -1,13 +1,11 @@ -import React from "react"; - import { Caption } from "@/ui/components/typography"; type ArticleCategoryPillProps = { - category: string; + category: string; }; export const ArticleCategoryPill = (props: ArticleCategoryPillProps) => { - const { category } = props; + const { category } = props; - return {category}; + return {category}; }; diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleCoverImage.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleCoverImage.tsx index 42f465e..ce38a0d 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleCoverImage.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleCoverImage.tsx @@ -1,19 +1,21 @@ import { GetProps, Image, styled } from "tamagui"; const StyledImage = styled(Image, { - borderRadius: "$4", - backgroundColor: "$gray3", - objectFit: "cover", + backgroundColor: "$gray3", + borderRadius: "$4", + objectFit: "cover", }); type ArticleCoverImageProps = GetProps & { - uri: string; - width: string | number; - height: number; + uri: string; + width: string | number; + height: number; }; export const ArticleCoverImage = (props: ArticleCoverImageProps) => { - const { width, height, uri, ...rest } = props; + const { width, height, uri, ...rest } = props; - return ; + return ( + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleList.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleList.tsx index 1654613..50e0dcb 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleList.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleList.tsx @@ -15,97 +15,97 @@ const HorizontalSeparator = () => ; const VerticalSeparator = () => ; const LoadingIndicator = () => ( - <> - - - - + <> + + + + ); export type ArticleListDisplayMode = "card" | "magazine" | "text-only"; type ArticleListProps = Omit, "renderItem"> & { - data: ArticleOverview[]; - horizontal?: boolean; - infiniteScroll?: boolean; - displayMode?: ArticleListDisplayMode; - hasNextPage?: boolean; - isFetchingNextPage?: boolean; - fetchNextPage?: () => void; + data: ArticleOverview[]; + horizontal?: boolean; + infiniteScroll?: boolean; + displayMode?: ArticleListDisplayMode; + hasNextPage?: boolean; + isFetchingNextPage?: boolean; + fetchNextPage?: () => void; }; type ArticleListComponent = React.FC & { - HorizontalSeparator: typeof HorizontalSeparator; - VerticalSeparator: typeof VerticalSeparator; - LoadingIndicator: typeof LoadingIndicator; + HorizontalSeparator: typeof HorizontalSeparator; + VerticalSeparator: typeof VerticalSeparator; + LoadingIndicator: typeof LoadingIndicator; }; const keyExtractor = (item: ArticleOverview) => item.id; const selectDisplayComponent = (mode: ArticleListDisplayMode) => { - switch (mode) { - case "card": - return ArticleOverviewCard; - case "magazine": - return ArticleMagazineCard; - case "text-only": - return ArticleTextOnlyCard; - default: - throw new Error(`Unknown display mode: ${mode}`); - } + switch (mode) { + case "card": + return ArticleOverviewCard; + case "magazine": + return ArticleMagazineCard; + case "text-only": + return ArticleTextOnlyCard; + default: + throw new Error(`Unknown display mode: ${mode}`); + } }; const ArticleList: ArticleListComponent = (props: ArticleListProps) => { - const { - data, - displayMode = "magazine", - horizontal = false, - infiniteScroll = false, - hasNextPage, - isFetchingNextPage, - fetchNextPage, - refreshing, - ...rest - } = props; + const { + data, + displayMode = "magazine", + horizontal = false, + infiniteScroll = false, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + refreshing, + ...rest + } = props; - const renderItem = useCallback( - ({ item }: { item: ArticleOverview }) => { - const itemWidth = horizontal ? screenWidth * 0.7 : undefined; - const DisplayComponent = selectDisplayComponent(displayMode); + const renderItem = useCallback( + ({ item }: { item: ArticleOverview }) => { + const itemWidth = horizontal ? screenWidth * 0.7 : undefined; + const DisplayComponent = selectDisplayComponent(displayMode); - return ( - - - - ); - }, - [horizontal, displayMode] - ); + return ( + + + + ); + }, + [horizontal, displayMode], + ); - const handleOnEndReached = useCallback(async () => { - if (infiniteScroll && hasNextPage && !isFetchingNextPage && fetchNextPage) { - fetchNextPage(); - } - }, [hasNextPage, isFetchingNextPage, fetchNextPage, infiniteScroll]); + const handleOnEndReached = useCallback(async () => { + if (infiniteScroll && hasNextPage && !isFetchingNextPage && fetchNextPage) { + fetchNextPage(); + } + }, [hasNextPage, isFetchingNextPage, fetchNextPage, infiniteScroll]); - return ( - Pas d’articles disponibles pour le moment.} - /> - ); + return ( + Pas d’articles disponibles pour le moment.} + ListFooterComponent={infiniteScroll ? LoadingIndicator : undefined} + onEndReached={handleOnEndReached} + onEndReachedThreshold={0.5} + refreshing={refreshing} + removeClippedSubviews={true} + renderItem={renderItem} + showsHorizontalScrollIndicator={false} + /> + ); }; ArticleList.HorizontalSeparator = HorizontalSeparator; diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleMagazineCard.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleMagazineCard.tsx index 4bc65b5..34f4d78 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleMagazineCard.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleMagazineCard.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { Link } from "expo-router"; import { Card, XStack, YStack } from "tamagui"; @@ -10,36 +8,36 @@ import { SourceReferencePill } from "@/ui/components/content/source/SourceRefere import { Caption, Text } from "@/ui/components/typography"; type ArticleMagazineCardProps = { - data: ArticleOverview; + data: ArticleOverview; }; export const ArticleMagazineCard = (props: ArticleMagazineCardProps) => { - const { data } = props; - const relativeTime = useRelativeTime(data.publishedAt); + const { data } = props; + const relativeTime = useRelativeTime(data.publishedAt); - return ( - - - - - - {data.title} - - - {data.excerpt} - - + return ( + + + + + + {data.title} + + + {data.excerpt} + + - {data.image && } - - + {data.image && } + + - - - - {relativeTime} - - - - ); + + + + {relativeTime} + + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleOverviewCard.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleOverviewCard.tsx index 6047f38..4d84a22 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleOverviewCard.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleOverviewCard.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { Link } from "expo-router"; import { Card, XStack, YStack } from "tamagui"; @@ -10,35 +8,33 @@ import { SourceReferencePill } from "@/ui/components/content/source/SourceRefere import { Caption, Text } from "@/ui/components/typography"; type ArticleOverviewCardProps = { - data: ArticleOverview; + data: ArticleOverview; }; export const ArticleOverviewCard = (props: ArticleOverviewCardProps) => { - const { data } = props; - const relativeTime = useRelativeTime(data.publishedAt); + const { data } = props; + const relativeTime = useRelativeTime(data.publishedAt); - return ( - - - <> - {data.image && } - - - {data.title} - - - {data.excerpt} - - - - + return ( + + + {data.image && } + + + {data.title} + + + {data.excerpt} + + + - - - - {relativeTime} - - - - ); + + + + {relativeTime} + + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleSkeleton.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleSkeleton.tsx index f338d26..188aa2f 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleSkeleton.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleSkeleton.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import { useCallback } from "react"; import ContentLoader, { Circle, Rect } from "react-content-loader/native"; import { Dimensions, FlatList } from "react-native"; @@ -10,120 +10,122 @@ const { width: screenWidth } = Dimensions.get("window"); const data: number[] = new Array(5).fill(0); type ArticleSkeletonListProps = { - horizontal?: boolean; - displayMode?: ArticleListDisplayMode; + horizontal?: boolean; + displayMode?: ArticleListDisplayMode; }; const OverviewCardSkeleton = (props: any) => ( - - - - + + + + - - + + - - - - + + + + ); const MagazineCardSkeleton = (props: any) => ( - - + + - - - - - + + + + + - - - - + + + + ); const TextOnlyCardSkeleton = (props: any) => ( - - - + + + - - + + - - - - + + + + ); const keyExtractor = (_: number, index: number) => index.toString(); const selectSkeletonComponent = (displayMode: ArticleListDisplayMode) => { - switch (displayMode) { - case "magazine": - return MagazineCardSkeleton; - case "text-only": - return TextOnlyCardSkeleton; - default: - return OverviewCardSkeleton; - } + switch (displayMode) { + case "magazine": + return MagazineCardSkeleton; + case "text-only": + return TextOnlyCardSkeleton; + default: + return OverviewCardSkeleton; + } }; export const ArticleSkeletonList = (props: ArticleSkeletonListProps) => { - const { horizontal = false, displayMode = "magazine" } = props; + const { horizontal = false, displayMode = "magazine" } = props; - const ItemSeparator = horizontal ? ArticleList.HorizontalSeparator : ArticleList.VerticalSeparator; + const ItemSeparator = horizontal + ? ArticleList.HorizontalSeparator + : ArticleList.VerticalSeparator; - const renderItem = useCallback(() => { - const itemWidth = horizontal ? screenWidth * 0.7 : screenWidth; - const SkeletonComponent = selectSkeletonComponent(displayMode); - - return ( - - - - ); - }, [horizontal, displayMode]); + const renderItem = useCallback(() => { + const itemWidth = horizontal ? screenWidth * 0.7 : screenWidth; + const SkeletonComponent = selectSkeletonComponent(displayMode); return ( - + + + ); + }, [horizontal, displayMode]); + + return ( + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/article/ArticleTextOnlyCard.tsx b/apps/mobile-legacy/src/ui/components/content/article/ArticleTextOnlyCard.tsx index 6e6b4b8..57db9fc 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/ArticleTextOnlyCard.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/ArticleTextOnlyCard.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { Link } from "expo-router"; import { Card, XStack, YStack } from "tamagui"; @@ -9,34 +7,34 @@ import { SourceReferencePill } from "@/ui/components/content/source/SourceRefere import { Caption, Text } from "@/ui/components/typography"; type ArticleTextOnlyCardProps = { - data: ArticleOverview; + data: ArticleOverview; }; export const ArticleTextOnlyCard = (props: ArticleTextOnlyCardProps) => { - const { data } = props; - const relativeTime = useRelativeTime(data.publishedAt); + const { data } = props; + const relativeTime = useRelativeTime(data.publishedAt); - return ( - - - - - - {data.title} - - - {data.excerpt} - - - - + return ( + + + + + + {data.title} + + + {data.excerpt} + + + + - - - - {relativeTime} - - - - ); + + + + {relativeTime} + + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/article/index.tsx b/apps/mobile-legacy/src/ui/components/content/article/index.tsx index 49a666a..a3a93ec 100644 --- a/apps/mobile-legacy/src/ui/components/content/article/index.tsx +++ b/apps/mobile-legacy/src/ui/components/content/article/index.tsx @@ -1,7 +1,7 @@ export { ArticleCategoryPill } from "@/ui/components/content/article/ArticleCategoryPill"; export { ArticleCoverImage } from "@/ui/components/content/article/ArticleCoverImage"; export { ArticleList } from "@/ui/components/content/article/ArticleList"; -export { ArticleSkeletonList } from "@/ui/components/content/article/ArticleSkeleton"; export { ArticleMagazineCard } from "@/ui/components/content/article/ArticleMagazineCard"; export { ArticleOverviewCard } from "@/ui/components/content/article/ArticleOverviewCard"; +export { ArticleSkeletonList } from "@/ui/components/content/article/ArticleSkeleton"; export { ArticleTextOnlyCard } from "@/ui/components/content/article/ArticleTextOnlyCard"; diff --git a/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkCard.tsx b/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkCard.tsx index e6c285e..7c7a74a 100644 --- a/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkCard.tsx +++ b/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkCard.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { Link } from "expo-router"; import { Card, XStack, YStack } from "tamagui"; @@ -8,41 +6,41 @@ import { useRelativeTime } from "@/hooks/use-relative-time"; import { Caption, Text } from "@/ui/components/typography"; type BookmarkCardProps = { - data: Bookmark; + data: Bookmark; }; export const BookmarkCard = (props: BookmarkCardProps) => { - const { data } = props; - const relativeTime = useRelativeTime(data.createdAt); + const { data } = props; + const relativeTime = useRelativeTime(data.createdAt); - return ( - - - - - - - - {data.name} - - {data.description && ( - - {data.description} - - )} - - - + return ( + + + + + + + + {data.name} + + {data.description && ( + + {data.description} + + )} + + + - - - {data.isPublic} - {data.articlesCount} articles - {relativeTime} - - - + + + {data.isPublic} + {data.articlesCount} articles + {relativeTime} - - ); + + + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkEmptyState.tsx b/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkEmptyState.tsx index d6b761f..49e9466 100644 --- a/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkEmptyState.tsx +++ b/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkEmptyState.tsx @@ -5,13 +5,15 @@ import { CreateBookmarkSheet } from "@/ui/components/content/bookmark/CreateBook import { Heading, Text } from "@/ui/components/typography"; export const BookmarkEmptyState = () => { - return ( - - - Empty Bookmarks - Create a bookmark to save your favorite articles and access them later. + return ( + + + Empty Bookmarks + + Create a bookmark to save your favorite articles and access them later. + - - - ); + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkList.tsx b/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkList.tsx index b556e1a..b83130c 100644 --- a/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkList.tsx +++ b/apps/mobile-legacy/src/ui/components/content/bookmark/BookmarkList.tsx @@ -10,58 +10,66 @@ import { BookmarkEmptyState } from "@/ui/components/content/bookmark/BookmarkEmp const VerticalSeparator = () => ; const LoadingIndicator = () => ( - <> - - - - + <> + + + + ); type BookmarkListProps = Omit, "renderItem"> & { - data: Bookmark[]; - infiniteScroll?: boolean; - hasNextPage?: boolean; - isFetchingNextPage?: boolean; - fetchNextPage?: () => void; + data: Bookmark[]; + infiniteScroll?: boolean; + hasNextPage?: boolean; + isFetchingNextPage?: boolean; + fetchNextPage?: () => void; }; type BookmarkListComponent = React.FC & { - VerticalSeparator: typeof VerticalSeparator; - LoadingIndicator: typeof LoadingIndicator; + VerticalSeparator: typeof VerticalSeparator; + LoadingIndicator: typeof LoadingIndicator; }; const keyExtractor = (item: Bookmark) => item.id; const renderItem = ({ item }: { item: Bookmark }) => { - return ; + return ; }; const BookmarkList: BookmarkListComponent = (props: BookmarkListProps) => { - const { data, infiniteScroll = false, hasNextPage, isFetchingNextPage, fetchNextPage, refreshing, ...rest } = props; + const { + data, + infiniteScroll = false, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + refreshing, + ...rest + } = props; - const handleOnEndReached = useCallback(async () => { - if (infiniteScroll && hasNextPage && !isFetchingNextPage && fetchNextPage) { - fetchNextPage(); - } - }, [hasNextPage, isFetchingNextPage, fetchNextPage, infiniteScroll]); + const handleOnEndReached = useCallback(async () => { + if (infiniteScroll && hasNextPage && !isFetchingNextPage && fetchNextPage) { + fetchNextPage(); + } + }, [hasNextPage, isFetchingNextPage, fetchNextPage, infiniteScroll]); - return ( - } - ListFooterComponent={infiniteScroll && refreshing ? LoadingIndicator : undefined} - /> - ); + return ( + } + ListFooterComponent={infiniteScroll && refreshing ? LoadingIndicator : undefined} + onEndReached={handleOnEndReached} + onEndReachedThreshold={0.5} + refreshing={refreshing} + removeClippedSubviews={true} + renderItem={renderItem} + showsHorizontalScrollIndicator={false} + /> + ); }; BookmarkList.VerticalSeparator = VerticalSeparator; diff --git a/apps/mobile-legacy/src/ui/components/content/bookmark/CreateBookmarkSheet.tsx b/apps/mobile-legacy/src/ui/components/content/bookmark/CreateBookmarkSheet.tsx index 22c9202..6137d7f 100644 --- a/apps/mobile-legacy/src/ui/components/content/bookmark/CreateBookmarkSheet.tsx +++ b/apps/mobile-legacy/src/ui/components/content/bookmark/CreateBookmarkSheet.tsx @@ -1,7 +1,6 @@ -import { useState } from "react"; - import { joiResolver } from "@hookform/resolvers/joi"; import { Sheet } from "@tamagui/sheet"; +import { useState } from "react"; import { useForm } from "react-hook-form"; import Toast from "react-native-toast-message"; import { Button, YStack } from "tamagui"; @@ -13,92 +12,92 @@ import { FormSwitch, FormTextArea, FormTextInput } from "@/ui/components/control import { SubmitButton } from "@/ui/components/controls/SubmitButton"; export const CreateBookmarkSheet = () => { - const { mutate, isPending } = useCreateBookmark(); - const [open, setOpen] = useState(false); + const { mutate, isPending } = useCreateBookmark(); + const [open, setOpen] = useState(false); - const { control, handleSubmit, formState } = useForm({ - resolver: joiResolver(BookmarkPayloadSchema), - }); + const { control, handleSubmit, formState } = useForm({ + resolver: joiResolver(BookmarkPayloadSchema), + }); - const onSubmit = (data: BookmarkPayload) => { - mutate(data, { - onSuccess: () => { - Toast.show({ - text1: "Félicitations !", - text2: "Votre signet a été créé avec succès.", - type: "success", - }); - setOpen(false); - }, - onError: (error: ErrorResponse) => { - Toast.show({ - text1: "Erreur", - text2: safeMessage(error), - type: "error", - }); - }, + const onSubmit = (data: BookmarkPayload) => { + mutate(data, { + onError: (error: ErrorResponse) => { + Toast.show({ + text1: "Erreur", + text2: safeMessage(error), + type: "error", }); - }; + }, + onSuccess: () => { + Toast.show({ + text1: "Félicitations !", + text2: "Votre signet a été créé avec succès.", + type: "success", + }); + setOpen(false); + }, + }); + }; - return ( - - + return ( + + - - - - - - - - - - - - - - ); + + + + + + + + + + + + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/bookmark/UpdateBookmarkSheet.tsx b/apps/mobile-legacy/src/ui/components/content/bookmark/UpdateBookmarkSheet.tsx index 96c52e6..7b3bafe 100644 --- a/apps/mobile-legacy/src/ui/components/content/bookmark/UpdateBookmarkSheet.tsx +++ b/apps/mobile-legacy/src/ui/components/content/bookmark/UpdateBookmarkSheet.tsx @@ -1,13 +1,16 @@ -import { useEffect, useState } from "react"; - import { joiResolver } from "@hookform/resolvers/joi"; import { Sheet } from "@tamagui/sheet"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import Toast from "react-native-toast-message"; import { Button, YStack } from "tamagui"; import { useUpdateBookmark } from "@/api/request/feed-management/bookmark"; -import { Bookmark, BookmarkPayload, BookmarkPayloadSchema } from "@/api/schema/feed-management/bookmark"; +import { + Bookmark, + BookmarkPayload, + BookmarkPayloadSchema, +} from "@/api/schema/feed-management/bookmark"; import { ErrorResponse, safeMessage } from "@/api/shared"; import { FormSwitch } from "@/ui/components/controls/forms/Switch"; import { FormTextArea } from "@/ui/components/controls/forms/TextArea"; @@ -15,101 +18,101 @@ import { FormTextInput } from "@/ui/components/controls/forms/TextInput"; import { SubmitButton } from "@/ui/components/controls/SubmitButton"; type UpdateBookmarkSheetProps = { - data: Bookmark; + data: Bookmark; }; export const UpdateBookmarkSheet = (props: UpdateBookmarkSheetProps) => { - const { data } = props; - const { mutate, isPending } = useUpdateBookmark(data.id); - const [open, setOpen] = useState(false); + const { data } = props; + const { mutate, isPending } = useUpdateBookmark(data.id); + const [open, setOpen] = useState(false); - const { control, handleSubmit, formState, reset } = useForm({ - resolver: joiResolver(BookmarkPayloadSchema), - }); + const { control, handleSubmit, formState, reset } = useForm({ + resolver: joiResolver(BookmarkPayloadSchema), + }); - useEffect(() => { - reset({ ...data }); - }, [data, reset]); + useEffect(() => { + reset({ ...data }); + }, [data, reset]); - const onSubmit = (data: BookmarkPayload) => { - mutate(data, { - onSuccess: () => { - Toast.show({ - text1: "Félicitations !", - text2: "Votre signet a été modifié avec succès.", - type: "success", - }); - setOpen(false); - }, - onError: (error: ErrorResponse) => { - Toast.show({ - text1: "Erreur", - text2: safeMessage(error), - type: "error", - }); - }, + const onSubmit = (data: BookmarkPayload) => { + mutate(data, { + onError: (error: ErrorResponse) => { + Toast.show({ + text1: "Erreur", + text2: safeMessage(error), + type: "error", }); - }; + }, + onSuccess: () => { + Toast.show({ + text1: "Félicitations !", + text2: "Votre signet a été modifié avec succès.", + type: "success", + }); + setOpen(false); + }, + }); + }; - return ( - - + return ( + + - - - - - - - - - - - - - - ); + + + + + + + + + + + + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/source/SourceFollowButton.tsx b/apps/mobile-legacy/src/ui/components/content/source/SourceFollowButton.tsx index 2a585db..7896170 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/SourceFollowButton.tsx +++ b/apps/mobile-legacy/src/ui/components/content/source/SourceFollowButton.tsx @@ -6,57 +6,57 @@ import { Button, GetProps } from "tamagui"; import { useFollowSource, useUnfollowSource } from "@/api/request/feed-management/source"; type SourceFollowButtonProps = GetProps & { - id: string; - name: string; - followed: boolean; + id: string; + name: string; + followed: boolean; }; export const SourceFollowButton = (props: SourceFollowButtonProps) => { - const { id, followed, name, ...rest } = props; - const [isFollowed, setIsFollowed] = useState(followed); - const { mutate: follow, isPending: following } = useFollowSource(id); - const { mutate: unfollow, isPending: unfollowing } = useUnfollowSource(id); - const loading = following || unfollowing; + const { id, followed, name, ...rest } = props; + const [isFollowed, setIsFollowed] = useState(followed); + const { mutate: follow, isPending: following } = useFollowSource(id); + const { mutate: unfollow, isPending: unfollowing } = useUnfollowSource(id); + const loading = following || unfollowing; - const handlePress = useCallback(() => { - if (isFollowed) { - Alert.alert( - "Confirmation", - `Êtes-vous sûr de vouloir ne plus suivre ${name} ?`, - [ - { - text: "Annuler", - style: "cancel", - }, - { - text: "Ne plus suivre", - style: "destructive", - onPress: () => { - unfollow(); - setIsFollowed(prev => !prev); - }, - }, - ], - { cancelable: false } - ); - } else { - follow(); - setIsFollowed(prev => !prev); - } - }, [isFollowed, name, unfollow, follow, setIsFollowed]); + const handlePress = useCallback(() => { + if (isFollowed) { + Alert.alert( + "Confirmation", + `Êtes-vous sûr de vouloir ne plus suivre ${name} ?`, + [ + { + style: "cancel", + text: "Annuler", + }, + { + onPress: () => { + unfollow(); + setIsFollowed((prev) => !prev); + }, + style: "destructive", + text: "Ne plus suivre", + }, + ], + { cancelable: false }, + ); + } else { + follow(); + setIsFollowed((prev) => !prev); + } + }, [isFollowed, name, unfollow, follow]); - return ( - - ); + return ( + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/source/SourceList.tsx b/apps/mobile-legacy/src/ui/components/content/source/SourceList.tsx index 89d8c33..38f777c 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/SourceList.tsx +++ b/apps/mobile-legacy/src/ui/components/content/source/SourceList.tsx @@ -10,43 +10,43 @@ const HorizontalSeparator = () => ; const VerticalSeparator = () => ; type SourceOverviewListProps = Omit, "renderItem"> & { - data: SourceOverview[]; - horizontal?: boolean; - infiniteScroll?: boolean; + data: SourceOverview[]; + horizontal?: boolean; + infiniteScroll?: boolean; }; type SourceOverviewListComponent = React.FC & { - HorizontalSeparator: typeof HorizontalSeparator; - VerticalSeparator: typeof VerticalSeparator; + HorizontalSeparator: typeof HorizontalSeparator; + VerticalSeparator: typeof VerticalSeparator; }; const keyExtractor = (item: SourceOverview) => item.name; const SourceList: SourceOverviewListComponent = (props: SourceOverviewListProps) => { - const { data, horizontal = false, ...rest } = props; + const { data, horizontal = false, ...rest } = props; - const renderItem = useCallback( - ({ item }: { item: SourceOverview }) => { - return ; - }, - [horizontal] - ); + const renderItem = useCallback( + ({ item }: { item: SourceOverview }) => { + return ; + }, + [horizontal], + ); - return ( - Pas de sources disponibles pour le moment.} - /> - ); + return ( + Pas de sources disponibles pour le moment.} + onEndReachedThreshold={0.5} + removeClippedSubviews={true} + renderItem={renderItem} + showsHorizontalScrollIndicator={false} + /> + ); }; SourceList.HorizontalSeparator = HorizontalSeparator; diff --git a/apps/mobile-legacy/src/ui/components/content/source/SourceOverviewCard.tsx b/apps/mobile-legacy/src/ui/components/content/source/SourceOverviewCard.tsx index 64744e8..ce0b077 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/SourceOverviewCard.tsx +++ b/apps/mobile-legacy/src/ui/components/content/source/SourceOverviewCard.tsx @@ -7,70 +7,74 @@ import { SourceProfileImage } from "@/ui/components/content/source/SourceProfile import { Text } from "@/ui/components/typography"; const SourceCardFrame = styled(YStack, { - alignItems: "center", - gap: "$2", - borderRadius: "$4", + alignItems: "center", + borderRadius: "$4", + gap: "$2", - variants: { - horizontal: { - true: { - maxWidth: 100, - flexShrink: 0, - }, - false: { - flexDirection: "row", - justifyContent: "space-between", - width: "100%", - gap: "$4", - paddingVertical: "$2", - }, - }, + variants: { + horizontal: { + false: { + flexDirection: "row", + gap: "$4", + justifyContent: "space-between", + paddingVertical: "$2", + width: "100%", + }, + true: { + flexShrink: 0, + maxWidth: 100, + }, }, + }, } as const); type SourceCardProps = GetProps & { - data: SourceOverview; - horizontal?: boolean; + data: SourceOverview; + horizontal?: boolean; }; export const SourceOverviewCard = (props: SourceCardProps) => { - const { data, horizontal = true, ...rest } = props; + const { data, horizontal = true, ...rest } = props; - const nameFontSize = horizontal ? "$3" : "$4"; + const nameFontSize = horizontal ? "$3" : "$4"; - return ( - - - - + return ( + + + + - - {horizontal ? ( - - {data.displayName ?? data.name} - - ) : ( - - - - {data.displayName ?? data.name} - - + + {horizontal ? ( + + {data.displayName ?? data.name} + + ) : ( + + + + {data.displayName ?? data.name} + + - - {data.url} - - - )} - + + {data.url} + + + )} + - - - ); + + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/source/SourceProfileImage.tsx b/apps/mobile-legacy/src/ui/components/content/source/SourceProfileImage.tsx index ca2eeb4..5014123 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/SourceProfileImage.tsx +++ b/apps/mobile-legacy/src/ui/components/content/source/SourceProfileImage.tsx @@ -1,32 +1,30 @@ -import type React from "react"; - import { GetProps, Image, styled } from "tamagui"; const StyledImage = styled(Image, { - borderRadius: "$12", - backgroundColor: "white", + backgroundColor: "white", + borderRadius: "$12", }); type SourceAvatarProps = GetProps & { - image: string; - name: string; - size?: number; + image: string; + name: string; + size?: number; }; export const SourceProfileImage = (props: SourceAvatarProps) => { - const { image, name, size = 50, ...rest } = props; + const { image, name, size = 50, ...rest } = props; - return ( - - ); + return ( + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/source/SourceReferencePill.tsx b/apps/mobile-legacy/src/ui/components/content/source/SourceReferencePill.tsx index 60a29b8..e082a0b 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/SourceReferencePill.tsx +++ b/apps/mobile-legacy/src/ui/components/content/source/SourceReferencePill.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { Link } from "expo-router"; import { Avatar, GetProps, XStack } from "tamagui"; @@ -7,31 +5,31 @@ import { SourceReference } from "@/api/schema/feed-management/source"; import { Text } from "@/ui/components/typography"; type SourceReferencePillProps = GetProps & { - data: SourceReference; + data: SourceReference; }; export function SourceReferencePill(props: SourceReferencePillProps) { - const { data, ...rest } = props; + const { data, ...rest } = props; - return ( - - - - - - - - {data.displayName ?? data.name} - - - - ); + return ( + + + + + + + + {data.displayName ?? data.name} + + + + ); } diff --git a/apps/mobile-legacy/src/ui/components/content/source/SourceSkeleton.tsx b/apps/mobile-legacy/src/ui/components/content/source/SourceSkeleton.tsx index 8555bfe..36a80a2 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/SourceSkeleton.tsx +++ b/apps/mobile-legacy/src/ui/components/content/source/SourceSkeleton.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import { useCallback } from "react"; import ContentLoader, { Circle, Rect } from "react-content-loader/native"; import { FlatList } from "react-native"; @@ -9,74 +9,74 @@ import { SourceList } from "@/ui/components/content/source/SourceList"; const data: number[] = new Array(5).fill(0); type SourceSkeletonListProps = { - horizontal?: boolean; + horizontal?: boolean; }; const VerticalSkeleton = (props: any) => ( - - - - - - + + + + + + ); const HorizontalSkeleton = (props: any) => ( - - - - - + + + + + ); const keyExtractor = (_: number, index: number) => index.toString(); const selectSkeletonComponent = (horizontal: boolean) => { - return horizontal ? ( - - ) : ( - - - - ); + return horizontal ? ( + + ) : ( + + + + ); }; export const SourceSkeletonList = (props: SourceSkeletonListProps) => { - const { horizontal = false } = props; + const { horizontal = false } = props; - const ItemSeparator = horizontal ? SourceList.HorizontalSeparator : SourceList.VerticalSeparator; + const ItemSeparator = horizontal ? SourceList.HorizontalSeparator : SourceList.VerticalSeparator; - const renderItem = useCallback(() => { - return selectSkeletonComponent(horizontal); - }, [horizontal]); + const renderItem = useCallback(() => { + return selectSkeletonComponent(horizontal); + }, [horizontal]); - return ( - - ); + return ( + + ); }; diff --git a/apps/mobile-legacy/src/ui/components/content/source/index.ts b/apps/mobile-legacy/src/ui/components/content/source/index.ts index 244fccb..4128321 100644 --- a/apps/mobile-legacy/src/ui/components/content/source/index.ts +++ b/apps/mobile-legacy/src/ui/components/content/source/index.ts @@ -1,6 +1,6 @@ -export { SourceList } from "@/ui/components/content/source/SourceList"; export { SourceFollowButton } from "@/ui/components/content/source/SourceFollowButton"; +export { SourceList } from "@/ui/components/content/source/SourceList"; +export { SourceOverviewCard } from "@/ui/components/content/source/SourceOverviewCard"; +export { SourceProfileImage } from "@/ui/components/content/source/SourceProfileImage"; export { SourceReferencePill } from "@/ui/components/content/source/SourceReferencePill"; export { SourceSkeletonList } from "@/ui/components/content/source/SourceSkeleton"; -export { SourceProfileImage } from "@/ui/components/content/source/SourceProfileImage"; -export { SourceOverviewCard } from "@/ui/components/content/source/SourceOverviewCard"; diff --git a/apps/mobile-legacy/src/ui/components/controls/BackButton.tsx b/apps/mobile-legacy/src/ui/components/controls/BackButton.tsx index 1c68312..809d3ea 100644 --- a/apps/mobile-legacy/src/ui/components/controls/BackButton.tsx +++ b/apps/mobile-legacy/src/ui/components/controls/BackButton.tsx @@ -2,24 +2,24 @@ import { ArrowLeft } from "@tamagui/lucide-icons"; import { Button, ButtonProps } from "tamagui"; type BackButtonProps = { - onPress: () => void; + onPress: () => void; }; export const BackButton = (props: BackButtonProps & ButtonProps) => { - const { onPress, ...rest } = props; + const { onPress, ...rest } = props; - return ( -