Merge pull request #139 from AnonymusRaccoon/native
@ -4,3 +4,4 @@ AUTHENTICATION_SECRET=
|
|||||||
POSTGRES_USER=kyoousername
|
POSTGRES_USER=kyoousername
|
||||||
POSTGRES_PASSWORD=kyoopassword
|
POSTGRES_PASSWORD=kyoopassword
|
||||||
POSTGRES_DB=kyooDB
|
POSTGRES_DB=kyooDB
|
||||||
|
PUBLIC_BACK_URL=http://localhost:5000
|
||||||
|
4
.github/workflows/analysis.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
|||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
with:
|
with:
|
||||||
ref: ${{github.ref}}
|
ref: ${{github.ref}}
|
||||||
check-name: tests
|
check-name: "Back tests"
|
||||||
repo-token: ${{secrets.GITHUB_TOKEN}}
|
repo-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
running-workflow-name: analysis
|
running-workflow-name: analysis
|
||||||
allowed-conclusions: success,skipped,cancelled,neutral,failure
|
allowed-conclusions: success,skipped,cancelled,neutral,failure
|
||||||
@ -53,7 +53,7 @@ jobs:
|
|||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
with:
|
with:
|
||||||
ref: ${{github.event.pull_request.head.sha}}
|
ref: ${{github.event.pull_request.head.sha}}
|
||||||
check-name: tests
|
check-name: "Back tests"
|
||||||
repo-token: ${{secrets.GITHUB_TOKEN}}
|
repo-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
running-workflow-name: analysis
|
running-workflow-name: analysis
|
||||||
allowed-conclusions: success,skipped,cancelled,neutral,failure
|
allowed-conclusions: success,skipped,cancelled,neutral,failure
|
||||||
|
34
.github/workflows/coding-style.yml
vendored
@ -1,15 +1,43 @@
|
|||||||
name: CodingStyle
|
name: Coding Style
|
||||||
on: [pull_request, workflow_dispatch]
|
on: [pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
back:
|
||||||
name: "Coding style check"
|
name: "Lint Back"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: 6.0.x
|
dotnet-version: 6.0.x
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: cd back && dotnet build -p:CheckCodingStyle=true -p:TreatWarningsAsErrors=true '-p:SkipTranscoder=true'
|
run: cd back && dotnet build -p:CheckCodingStyle=true -p:TreatWarningsAsErrors=true '-p:SkipTranscoder=true'
|
||||||
|
|
||||||
|
front:
|
||||||
|
name: "Lint Front"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./front
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Find yarn cache
|
||||||
|
id: yarn-cache-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --immutable
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: yarn lint
|
||||||
|
2
.github/workflows/docker.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
- context: ./front
|
- context: ./front
|
||||||
label: front
|
label: front
|
||||||
image: ghcr.io/${{github.repository_owner}}/kyoo_front
|
image: ghcr.io/${{github.repository_owner}}/kyoo_front
|
||||||
name: Docker build ${{matrix.label}}
|
name: Build ${{matrix.label}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
7
.github/workflows/documentation.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Update the documentation
|
name: Documentation
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -6,8 +6,9 @@ on:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Building:
|
doc:
|
||||||
runs-on: [ubuntu-latest]
|
name: Update the documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: nikeee/docfx-action@v1.0.0
|
- uses: nikeee/docfx-action@v1.0.0
|
||||||
|
60
.github/workflows/native-build.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
name: Native build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- next
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
name: Expo Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./front
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Check for EXPO_TOKEN
|
||||||
|
run: |
|
||||||
|
if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
|
||||||
|
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
cache: yarn
|
||||||
|
cache-dependency-path: front/yarn.lock
|
||||||
|
|
||||||
|
- name: Setup Expo
|
||||||
|
uses: expo/expo-github-action@v7
|
||||||
|
with:
|
||||||
|
expo-version: latest
|
||||||
|
eas-version: latest
|
||||||
|
token: ${{ secrets.EXPO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --immutable
|
||||||
|
|
||||||
|
- name: Build Mobile Release
|
||||||
|
run: yarn build:mobile | tee log.txt
|
||||||
|
|
||||||
|
- name: Parse Asset URL
|
||||||
|
id: url
|
||||||
|
run: |
|
||||||
|
ASSET_URL=$(grep -oe 'https://expo.dev/artifacts/eas/.*' log.txt)
|
||||||
|
echo The android url is $ASSET_URL
|
||||||
|
echo "assetUrl=$ASSET_URL" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Download APK Asset
|
||||||
|
run: wget -O kyoo.apk ${{ steps.url.outputs.assetUrl }}
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: kyoo.apk
|
||||||
|
path: ./front/kyoo.apk
|
44
.github/workflows/native-update.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
name: Native update
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- next
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
name: Expo Update
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./front
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Check for EXPO_TOKEN
|
||||||
|
run: |
|
||||||
|
if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
|
||||||
|
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
cache: yarn
|
||||||
|
cache-dependency-path: front/yarn.lock
|
||||||
|
|
||||||
|
- name: Setup Expo
|
||||||
|
uses: expo/expo-github-action@v7
|
||||||
|
with:
|
||||||
|
expo-version: latest
|
||||||
|
eas-version: latest
|
||||||
|
token: ${{ secrets.EXPO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --immutable
|
||||||
|
|
||||||
|
- name: Publish update
|
||||||
|
run: yarn update
|
18
.github/workflows/release.yml
vendored
@ -8,6 +8,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
name: Release a new version
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -20,17 +21,16 @@ jobs:
|
|||||||
repo-token: ${{secrets.GITHUB_TOKEN}}
|
repo-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
running-workflow-name: release
|
running-workflow-name: release
|
||||||
allowed-conclusions: success,skipped,cancelled,neutral
|
allowed-conclusions: success,skipped,cancelled,neutral
|
||||||
# - name: Public the abstractions to nuget
|
- name: Download artifacts
|
||||||
# id: publish_nuget
|
uses: dawidd6/action-download-artifact@v2
|
||||||
# uses: rohith/publish-nuget@v2
|
with:
|
||||||
# with:
|
commit: ${{env.COMMIT_SHA}}
|
||||||
# PROJECT_FILE_PATH: Kyoo.Abstractions/Kyoo.Abstractions.csproj
|
workflow: native-build.yml
|
||||||
# PACKAGE_NAME: Kyoo.Abstractions
|
path: ./artifacts
|
||||||
# VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
# NUGET_KEY: ${{secrets.NUGET_API_KEY}}
|
|
||||||
# INCLUDE_SYMBOLS: true
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
generateReleaseNotes: true
|
generateReleaseNotes: true
|
||||||
|
artifacts: ./artifacts/**/*
|
||||||
token: ${{secrets.GITHUB_TOKEN}}
|
token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
3
.github/workflows/robot.yml
vendored
@ -8,7 +8,8 @@ on:
|
|||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
test:
|
||||||
|
name: Run Robot Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
1
.github/workflows/tests.yml
vendored
@ -8,6 +8,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
|
name: Back tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: mcr.microsoft.com/dotnet/sdk:6.0
|
container: mcr.microsoft.com/dotnet/sdk:6.0
|
||||||
services:
|
services:
|
||||||
|
@ -120,6 +120,24 @@ namespace Kyoo.Core.Api
|
|||||||
return _libraryManager.Search<Show>(query);
|
return _libraryManager.Search<Show>(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search items
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Search for items
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="query">The query to search for.</param>
|
||||||
|
/// <returns>A list of items found for the specified query.</returns>
|
||||||
|
[HttpGet("items")]
|
||||||
|
[HttpGet("item", Order = AlternativeRoute)]
|
||||||
|
[Permission(nameof(Show), Kind.Read)]
|
||||||
|
[ApiDefinition("Items")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
public Task<ICollection<LibraryItem>> SearchItems(string query)
|
||||||
|
{
|
||||||
|
return _libraryManager.Search<LibraryItem>(query);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Search episodes
|
/// Search episodes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -29,20 +29,24 @@ services:
|
|||||||
dockerfile: Dockerfile.dev
|
dockerfile: Dockerfile.dev
|
||||||
volumes:
|
volumes:
|
||||||
- ./front:/app
|
- ./front:/app
|
||||||
- /app/node_modules/
|
- /app/.yarn
|
||||||
- /app/.next/
|
- /app/node_modules
|
||||||
|
- /app/apps/web/.next/
|
||||||
|
- /app/apps/mobile/.expo/
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
- "19000:19000"
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
environment:
|
environment:
|
||||||
- KYOO_URL=http://back:5000
|
- KYOO_URL=${KYOO_URL:-http://back:5000}
|
||||||
|
- PUBLIC_BACK_URL=${PUBLIC_BACK_URL}
|
||||||
ingress:
|
ingress:
|
||||||
image: nginx
|
image: nginx
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
environment:
|
environment:
|
||||||
- PORT=8901
|
- PORT=8901
|
||||||
- FRONT_URL=http://front:3000
|
- FRONT_URL=http://front:3000
|
||||||
- BACK_URL=http://back:5000
|
- BACK_URL=${KYOO_URL:-http://back:5000}
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx.conf.template:/etc/nginx/templates/kyoo.conf.template:ro
|
- ./nginx.conf.template:/etc/nginx/templates/kyoo.conf.template:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -22,14 +22,15 @@ services:
|
|||||||
build: ./front
|
build: ./front
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
environment:
|
environment:
|
||||||
- KYOO_URL=http://back:5000
|
- KYOO_URL=${KYOO_URL:-http://back:5000}
|
||||||
|
- PUBLIC_BACK_URL=${PUBLIC_BACK_URL}
|
||||||
ingress:
|
ingress:
|
||||||
image: nginx
|
image: nginx
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
environment:
|
environment:
|
||||||
- PORT=8901
|
- PORT=8901
|
||||||
- FRONT_URL=http://front:8901
|
- FRONT_URL=http://front:8901
|
||||||
- BACK_URL=http://back:5000
|
- BACK_URL=${KYOO_URL:-http://back:5000}
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx.conf.template:/etc/nginx/templates/kyoo.conf.template:ro
|
- ./nginx.conf.template:/etc/nginx/templates/kyoo.conf.template:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -7,4 +7,8 @@ node_modules
|
|||||||
npm-debug.log
|
npm-debug.log
|
||||||
README.md
|
README.md
|
||||||
.next
|
.next
|
||||||
|
.expo
|
||||||
.git
|
.git
|
||||||
|
.yarn
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"extends": ["next/core-web-vitals", "prettier"],
|
"extends": ["next/core-web-vitals", "prettier"],
|
||||||
"plugins": ["header"],
|
"plugins": ["header"],
|
||||||
|
"settings": {
|
||||||
|
"next": {
|
||||||
|
"rootDir": "apps/web/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"@next/next/no-img-element": "off",
|
"@next/next/no-img-element": "off",
|
||||||
"header/header": [
|
"header/header": [
|
||||||
|
2
front/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/.yarn/releases/** binary
|
||||||
|
/.yarn/plugins/** binary
|
24
front/.gitignore
vendored
@ -1,19 +1,19 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
node_modules
|
||||||
/.pnp
|
.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
coverage
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
/.next/
|
.next/
|
||||||
/out/
|
out/
|
||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
build
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@ -33,3 +33,15 @@ yarn-error.log*
|
|||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
|
||||||
|
.expo
|
||||||
|
|
||||||
|
|
||||||
|
apps/web/next-env.d.ts
|
||||||
|
541
front/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
28
front/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
801
front/.yarn/releases/yarn-3.2.4.cjs
vendored
Executable file
20
front/.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real eslint/bin/eslint.js your application uses
|
||||||
|
module.exports = absRequire(`eslint/bin/eslint.js`);
|
20
front/.yarn/sdks/eslint/lib/api.js
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real eslint your application uses
|
||||||
|
module.exports = absRequire(`eslint`);
|
6
front/.yarn/sdks/eslint/package.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "eslint",
|
||||||
|
"version": "8.19.0-sdk",
|
||||||
|
"main": "./lib/api.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
3
front/.yarn/sdks/integrations.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This file is automatically generated by @yarnpkg/sdks.
|
||||||
|
# Manual changes might be lost!
|
||||||
|
|
20
front/.yarn/sdks/prettier/index.js
vendored
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require prettier/index.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real prettier/index.js your application uses
|
||||||
|
module.exports = absRequire(`prettier/index.js`);
|
6
front/.yarn/sdks/prettier/package.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "prettier",
|
||||||
|
"version": "2.7.1-sdk",
|
||||||
|
"main": "./index.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
20
front/.yarn/sdks/typescript/bin/tsc
vendored
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/bin/tsc
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/bin/tsc your application uses
|
||||||
|
module.exports = absRequire(`typescript/bin/tsc`);
|
20
front/.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/bin/tsserver
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/bin/tsserver your application uses
|
||||||
|
module.exports = absRequire(`typescript/bin/tsserver`);
|
20
front/.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsc.js your application uses
|
||||||
|
module.exports = absRequire(`typescript/lib/tsc.js`);
|
223
front/.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const moduleWrapper = tsserver => {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
return tsserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {isAbsolute} = require(`path`);
|
||||||
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
|
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
|
const isPortal = str => str.startsWith("portal:/");
|
||||||
|
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
|
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||||
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
|
// before forwarding it to TS, and to add it back on all returned paths.
|
||||||
|
|
||||||
|
function toEditorPath(str) {
|
||||||
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
|
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||||
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
|
// this makes it much easier to work with workspaces that list peer
|
||||||
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
|
// file instances instead of the real ones.
|
||||||
|
//
|
||||||
|
// We only do this to modules owned by the the dependency tree roots.
|
||||||
|
// This avoids breaking the resolution when jumping inside a vendor
|
||||||
|
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||||
|
// errors on react).
|
||||||
|
//
|
||||||
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
|
if (resolved) {
|
||||||
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
|
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||||
|
str = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = normalize(str);
|
||||||
|
|
||||||
|
if (str.match(/\.zip\//)) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||||
|
// VSCode only adds it automatically for supported schemes,
|
||||||
|
// so we have to do it manually for the `zip` scheme.
|
||||||
|
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||||
|
//
|
||||||
|
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||||
|
//
|
||||||
|
// 2021-10-08: VSCode changed the format in 1.61.
|
||||||
|
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-04-06: VSCode changed the format in 1.66.
|
||||||
|
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-05-06: VSCode changed the format in 1.68
|
||||||
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
case `vscode <1.61`: {
|
||||||
|
str = `^zip:${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.66`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.68`: {
|
||||||
|
str = `^/zip${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// To make "go to definition" work,
|
||||||
|
// We have to resolve the actual file system path from virtual path
|
||||||
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = resolve(`zipfile:${str}`);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
|
// We have to resolve the actual file system path from virtual path,
|
||||||
|
// everything else is up to neovim
|
||||||
|
case `neovim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = `zipfile://${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
str = `zip:${str}`;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromEditorPath(str) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
|
// before `zipfile:`
|
||||||
|
return process.platform === `win32`
|
||||||
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `neovim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`:
|
||||||
|
default: {
|
||||||
|
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force enable 'allowLocalPluginLoads'
|
||||||
|
// TypeScript tries to resolve plugins using a path relative to itself
|
||||||
|
// which doesn't work when using the global cache
|
||||||
|
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||||
|
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||||
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
|
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||||
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// And here is the point where we hijack the VSCode <-> TS communications
|
||||||
|
// by adding ourselves in the middle. We locate everything that looks
|
||||||
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
|
const Session = tsserver.server.Session;
|
||||||
|
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||||
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
|
Object.assign(Session.prototype, {
|
||||||
|
onMessage(/** @type {string | object} */ message) {
|
||||||
|
const isStringMessage = typeof message === 'string';
|
||||||
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedMessage != null &&
|
||||||
|
typeof parsedMessage === `object` &&
|
||||||
|
parsedMessage.arguments &&
|
||||||
|
typeof parsedMessage.arguments.hostInfo === `string`
|
||||||
|
) {
|
||||||
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
|
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||||
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||||
|
) ?? []).map(Number)
|
||||||
|
|
||||||
|
if (major === 1) {
|
||||||
|
if (minor < 61) {
|
||||||
|
hostInfo += ` <1.61`;
|
||||||
|
} else if (minor < 66) {
|
||||||
|
hostInfo += ` <1.66`;
|
||||||
|
} else if (minor < 68) {
|
||||||
|
hostInfo += ` <1.68`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||||
|
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalOnMessage.call(
|
||||||
|
this,
|
||||||
|
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
send(/** @type {any} */ msg) {
|
||||||
|
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tsserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||||
|
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`));
|
223
front/.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const moduleWrapper = tsserver => {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
return tsserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {isAbsolute} = require(`path`);
|
||||||
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
|
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
|
const isPortal = str => str.startsWith("portal:/");
|
||||||
|
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
|
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||||
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
|
// before forwarding it to TS, and to add it back on all returned paths.
|
||||||
|
|
||||||
|
function toEditorPath(str) {
|
||||||
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
|
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||||
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
|
// this makes it much easier to work with workspaces that list peer
|
||||||
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
|
// file instances instead of the real ones.
|
||||||
|
//
|
||||||
|
// We only do this to modules owned by the the dependency tree roots.
|
||||||
|
// This avoids breaking the resolution when jumping inside a vendor
|
||||||
|
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||||
|
// errors on react).
|
||||||
|
//
|
||||||
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
|
if (resolved) {
|
||||||
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
|
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||||
|
str = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = normalize(str);
|
||||||
|
|
||||||
|
if (str.match(/\.zip\//)) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||||
|
// VSCode only adds it automatically for supported schemes,
|
||||||
|
// so we have to do it manually for the `zip` scheme.
|
||||||
|
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||||
|
//
|
||||||
|
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||||
|
//
|
||||||
|
// 2021-10-08: VSCode changed the format in 1.61.
|
||||||
|
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-04-06: VSCode changed the format in 1.66.
|
||||||
|
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-05-06: VSCode changed the format in 1.68
|
||||||
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
case `vscode <1.61`: {
|
||||||
|
str = `^zip:${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.66`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.68`: {
|
||||||
|
str = `^/zip${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// To make "go to definition" work,
|
||||||
|
// We have to resolve the actual file system path from virtual path
|
||||||
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = resolve(`zipfile:${str}`);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
|
// We have to resolve the actual file system path from virtual path,
|
||||||
|
// everything else is up to neovim
|
||||||
|
case `neovim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = `zipfile://${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
str = `zip:${str}`;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromEditorPath(str) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
|
// before `zipfile:`
|
||||||
|
return process.platform === `win32`
|
||||||
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `neovim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`:
|
||||||
|
default: {
|
||||||
|
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force enable 'allowLocalPluginLoads'
|
||||||
|
// TypeScript tries to resolve plugins using a path relative to itself
|
||||||
|
// which doesn't work when using the global cache
|
||||||
|
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||||
|
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||||
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
|
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||||
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// And here is the point where we hijack the VSCode <-> TS communications
|
||||||
|
// by adding ourselves in the middle. We locate everything that looks
|
||||||
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
|
const Session = tsserver.server.Session;
|
||||||
|
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||||
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
|
Object.assign(Session.prototype, {
|
||||||
|
onMessage(/** @type {string | object} */ message) {
|
||||||
|
const isStringMessage = typeof message === 'string';
|
||||||
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedMessage != null &&
|
||||||
|
typeof parsedMessage === `object` &&
|
||||||
|
parsedMessage.arguments &&
|
||||||
|
typeof parsedMessage.arguments.hostInfo === `string`
|
||||||
|
) {
|
||||||
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
|
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||||
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||||
|
) ?? []).map(Number)
|
||||||
|
|
||||||
|
if (major === 1) {
|
||||||
|
if (minor < 61) {
|
||||||
|
hostInfo += ` <1.61`;
|
||||||
|
} else if (minor < 66) {
|
||||||
|
hostInfo += ` <1.66`;
|
||||||
|
} else if (minor < 68) {
|
||||||
|
hostInfo += ` <1.68`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||||
|
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalOnMessage.call(
|
||||||
|
this,
|
||||||
|
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
send(/** @type {any} */ msg) {
|
||||||
|
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tsserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||||
|
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
|
20
front/.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/typescript.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/typescript.js your application uses
|
||||||
|
module.exports = absRequire(`typescript/lib/typescript.js`);
|
6
front/.yarn/sdks/typescript/package.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "typescript",
|
||||||
|
"version": "4.7.4-sdk",
|
||||||
|
"main": "./lib/typescript.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
24
front/.yarnrc.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
packageExtensions:
|
||||||
|
"@expo/cli@*":
|
||||||
|
dependencies:
|
||||||
|
expo-modules-autolinking: "*"
|
||||||
|
babel-preset-expo@*:
|
||||||
|
dependencies:
|
||||||
|
"@babel/core": "*"
|
||||||
|
expo-asset@*:
|
||||||
|
dependencies:
|
||||||
|
expo: "*"
|
||||||
|
react-native-codegen@*:
|
||||||
|
peerDependenciesMeta:
|
||||||
|
"@babel/preset-env":
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||||
|
spec: "@yarnpkg/plugin-workspace-tools"
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||||
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.2.4.cjs
|
@ -1,20 +1,29 @@
|
|||||||
FROM node:16-alpine AS builder
|
FROM node:16-alpine AS builder
|
||||||
|
RUN apk add git bash
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY .yarn ./.yarn
|
||||||
|
COPY .yarnrc.yml ./
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json yarn.lock ./
|
||||||
RUN yarn --frozen-lockfile
|
COPY apps/web/package.json apps/web/package.json
|
||||||
|
COPY apps/mobile/package.json apps/mobile/package.json
|
||||||
|
COPY packages/ui/package.json packages/ui/package.json
|
||||||
|
COPY packages/primitives/package.json packages/primitives/package.json
|
||||||
|
COPY packages/models/package.json packages/models/package.json
|
||||||
|
RUN yarn --immutable
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
RUN yarn build
|
RUN yarn build:web
|
||||||
|
|
||||||
|
|
||||||
FROM node:16-alpine
|
FROM node:16-alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=builder /app/.next/standalone .
|
COPY --from=builder /app/apps/web/.next/standalone/apps/web .
|
||||||
COPY --from=builder /app/.next/static ./.next/static/
|
COPY --from=builder /app/apps/web/.next/standalone/node_modules ./node_modules
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/apps/web/.next/static ./.next/static/
|
||||||
|
COPY --from=builder /app/apps/web/public ./public
|
||||||
|
|
||||||
EXPOSE 8901
|
EXPOSE 8901
|
||||||
ENV PORT 8901
|
ENV PORT 8901
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
FROM node:16-alpine AS builder
|
FROM node:16-alpine AS builder
|
||||||
|
RUN apk add git bash
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY .yarn ./.yarn
|
||||||
|
COPY .yarnrc.yml ./
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json yarn.lock ./
|
||||||
RUN yarn --frozen-lockfile
|
COPY apps/web/package.json apps/web/package.json
|
||||||
|
COPY apps/mobile/package.json apps/mobile/package.json
|
||||||
|
COPY packages/ui/package.json packages/ui/package.json
|
||||||
|
COPY packages/primitives/package.json packages/primitives/package.json
|
||||||
|
COPY packages/models/package.json packages/models/package.json
|
||||||
|
RUN yarn --immutable
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENV PORT 3000
|
EXPOSE 19000
|
||||||
CMD ["yarn", "dev"]
|
CMD ["yarn", "dev"]
|
||||||
|
74
front/apps/mobile/app.config.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const IS_DEV = process.env.APP_VARIANT === "development";
|
||||||
|
|
||||||
|
// Defined outside the config because dark splashscreen needs to be platform specific.
|
||||||
|
const splash = {
|
||||||
|
image: "./assets/icon.png",
|
||||||
|
resizeMode: "contain",
|
||||||
|
backgroundColor: "#eff1f5",
|
||||||
|
dark: {
|
||||||
|
image: "./assets/icon.png",
|
||||||
|
resizeMode: "contain",
|
||||||
|
backgroundColor: "#1e1e2e",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
expo: {
|
||||||
|
name: IS_DEV ? "Kyoo Development" : "Kyoo",
|
||||||
|
slug: "kyoo",
|
||||||
|
scheme: "kyoo",
|
||||||
|
version: "1.0.0",
|
||||||
|
orientation: "default",
|
||||||
|
icon: "./assets/icon.png",
|
||||||
|
entryPoint: "./index.tsx",
|
||||||
|
userInterfaceStyle: "automatic",
|
||||||
|
splash,
|
||||||
|
updates: {
|
||||||
|
fallbackToCacheTimeout: 0,
|
||||||
|
},
|
||||||
|
assetBundlePatterns: ["**/*"],
|
||||||
|
ios: {
|
||||||
|
supportsTablet: true,
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
package: IS_DEV ? "moe.sdg.kyoo.dev" : "moe.sdg.kyoo",
|
||||||
|
adaptiveIcon: {
|
||||||
|
foregroundImage: "./assets/icon.png",
|
||||||
|
backgroundColor: "#eff1f5",
|
||||||
|
},
|
||||||
|
splash,
|
||||||
|
},
|
||||||
|
updates: {
|
||||||
|
url: "https://u.expo.dev/55de6b52-c649-4a15-9a45-569ff5ed036c",
|
||||||
|
},
|
||||||
|
runtimeVersion: {
|
||||||
|
policy: "sdkVersion",
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
eas: {
|
||||||
|
projectId: "55de6b52-c649-4a15-9a45-569ff5ed036c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
114
front/apps/mobile/app/_layout.tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PortalProvider } from "@gorhom/portal";
|
||||||
|
import { ThemeSelector } from "@kyoo/primitives";
|
||||||
|
import { NavbarRight, NavbarTitle } from "@kyoo/ui";
|
||||||
|
import { createQueryClient } from "@kyoo/models";
|
||||||
|
import { QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { Stack } from "expo-router";
|
||||||
|
import { getLocales } from "expo-localization";
|
||||||
|
import * as SplashScreen from "expo-splash-screen";
|
||||||
|
import {
|
||||||
|
useFonts,
|
||||||
|
Poppins_300Light,
|
||||||
|
Poppins_400Regular,
|
||||||
|
Poppins_900Black,
|
||||||
|
} from "@expo-google-fonts/poppins";
|
||||||
|
import { useCallback, useLayoutEffect, useState } from "react";
|
||||||
|
import { useColorScheme } from "react-native";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
import { useTheme } from "yoshiki/native";
|
||||||
|
import "intl-pluralrules";
|
||||||
|
|
||||||
|
// TODO: use a backend to load jsons.
|
||||||
|
import en from "../../../translations/en.json";
|
||||||
|
import fr from "../../../translations/fr.json";
|
||||||
|
|
||||||
|
i18next.use(initReactI18next).init({
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
fallbackLng: "en",
|
||||||
|
lng: getLocales()[0].languageCode,
|
||||||
|
resources: {
|
||||||
|
en: { translation: en },
|
||||||
|
fr: { translation: fr },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThemedStack = ({ onLayout }: { onLayout?: () => void }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
screenOptions={{
|
||||||
|
headerTitle: () => <NavbarTitle onLayout={onLayout} />,
|
||||||
|
headerRight: () => <NavbarRight />,
|
||||||
|
contentStyle: {
|
||||||
|
backgroundColor: theme.background,
|
||||||
|
},
|
||||||
|
headerStyle: {
|
||||||
|
backgroundColor: theme.appbar,
|
||||||
|
},
|
||||||
|
headerTintColor: theme.colors.white,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
|
export default function Root() {
|
||||||
|
const [queryClient] = useState(() => createQueryClient());
|
||||||
|
const theme = useColorScheme();
|
||||||
|
const [fontsLoaded] = useFonts({ Poppins_300Light, Poppins_400Regular, Poppins_900Black });
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
// This does not seems to work on the global scope so why not.
|
||||||
|
SplashScreen.preventAutoHideAsync();
|
||||||
|
})
|
||||||
|
|
||||||
|
const onLayout = useCallback(async () => {
|
||||||
|
if (fontsLoaded) {
|
||||||
|
await SplashScreen.hideAsync();
|
||||||
|
}
|
||||||
|
}, [fontsLoaded]);
|
||||||
|
|
||||||
|
if (!fontsLoaded) return null;
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<ThemeSelector
|
||||||
|
theme={theme ?? "light"}
|
||||||
|
font={{
|
||||||
|
normal: "Poppins_400Regular",
|
||||||
|
"300": "Poppins_300Light",
|
||||||
|
"400": "Poppins_400Regular",
|
||||||
|
"900": "Poppins_900Black",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PortalProvider>
|
||||||
|
<ThemedStack onLayout={onLayout} />
|
||||||
|
</PortalProvider>
|
||||||
|
</ThemeSelector>
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
@ -18,6 +18,6 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BrowsePage from "./index";
|
import { BrowsePage } from "@kyoo/ui";
|
||||||
|
|
||||||
export default BrowsePage;
|
export default BrowsePage;
|
24
front/apps/mobile/app/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import BrowsePage from "./browse";
|
||||||
|
|
||||||
|
// While there is no home page, show the browse page.
|
||||||
|
export default BrowsePage;
|
27
front/apps/mobile/app/movie/[slug].tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MovieDetails } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "../../utils";
|
||||||
|
|
||||||
|
export default withRoute(MovieDetails, {
|
||||||
|
options: { headerTransparent: true, headerStyle: { backgroundColor: "transparent" } },
|
||||||
|
statusBar: { barStyle: "light-content" },
|
||||||
|
});
|
59
front/apps/mobile/app/search/index.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SearchPage } from "@kyoo/ui";
|
||||||
|
import { Stack } from "expo-router";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { createParam } from "solito";
|
||||||
|
import { useRouter } from "solito/router";
|
||||||
|
import { useTheme } from "yoshiki/native";
|
||||||
|
|
||||||
|
const { useParam } = createParam<{ q?: string }>();
|
||||||
|
|
||||||
|
const Search = ({ route }: { route: any }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const { back } = useRouter();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [query, setQuery] = useParam("q");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
headerTitle: () => null,
|
||||||
|
// TODO: this shouuld not be null but since the header right is on the left of the search bar. shrug
|
||||||
|
headerRight: () => null,
|
||||||
|
headerSearchBarOptions: {
|
||||||
|
autoFocus: true,
|
||||||
|
headerIconColor: theme.colors.white,
|
||||||
|
hintTextColor: theme.light.overlay1,
|
||||||
|
textColor: theme.paragraph,
|
||||||
|
placeholder: t("navbar.search")!,
|
||||||
|
onClose: () => back(),
|
||||||
|
onChangeText: (e) => setQuery(e.nativeEvent.text),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SearchPage {...route.params} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Search;
|
27
front/apps/mobile/app/show/[slug].tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ShowDetails } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "../../utils";
|
||||||
|
|
||||||
|
export default withRoute(ShowDetails, {
|
||||||
|
options: { headerTransparent: true, headerStyle: { backgroundColor: "transparent" } },
|
||||||
|
statusBar: { barStyle: "light-content" },
|
||||||
|
});
|
@ -18,9 +18,13 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { withThemeProps } from "./with-theme";
|
import { Player } from "@kyoo/ui";
|
||||||
import _InfiniteScroll from "react-infinite-scroll-component";
|
import { withRoute } from "../../utils";
|
||||||
|
|
||||||
export const InfiniteScroll = withThemeProps(_InfiniteScroll, {
|
export default withRoute(Player, {
|
||||||
name: "InfiniteScroll",
|
options: {
|
||||||
|
headerShown: false,
|
||||||
|
},
|
||||||
|
statusBar: { hidden: true },
|
||||||
|
fullscreen: true,
|
||||||
});
|
});
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
32
front/apps/mobile/babel.config.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function (api) {
|
||||||
|
api.cache(true);
|
||||||
|
return {
|
||||||
|
presets: ["babel-preset-expo"],
|
||||||
|
plugins: [
|
||||||
|
// NOTE: `expo-router/babel` is a temporary extension to `babel-preset-expo`.
|
||||||
|
require.resolve("expo-router/babel"),
|
||||||
|
"transform-inline-environment-variables",
|
||||||
|
"react-native-reanimated/plugin",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
26
front/apps/mobile/eas.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"cli": {
|
||||||
|
"version": ">= 3.0.0"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"development": {
|
||||||
|
"developmentClient": true,
|
||||||
|
"distribution": "internal",
|
||||||
|
"env": {
|
||||||
|
"APP_VARIANT": "development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"distribution": "internal"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"channel": "prod",
|
||||||
|
"android": {
|
||||||
|
"buildType": "apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit": {
|
||||||
|
"production": {}
|
||||||
|
}
|
||||||
|
}
|
30
front/apps/mobile/index.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { registerRootComponent } from "expo";
|
||||||
|
import { ExpoRoot } from "expo-router";
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
// @ts-ignore
|
||||||
|
const ctx = require.context("./app");
|
||||||
|
return <ExpoRoot context={ctx} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerRootComponent(App);
|
60
front/apps/mobile/metro.config.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { getDefaultConfig } = require("expo/metro-config");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const projectRoot = __dirname;
|
||||||
|
const defaultConfig = getDefaultConfig(projectRoot);
|
||||||
|
|
||||||
|
function addMonorepoSupport(config) {
|
||||||
|
const workspaceRoot = path.resolve(projectRoot, "../..");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
watchFolders: [...config.watchFolders, workspaceRoot],
|
||||||
|
resolver: {
|
||||||
|
...config.resolver,
|
||||||
|
nodeModulesPaths: [
|
||||||
|
...config.resolver.nodeModulesPaths,
|
||||||
|
path.resolve(projectRoot, "node_modules"),
|
||||||
|
path.resolve(workspaceRoot, "node_modules"),
|
||||||
|
],
|
||||||
|
disableHierarchicalLookup: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSvgTransformer(config) {
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
transformer: {
|
||||||
|
...config.transformer,
|
||||||
|
babelTransformerPath: require.resolve("react-native-svg-transformer"),
|
||||||
|
},
|
||||||
|
resolver: {
|
||||||
|
...config.resolver,
|
||||||
|
assetExts: config.resolver.assetExts.filter((ext) => ext !== "svg"),
|
||||||
|
sourceExts: [...config.resolver.sourceExts, "svg"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addMonorepoSupport(addSvgTransformer(defaultConfig));
|
60
front/apps/mobile/package.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"name": "mobile",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.tsx",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "expo start",
|
||||||
|
"android": "expo start --android",
|
||||||
|
"ios": "expo start --ios",
|
||||||
|
"web": "expo start --web",
|
||||||
|
"build": "eas build --profile production --platform android --non-interactive",
|
||||||
|
"build:dev": "eas build --profile development --platform android --non-interactive",
|
||||||
|
"update": "eas update --auto --channel prod"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@expo-google-fonts/poppins": "^0.2.2",
|
||||||
|
"@gorhom/portal": "^1.0.14",
|
||||||
|
"@kyoo/ui": "workspace:^",
|
||||||
|
"@material-symbols/svg-400": "^0.4.2",
|
||||||
|
"@shopify/flash-list": "1.3.1",
|
||||||
|
"@tanstack/react-query": "^4.19.1",
|
||||||
|
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
||||||
|
"expo": "^47.0.0",
|
||||||
|
"expo-build-properties": "~0.4.1",
|
||||||
|
"expo-constants": "~14.0.2",
|
||||||
|
"expo-dev-client": "~2.0.1",
|
||||||
|
"expo-font": "~11.0.1",
|
||||||
|
"expo-linear-gradient": "~12.0.1",
|
||||||
|
"expo-linking": "~3.3.0",
|
||||||
|
"expo-localization": "~14.0.0",
|
||||||
|
"expo-navigation-bar": "~2.0.1",
|
||||||
|
"expo-router": "^0.0.36",
|
||||||
|
"expo-screen-orientation": "~5.0.1",
|
||||||
|
"expo-status-bar": "~1.4.2",
|
||||||
|
"expo-updates": "~0.15.6",
|
||||||
|
"i18next": "^22.0.6",
|
||||||
|
"intl-pluralrules": "^1.3.1",
|
||||||
|
"moti": "^0.21.0",
|
||||||
|
"react": "18.1.0",
|
||||||
|
"react-dom": "18.1.0",
|
||||||
|
"react-i18next": "^12.0.0",
|
||||||
|
"react-native": "0.70.5",
|
||||||
|
"react-native-reanimated": "~2.12.0",
|
||||||
|
"react-native-safe-area-context": "4.4.1",
|
||||||
|
"react-native-screens": "~3.18.0",
|
||||||
|
"react-native-svg": "13.4.0",
|
||||||
|
"react-native-video": "alpha",
|
||||||
|
"yoshiki": "0.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.19.3",
|
||||||
|
"@types/react": "~18.0.24",
|
||||||
|
"@types/react-native": "~0.70.6",
|
||||||
|
"react-native-svg-transformer": "^1.0.0",
|
||||||
|
"typescript": "^4.6.3"
|
||||||
|
},
|
||||||
|
"installConfig": {
|
||||||
|
"hoistingLimits": "workspaces"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
|
}
|
6
front/apps/mobile/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
61
front/apps/mobile/utils.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Stack } from "expo-router";
|
||||||
|
import { ComponentType, useEffect } from "react";
|
||||||
|
import { StatusBar, StatusBarProps } from "react-native";
|
||||||
|
import * as ScreenOrientation from "expo-screen-orientation";
|
||||||
|
import * as NavigationBar from "expo-navigation-bar";
|
||||||
|
|
||||||
|
const FullscreenProvider = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
|
||||||
|
NavigationBar.setVisibilityAsync("hidden");
|
||||||
|
return () => {
|
||||||
|
ScreenOrientation.unlockAsync();
|
||||||
|
NavigationBar.setVisibilityAsync("visible");
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withRoute = <Props,>(
|
||||||
|
Component: ComponentType<Props>,
|
||||||
|
options?: Parameters<typeof Stack.Screen>[0] & {
|
||||||
|
statusBar?: StatusBarProps;
|
||||||
|
fullscreen?: boolean;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { statusBar, fullscreen, ...routeOptions } = options ?? {};
|
||||||
|
const WithUseRoute = ({ route, ...props }: Props & { route: any }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{routeOptions && <Stack.Screen {...routeOptions} />}
|
||||||
|
{statusBar && <StatusBar {...statusBar} />}
|
||||||
|
{fullscreen && <FullscreenProvider />}
|
||||||
|
<Component {...route.params} {...props} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { ...all } = Component;
|
||||||
|
Object.assign(WithUseRoute, { ...all });
|
||||||
|
return WithUseRoute;
|
||||||
|
};
|
27
front/apps/web/babel.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function (api) {
|
||||||
|
api.cache(true);
|
||||||
|
return {
|
||||||
|
presets: ["babel-preset-expo"],
|
||||||
|
plugins: ["react-native-reanimated/plugin"],
|
||||||
|
};
|
||||||
|
};
|
133
front/apps/web/next.config.js
Executable file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
|
const DefinePlugin = require("webpack").DefinePlugin;
|
||||||
|
|
||||||
|
const suboctopus = path.dirname(require.resolve("libass-wasm"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("next").NextConfig}
|
||||||
|
*/
|
||||||
|
const nextConfig = {
|
||||||
|
// FIXME: https://github.com/nandorojo/moti/issues/224
|
||||||
|
reactStrictMode: false,
|
||||||
|
swcMinify: true,
|
||||||
|
output: "standalone",
|
||||||
|
webpack: (config) => {
|
||||||
|
config.plugins = [
|
||||||
|
...config.plugins,
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
context: suboctopus,
|
||||||
|
from: "*",
|
||||||
|
to: "static/chunks/",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
config.resolve = {
|
||||||
|
...config.resolve,
|
||||||
|
alias: {
|
||||||
|
...config.resolve.alias,
|
||||||
|
"react-native$": "react-native-web",
|
||||||
|
},
|
||||||
|
extensions: [".web.ts", ".web.tsx", ".web.js", ".web.jsx", ...config.resolve.extensions],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!config.plugins) config.plugins = [];
|
||||||
|
config.plugins.push(
|
||||||
|
new DefinePlugin({
|
||||||
|
__DEV__: JSON.stringify(process.env.NODE_ENV !== "production"),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.svg$/i,
|
||||||
|
issuer: /\.[jt]sx?$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "@svgr/webpack",
|
||||||
|
options: {
|
||||||
|
native: true,
|
||||||
|
svgoConfig: {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: "removeViewBox",
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
async redirects() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/",
|
||||||
|
destination: "/browse",
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
locales: ["en", "fr"],
|
||||||
|
defaultLocale: "en",
|
||||||
|
},
|
||||||
|
experimental: {
|
||||||
|
forceSwcTransforms: true,
|
||||||
|
outputFileTracingRoot: path.join(__dirname, "../../"),
|
||||||
|
transpilePackages: [
|
||||||
|
"@kyoo/ui",
|
||||||
|
"@kyoo/primitives",
|
||||||
|
"@kyoo/models",
|
||||||
|
"solito",
|
||||||
|
"react-native",
|
||||||
|
"react-native-web",
|
||||||
|
"react-native-svg",
|
||||||
|
"react-native-reanimated",
|
||||||
|
"moti",
|
||||||
|
"yoshiki",
|
||||||
|
"@expo/vector-icons",
|
||||||
|
"@expo/html-elements",
|
||||||
|
"expo-font",
|
||||||
|
"expo-asset",
|
||||||
|
"expo-av",
|
||||||
|
"expo-modules-core",
|
||||||
|
"expo-linear-gradient",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
nextConfig.rewrites = async () => [
|
||||||
|
{
|
||||||
|
source: "/api/:path*",
|
||||||
|
destination: `${process.env.KYOO_URL}/:path*` ?? "http://localhost:5000/:path*",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig;
|
54
front/apps/web/package.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint",
|
||||||
|
"format": "prettier --check --ignore-path .gitignore '!src/utils/jotai-utils.tsx' .",
|
||||||
|
"format:fix": "prettier --write --ignore-path .gitignore '!src/utils/jotai-utils.tsx' ."
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@kyoo/models": "workspace:^",
|
||||||
|
"@kyoo/primitives": "workspace:^",
|
||||||
|
"@kyoo/ui": "workspace:^",
|
||||||
|
"@material-symbols/svg-400": "^0.4.2",
|
||||||
|
"@next/font": "13.0.5",
|
||||||
|
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
||||||
|
"@tanstack/react-query": "^4.19.1",
|
||||||
|
"expo-linear-gradient": "^12.0.1",
|
||||||
|
"hls.js": "^1.2.8",
|
||||||
|
"i18next": "^22.0.6",
|
||||||
|
"jotai": "^1.10.0",
|
||||||
|
"libass-wasm": "^4.1.0",
|
||||||
|
"moti": "^0.21.0",
|
||||||
|
"next": "13.0.5",
|
||||||
|
"next-translate": "^1.6.0",
|
||||||
|
"raf": "^3.4.1",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
|
"react-i18next": "^12.0.0",
|
||||||
|
"react-native-reanimated": "^2.13.0",
|
||||||
|
"react-native-svg": "13.4.0",
|
||||||
|
"react-native-video": "alpha",
|
||||||
|
"react-native-web": "^0.18.10",
|
||||||
|
"solito": "^2.0.5",
|
||||||
|
"superjson": "^1.11.0",
|
||||||
|
"yoshiki": "0.4.5",
|
||||||
|
"zod": "^3.19.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@svgr/webpack": "^6.5.1",
|
||||||
|
"@types/node": "18.11.9",
|
||||||
|
"@types/react": "18.0.25",
|
||||||
|
"@types/react-dom": "18.0.9",
|
||||||
|
"@types/react-native-video": "^5.0.14",
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"eslint": "^8.30.0",
|
||||||
|
"eslint-config-next": "13.0.5",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"webpack": "^5.75.0"
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
BIN
front/apps/web/public/default.woff2
Normal file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 597 B |
BIN
front/apps/web/public/icon-256x256.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
29
front/apps/web/src/i18n-d.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "i18next";
|
||||||
|
import en from "../../../translations/en.json";
|
||||||
|
|
||||||
|
declare module "i18next" {
|
||||||
|
interface CustomTypeOptions {
|
||||||
|
returnNull: false;
|
||||||
|
resources: { translations: typeof en };
|
||||||
|
}
|
||||||
|
}
|
82
front/apps/web/src/i18n.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentType, useMemo } from "react";
|
||||||
|
import i18next, { InitOptions } from "i18next";
|
||||||
|
import { I18nextProvider } from "react-i18next";
|
||||||
|
import { AppContext, AppInitialProps, type AppProps } from "next/app";
|
||||||
|
|
||||||
|
import en from "../../../translations/en.json";
|
||||||
|
import fr from "../../../translations/fr.json";
|
||||||
|
|
||||||
|
export const withTranslations = (
|
||||||
|
AppToTranslate: ComponentType<AppProps> & {
|
||||||
|
getInitialProps: (ctx: AppContext) => Promise<AppInitialProps>;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const i18n = i18next.createInstance();
|
||||||
|
const commonOptions: InitOptions = {
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppWithTranslations = (props: AppProps) => {
|
||||||
|
const li18n = useMemo(
|
||||||
|
() =>
|
||||||
|
typeof window === "undefined"
|
||||||
|
? i18n
|
||||||
|
: (i18next.init({
|
||||||
|
...commonOptions,
|
||||||
|
lng: props.pageProps.__lang,
|
||||||
|
resources: props.pageProps.__resources,
|
||||||
|
}),
|
||||||
|
i18next),
|
||||||
|
[props.pageProps.__lang, props.pageProps.__resources],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<I18nextProvider i18n={li18n}>
|
||||||
|
<AppToTranslate {...props} />
|
||||||
|
</I18nextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
AppWithTranslations.getInitialProps = async (ctx: AppContext) => {
|
||||||
|
const props: AppInitialProps = await AppToTranslate.getInitialProps(ctx);
|
||||||
|
const lng = ctx.router.locale || ctx.router.defaultLocale || "en";
|
||||||
|
// TODO: use a backend to fetch only the needed translations.
|
||||||
|
// TODO: use a different backend on the client and fetch needed translations.
|
||||||
|
const resources = {
|
||||||
|
en: { translation: en },
|
||||||
|
fr: { translation: fr },
|
||||||
|
};
|
||||||
|
await i18n.init({
|
||||||
|
...commonOptions,
|
||||||
|
lng,
|
||||||
|
fallbackLng: ctx.router.defaultLocale || "en",
|
||||||
|
resources,
|
||||||
|
});
|
||||||
|
props.pageProps.__lang = lng;
|
||||||
|
props.pageProps.__resources = resources;
|
||||||
|
return props;
|
||||||
|
};
|
||||||
|
|
||||||
|
return AppWithTranslations;
|
||||||
|
};
|
@ -18,37 +18,31 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import "../polyfill";
|
||||||
import appWithI18n from "next-translate/appWithI18n";
|
|
||||||
import { ThemeProvider } from "@mui/material";
|
import { Hydrate, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import NextApp, { AppContext } from "next/app";
|
import { HiddenIfNoJs, SkeletonCss, ThemeSelector, WebTooltip } from "@kyoo/primitives";
|
||||||
import type { AppProps } from "next/app";
|
import { createQueryClient, fetchQuery, QueryIdentifier, QueryPage } from "@kyoo/models";
|
||||||
import { Hydrate, QueryClientProvider } from "react-query";
|
import { useState } from "react";
|
||||||
import { createQueryClient, fetchQuery, QueryIdentifier, QueryPage } from "~/utils/query";
|
import NextApp, { AppContext, type AppProps } from "next/app";
|
||||||
import { defaultTheme } from "~/utils/themes/default-theme";
|
import { Poppins } from "@next/font/google";
|
||||||
|
import { useTheme, useMobileHover } from "yoshiki/web";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useMobileHover } from "~/utils/utils";
|
import { withTranslations } from "../i18n";
|
||||||
|
|
||||||
// Simply silence a SSR warning (see https://github.com/facebook/react/issues/14927 for more details)
|
const font = Poppins({ weight: ["300", "400", "900"], subsets: ["latin"], display: "swap" });
|
||||||
if (typeof window === "undefined") {
|
|
||||||
React.useLayoutEffect = React.useEffect;
|
|
||||||
}
|
|
||||||
|
|
||||||
const App = ({ Component, pageProps }: AppProps) => {
|
|
||||||
const [queryClient] = useState(() => createQueryClient());
|
|
||||||
const { queryState, ...props } = superjson.deserialize<any>(pageProps ?? {});
|
|
||||||
const getLayout = (Component as QueryPage).getLayout ?? ((page) => page);
|
|
||||||
|
|
||||||
useMobileHover();
|
|
||||||
|
|
||||||
|
const GlobalCssTheme = () => {
|
||||||
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
background-color: ${defaultTheme.palette.background.default};
|
background-color: ${theme.background};
|
||||||
|
font-family: ${font.style.fontFamily};
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
@ -64,13 +58,48 @@ const App = ({ Component, pageProps }: AppProps) => {
|
|||||||
*:hover::-webkit-scrollbar-thumb {
|
*:hover::-webkit-scrollbar-thumb {
|
||||||
background-color: rgb(134, 127, 127);
|
background-color: rgb(134, 127, 127);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#__next {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infinite-scroll-component__outerdiv {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
::cue {
|
||||||
|
background-color: transparent;
|
||||||
|
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
<WebTooltip theme={theme} />
|
||||||
|
<SkeletonCss />
|
||||||
|
<HiddenIfNoJs />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = ({ Component, pageProps }: AppProps) => {
|
||||||
|
const [queryClient] = useState(() => createQueryClient());
|
||||||
|
const { queryState, ...props } = superjson.deserialize<any>(pageProps ?? { json: {} });
|
||||||
|
const layoutInfo = (Component as QueryPage).getLayout ?? (({ page }) => page);
|
||||||
|
const { Layout, props: layoutProps } =
|
||||||
|
typeof layoutInfo === "function" ? { Layout: layoutInfo, props: {} } : layoutInfo;
|
||||||
|
|
||||||
|
useMobileHover();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Kyoo</title>
|
<title>Kyoo</title>
|
||||||
</Head>
|
</Head>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<Hydrate state={queryState}>
|
<Hydrate state={queryState}>
|
||||||
<ThemeProvider theme={defaultTheme}>{getLayout(<Component {...props} />)}</ThemeProvider>
|
<ThemeSelector theme="auto" font={{ normal: "inherit" }}>
|
||||||
|
<GlobalCssTheme />
|
||||||
|
<Layout page={<Component {...props} />} {...layoutProps} />
|
||||||
|
</ThemeSelector>
|
||||||
</Hydrate>
|
</Hydrate>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</>
|
</>
|
||||||
@ -81,21 +110,15 @@ App.getInitialProps = async (ctx: AppContext) => {
|
|||||||
const appProps = await NextApp.getInitialProps(ctx);
|
const appProps = await NextApp.getInitialProps(ctx);
|
||||||
|
|
||||||
const getUrl = (ctx.Component as QueryPage).getFetchUrls;
|
const getUrl = (ctx.Component as QueryPage).getFetchUrls;
|
||||||
const urls: QueryIdentifier[] = getUrl ? getUrl(ctx.router.query as any) : [];
|
const getLayoutUrl = ((ctx.Component as QueryPage).getLayout as QueryPage)?.getFetchUrls;
|
||||||
|
|
||||||
|
const urls: QueryIdentifier[] = [
|
||||||
|
...(getUrl ? getUrl(ctx.router.query as any) : []),
|
||||||
|
...(getLayoutUrl ? getLayoutUrl(ctx.router.query as any) : []),
|
||||||
|
];
|
||||||
appProps.pageProps.queryState = await fetchQuery(urls);
|
appProps.pageProps.queryState = await fetchQuery(urls);
|
||||||
|
|
||||||
return { pageProps: superjson.serialize(appProps.pageProps) };
|
return { pageProps: superjson.serialize(appProps.pageProps) };
|
||||||
};
|
};
|
||||||
|
|
||||||
// The as any is needed since appWithI18n as wrong type hints
|
export default withTranslations(App);
|
||||||
export default appWithI18n(App as any, {
|
|
||||||
skipInitialProps: false,
|
|
||||||
locales: ["en", "fr"],
|
|
||||||
defaultLocale: "en",
|
|
||||||
loader: false,
|
|
||||||
pages: {
|
|
||||||
"*": ["common", "browse", "player"],
|
|
||||||
},
|
|
||||||
loadLocaleFrom: (locale, namespace) =>
|
|
||||||
import(`../../locales/${locale}/${namespace}`).then((m) => m.default),
|
|
||||||
});
|
|
120
front/apps/web/src/pages/_document.tsx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AppRegistry } from "react-native";
|
||||||
|
import { Html, Main, Head, NextScript, DocumentContext } from "next/document";
|
||||||
|
import { createStyleRegistry, StyleRegistryProvider } from "yoshiki/web";
|
||||||
|
|
||||||
|
export const style = `
|
||||||
|
/**
|
||||||
|
* Building on the RNWeb reset:
|
||||||
|
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
|
||||||
|
*/
|
||||||
|
html, body, #__next {
|
||||||
|
width: 100%;
|
||||||
|
/* To smooth any scrolling behavior */
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
/* Allows content to fill the viewport and go beyond the bottom */
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
#__next {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-basis: auto;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
/* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
/* Allows you to scroll below the viewport; default value is visible */
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior-y: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-ms-overflow-style: scrollbar;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Document = () => {
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Head>
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/icon-16x16.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/icon-32x32.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="64x64" href="/icon-64x64.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="128x128" href="/icon-128x128.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="256x256" href="/icon-256x256.png" />
|
||||||
|
</Head>
|
||||||
|
<body className="hoverEnabled">
|
||||||
|
<Main />
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Document.getInitialProps = async (ctx: DocumentContext) => {
|
||||||
|
const renderPage = ctx.renderPage;
|
||||||
|
const registry = createStyleRegistry();
|
||||||
|
|
||||||
|
ctx.renderPage = () =>
|
||||||
|
renderPage({
|
||||||
|
enhanceApp: (App) => (props) => {
|
||||||
|
return (
|
||||||
|
<StyleRegistryProvider registry={registry}>
|
||||||
|
<App {...props} />
|
||||||
|
</StyleRegistryProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = await ctx.defaultGetInitialProps(ctx);
|
||||||
|
|
||||||
|
AppRegistry.registerComponent("Main", () => Main);
|
||||||
|
// @ts-ignore React native web missing type.
|
||||||
|
const { getStyleElement } = AppRegistry.getApplication("Main");
|
||||||
|
const page = await ctx.renderPage();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
...page,
|
||||||
|
styles: (
|
||||||
|
<>
|
||||||
|
{props.styles}
|
||||||
|
{page.styles}
|
||||||
|
<style>{style}</style>
|
||||||
|
{getStyleElement()}
|
||||||
|
{registry.flushToComponent()}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export default Document;
|
24
front/apps/web/src/pages/browse/[slug].tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BrowsePage } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "~/router";
|
||||||
|
|
||||||
|
export default withRoute(BrowsePage);
|
24
front/apps/web/src/pages/browse/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BrowsePage } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "~/router";
|
||||||
|
|
||||||
|
export default withRoute(BrowsePage);
|
24
front/apps/web/src/pages/movie/[slug].tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MovieDetails } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "~/router";
|
||||||
|
|
||||||
|
export default withRoute(MovieDetails);
|
24
front/apps/web/src/pages/search/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SearchPage } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "~/router";
|
||||||
|
|
||||||
|
export default withRoute(SearchPage);
|
24
front/apps/web/src/pages/show/[slug].tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ShowDetails } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "~/router";
|
||||||
|
|
||||||
|
export default withRoute(ShowDetails);
|
24
front/apps/web/src/pages/watch/[slug].tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Player } from "@kyoo/ui";
|
||||||
|
import { withRoute } from "~/router";
|
||||||
|
|
||||||
|
export default withRoute(Player);
|
@ -18,24 +18,15 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { styled, experimental_sx as sx } from "@mui/system";
|
import "raf/polyfill";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
export const Container = styled("div")(
|
// FIXME need reanimated update, see https://github.com/software-mansion/react-native-reanimated/issues/3355
|
||||||
sx({
|
if (typeof window !== "undefined") {
|
||||||
display: "flex",
|
// @ts-ignore
|
||||||
px: "15px",
|
window._frameTimestamp = null;
|
||||||
mx: "auto",
|
}
|
||||||
width: {
|
// Simply silence a SSR warning (see https://github.com/facebook/react/issues/14927 for more details)
|
||||||
sm: "540px",
|
if (typeof window === "undefined") {
|
||||||
md: "880px",
|
React.useLayoutEffect = React.useEffect;
|
||||||
lg: "1170px",
|
}
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const containerPadding = {
|
|
||||||
xs: "15px",
|
|
||||||
sm: "calc((100vw - 540px) / 2)",
|
|
||||||
md: "calc((100vw - 880px) / 2)",
|
|
||||||
lg: "calc((100vw - 1170px) / 2)",
|
|
||||||
};
|
|
@ -25,6 +25,7 @@ export const withRoute = <Props,>(Component: ComponentType<Props>) => {
|
|||||||
const WithUseRoute = (props: Props) => {
|
const WithUseRoute = (props: Props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
return <Component {...router.query} {...props} />;
|
return <Component {...router.query} {...props} />;
|
||||||
};
|
};
|
||||||
|
|
@ -13,7 +13,6 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"jsxImportSource": "@emotion/react",
|
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"navbar": {
|
|
||||||
"home": "Home",
|
|
||||||
"login": "Login"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"back": "Back",
|
|
||||||
"previous": "Previous episode",
|
|
||||||
"next": "Next episode",
|
|
||||||
"play": "Play",
|
|
||||||
"pause": "Pause",
|
|
||||||
"mute": "Toggle mute",
|
|
||||||
"volume": "Volume",
|
|
||||||
"subtitles": "Subtitles",
|
|
||||||
"subtitle-none": "None",
|
|
||||||
"fullscreen": "Fullscreen"
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"navbar": {
|
|
||||||
"home": "Accueil",
|
|
||||||
"login": "Connexion"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"back": "Retour",
|
|
||||||
"previous": "Episode précédent",
|
|
||||||
"next": "Episode suivant",
|
|
||||||
"play": "Jouer",
|
|
||||||
"pause": "Pause",
|
|
||||||
"mute": "Muet",
|
|
||||||
"volume": "Volume",
|
|
||||||
"subtitles": "Sous titres",
|
|
||||||
"subtitle-none": "Aucun",
|
|
||||||
"fullscreen": "Plein-écran"
|
|
||||||
}
|
|
5
front/next-env.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* Kyoo - A portable and vast media library solution.
|
|
||||||
* Copyright (c) Kyoo.
|
|
||||||
*
|
|
||||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
*
|
|
||||||
* Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* Kyoo is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const CopyPlugin = require("copy-webpack-plugin");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import("next").NextConfig}
|
|
||||||
*/
|
|
||||||
const nextConfig = {
|
|
||||||
reactStrictMode: true,
|
|
||||||
swcMinify: true,
|
|
||||||
output: "standalone",
|
|
||||||
webpack: (config) => {
|
|
||||||
config.plugins = [
|
|
||||||
...config.plugins,
|
|
||||||
new CopyPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
context: "node_modules/@jellyfin/libass-wasm/dist/js/",
|
|
||||||
from: "*",
|
|
||||||
to: "static/chunks/",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
async redirects() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: "/",
|
|
||||||
destination: "/browse",
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
i18n: {
|
|
||||||
locales: ["en", "fr"],
|
|
||||||
defaultLocale: "en",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== "production") {
|
|
||||||
nextConfig.rewrites = async () => [
|
|
||||||
{ source: "/api/:path*", destination: process.env.KYOO_URL ?? "http://localhost:5000/:path*" },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = nextConfig;
|
|
@ -1,15 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "kyoo",
|
"name": "kyoo",
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "yarn workspaces foreach -pi run dev",
|
||||||
"build": "next build",
|
"web": "yarn workspace web dev",
|
||||||
"start": "next start",
|
"mobile": "yarn workspace mobile dev",
|
||||||
"lint": "next lint",
|
"build:web": "yarn workspace web build",
|
||||||
"format": "prettier --check --ignore-path .gitignore '!src/utils/jotai-utils.tsx' .",
|
"build:mobile": "yarn workspace mobile build",
|
||||||
"format:fix": "prettier --write --ignore-path .gitignore '!src/utils/jotai-utils.tsx' ."
|
"build:mobile:dev": "yarn workspace mobile build:dev",
|
||||||
|
"update": "yarn workspace mobile update",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
|
"eslintIgnore": [
|
||||||
|
"next-env.d.ts"
|
||||||
|
],
|
||||||
|
"workspaces": [
|
||||||
|
"apps/*",
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
@ -20,34 +28,14 @@
|
|||||||
"jsdocSingleLineComment": false,
|
"jsdocSingleLineComment": false,
|
||||||
"tsdoc": true
|
"tsdoc": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"@emotion/react": "^11.9.3",
|
|
||||||
"@emotion/styled": "^11.9.3",
|
|
||||||
"@jellyfin/libass-wasm": "^4.1.1",
|
|
||||||
"@mui/icons-material": "^5.8.4",
|
|
||||||
"@mui/material": "^5.8.7",
|
|
||||||
"hls.js": "^1.2.4",
|
|
||||||
"jotai": "^1.8.4",
|
|
||||||
"next": "12.2.2",
|
|
||||||
"next-translate": "^1.5.0",
|
|
||||||
"react": "18.2.0",
|
|
||||||
"react-dom": "18.2.0",
|
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
|
||||||
"react-query": "^4.0.0-beta.23",
|
|
||||||
"superjson": "^1.9.1",
|
|
||||||
"zod": "^3.18.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.0.3",
|
"eslint": "8.30.0",
|
||||||
"@types/react": "18.0.15",
|
"eslint-config-next": "13.0.5",
|
||||||
"@types/react-dom": "18.0.6",
|
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
|
||||||
"eslint": "8.19.0",
|
|
||||||
"eslint-config-next": "12.2.2",
|
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-header": "^3.1.1",
|
"eslint-plugin-header": "^3.1.1",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.8.0",
|
||||||
"prettier-plugin-jsdoc": "^0.3.38",
|
"prettier-plugin-jsdoc": "^0.4.2",
|
||||||
"typescript": "4.7.4"
|
"typescript": "4.9.3"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@3.2.4"
|
||||||
}
|
}
|
||||||
|
23
front/packages/models/package.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@kyoo/models",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"types": "src/index.ts",
|
||||||
|
"packageManager": "yarn@3.2.4",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.0.25",
|
||||||
|
"typescript": "^4.9.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tanstack/react-query": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-native-web": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.19.1"
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,10 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from "./resources";
|
||||||
|
export * from "./traits";
|
||||||
export * from "./page";
|
export * from "./page";
|
||||||
export * from "./kyoo-errors";
|
export * from "./kyoo-errors";
|
||||||
export * from "./traits";
|
export * from "./utils"
|
||||||
export * from "./resources";
|
|
||||||
|
export * from "./query";
|
@ -18,28 +18,36 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentType, ReactElement, ReactNode } from "react";
|
import { ComponentProps, ComponentType, ReactElement } from "react";
|
||||||
import {
|
import {
|
||||||
dehydrate,
|
dehydrate,
|
||||||
QueryClient,
|
QueryClient,
|
||||||
QueryFunctionContext,
|
QueryFunctionContext,
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
|
UseInfiniteQueryOptions,
|
||||||
useQuery,
|
useQuery,
|
||||||
} from "react-query";
|
} from "@tanstack/react-query";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { KyooErrors, Page } from "~/models";
|
import { KyooErrors } from "./kyoo-errors";
|
||||||
import { Paged } from "~/models/page";
|
import { Page, Paged } from "./page";
|
||||||
|
import { Platform } from "react-native";
|
||||||
|
|
||||||
const queryFn = async <Data>(
|
const queryFn = async <Data,>(
|
||||||
type: z.ZodType<Data>,
|
type: z.ZodType<Data>,
|
||||||
context: QueryFunctionContext,
|
context: QueryFunctionContext,
|
||||||
): Promise<Data> => {
|
): Promise<Data> => {
|
||||||
const kyoo_url = process.env.KYOO_URL ?? "http://localhost:5000";
|
const kyooUrl =
|
||||||
|
Platform.OS !== "web"
|
||||||
|
? process.env.PUBLIC_BACK_URL
|
||||||
|
: typeof window === "undefined"
|
||||||
|
? process.env.KYOO_URL ?? "http://localhost:5000"
|
||||||
|
: "/api";
|
||||||
|
if (!kyooUrl) console.error("Kyoo's url is not defined.");
|
||||||
|
|
||||||
let resp;
|
let resp;
|
||||||
try {
|
try {
|
||||||
resp = await fetch(
|
resp = await fetch(
|
||||||
[typeof window === "undefined" ? kyoo_url : "/api"]
|
[kyooUrl]
|
||||||
.concat(
|
.concat(
|
||||||
context.pageParam ? [context.pageParam] : (context.queryKey.filter((x) => x) as string[]),
|
context.pageParam ? [context.pageParam] : (context.queryKey.filter((x) => x) as string[]),
|
||||||
)
|
)
|
||||||
@ -93,24 +101,30 @@ export const createQueryClient = () =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export type QueryIdentifier<T = unknown> = {
|
export type QueryIdentifier<T = unknown> = {
|
||||||
parser: z.ZodType<T>;
|
parser: z.ZodType<T, z.ZodTypeDef, any>;
|
||||||
path: (string | undefined)[];
|
path: (string | undefined)[];
|
||||||
params?: { [query: string]: boolean | number | string | string[] | undefined };
|
params?: { [query: string]: boolean | number | string | string[] | undefined };
|
||||||
infinite?: boolean;
|
infinite?: boolean;
|
||||||
|
/**
|
||||||
|
* A custom get next function if the infinite query is not a page.
|
||||||
|
*/
|
||||||
|
getNext?: (item: unknown) => string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QueryPage<Props = {}> = ComponentType<Props> & {
|
export type QueryPage<Props = {}> = ComponentType<Props> & {
|
||||||
getFetchUrls?: (route: { [key: string]: string }) => QueryIdentifier[];
|
getFetchUrls?: (route: { [key: string]: string }) => QueryIdentifier[];
|
||||||
getLayout?: (page: ReactElement) => ReactNode;
|
getLayout?:
|
||||||
|
| ComponentType<{ page: ReactElement }>
|
||||||
|
| { Layout: ComponentType<{ page: ReactElement }>; props: object };
|
||||||
};
|
};
|
||||||
|
|
||||||
const toQueryKey = <Data>(query: QueryIdentifier<Data>) => {
|
const toQueryKey = <Data,>(query: QueryIdentifier<Data>) => {
|
||||||
if (query.params) {
|
if (query.params) {
|
||||||
return [
|
return [
|
||||||
...query.path,
|
...query.path,
|
||||||
"?" +
|
"?" +
|
||||||
Object.entries(query.params)
|
Object.entries(query.params)
|
||||||
.filter(([k, v]) => v !== undefined)
|
.filter(([_, v]) => v !== undefined)
|
||||||
.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`)
|
.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`)
|
||||||
.join("&"),
|
.join("&"),
|
||||||
];
|
];
|
||||||
@ -119,14 +133,28 @@ const toQueryKey = <Data>(query: QueryIdentifier<Data>) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFetch = <Data>(query: QueryIdentifier<Data>) => {
|
export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
|
||||||
return useQuery<Data, KyooErrors>({
|
return useQuery<Data, KyooErrors>({
|
||||||
queryKey: toQueryKey(query),
|
queryKey: toQueryKey(query),
|
||||||
queryFn: (ctx) => queryFn(query.parser, ctx),
|
queryFn: (ctx) => queryFn(query.parser, ctx),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useInfiniteFetch = <Data>(query: QueryIdentifier<Data>) => {
|
export const useInfiniteFetch = <Data,>(
|
||||||
|
query: QueryIdentifier<Data>,
|
||||||
|
options?: Partial<UseInfiniteQueryOptions<Data[], KyooErrors>>,
|
||||||
|
) => {
|
||||||
|
if (query.getNext) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const ret = useInfiniteQuery<Data[], KyooErrors>({
|
||||||
|
queryKey: toQueryKey(query),
|
||||||
|
queryFn: (ctx) => queryFn(z.array(query.parser), ctx),
|
||||||
|
getNextPageParam: query.getNext,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
return { ...ret, items: ret.data?.pages.flatMap((x) => x) };
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const ret = useInfiniteQuery<Page<Data>, KyooErrors>({
|
const ret = useInfiniteQuery<Page<Data>, KyooErrors>({
|
||||||
queryKey: toQueryKey(query),
|
queryKey: toQueryKey(query),
|
||||||
queryFn: (ctx) => queryFn(Paged(query.parser), ctx),
|
queryFn: (ctx) => queryFn(Paged(query.parser), ctx),
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zdate } from "~/utils/zod";
|
import { zdate } from "../utils";
|
||||||
import { ImagesP } from "../traits";
|
import { ImagesP } from "../traits";
|
||||||
import { ResourceP } from "../traits/resource";
|
import { ResourceP } from "../traits/resource";
|
||||||
|
|
@ -28,3 +28,4 @@ export * from "./person";
|
|||||||
export * from "./studio";
|
export * from "./studio";
|
||||||
export * from "./episode";
|
export * from "./episode";
|
||||||
export * from "./season";
|
export * from "./season";
|
||||||
|
export * from "./watch-item";
|
@ -34,7 +34,7 @@ export enum ItemType {
|
|||||||
|
|
||||||
export const LibraryItemP = z.preprocess(
|
export const LibraryItemP = z.preprocess(
|
||||||
(x: any) => {
|
(x: any) => {
|
||||||
x.aliases ??= [];
|
if (!x.aliases) x.aliases = [];
|
||||||
return x;
|
return x;
|
||||||
},
|
},
|
||||||
z.union([
|
z.union([
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zdate } from "~/utils/zod";
|
import { zdate } from "../utils";
|
||||||
import { ImagesP, ResourceP } from "../traits";
|
import { ImagesP, ResourceP } from "../traits";
|
||||||
import { GenreP } from "./genre";
|
import { GenreP } from "./genre";
|
||||||
import { StudioP } from "./studio";
|
import { StudioP } from "./studio";
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zdate } from "~/utils/zod";
|
import { zdate } from "../utils";
|
||||||
import { ImagesP } from "../traits";
|
import { ImagesP } from "../traits";
|
||||||
import { ResourceP } from "../traits/resource";
|
import { ResourceP } from "../traits/resource";
|
||||||
|
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zdate } from "~/utils/zod";
|
import { zdate } from "../utils";
|
||||||
import { ImagesP, ResourceP } from "../traits";
|
import { ImagesP, ResourceP } from "../traits";
|
||||||
import { GenreP } from "./genre";
|
import { GenreP } from "./genre";
|
||||||
import { SeasonP } from "./season";
|
import { SeasonP } from "./season";
|