Compare commits

..

3 Commits

Author SHA1 Message Date
Lance Chant
4db89ec916 fixed typo
Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
2026-07-04 13:10:51 +02:00
Lance Chant
0bb7aa68ae addressing PR comments
Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
2026-07-04 13:00:54 +02:00
Lance Chant
d1637a778e feat: adding episode count indicator
Added a series count indicator, and watched indicator for series

Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
2026-07-04 12:33:59 +02:00
14 changed files with 410 additions and 439 deletions

View File

@@ -30,66 +30,64 @@ jobs:
actions: write # dispatch artifact-comment.yml to refresh the PR comment actions: write # dispatch artifact-comment.yml to refresh the PR comment
steps: steps:
- parallel: - name: 🗑️ Free Disk Space
- name: 🗑️ Free Disk Space uses: BRAINSia/free-disk-space@7048ffbf50819342ac964ef3998a51c2564a8a75 # v2.1.3
uses: BRAINSia/free-disk-space@7048ffbf50819342ac964ef3998a51c2564a8a75 # v2.1.3 with:
with: tool-cache: false
tool-cache: false mandb: true
mandb: true android: false
android: false dotnet: true
dotnet: true haskell: true
haskell: true large-packages: false
large-packages: false docker-images: true
docker-images: true swap-storage: false
swap-storage: false
- name: 📥 Checkout code - name: 📥 Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
show-progress: false show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: ☕ Set up JDK 17 - name: 💾 Cache Bun dependencies
# ubuntu-26.04 defaults to JDK 25, which breaks the RN/AGP native build uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# (Kotlin falls back to JVM_23, the foojay toolchain + CMake configure with:
# fail). Pin Temurin 17 for a deterministic Android build. path: ~/.bun/install/cache
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
with: restore-keys: |
distribution: temurin ${{ runner.os }}-${{ runner.arch }}-bun-
java-version: "17"
- parallel:
- name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-bun-
- name: 💾 Cache Gradle global
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: |
~/.gradle/caches/modules-2
~/.gradle/wrapper
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-gradle-
- name: 📦 Install dependencies and reload submodules - name: 📦 Install dependencies and reload submodules
run: | run: |
bun install --frozen-lockfile bun install --frozen-lockfile
bun run submodule-reload bun run submodule-reload
- name: ☕ Set up JDK 17
# ubuntu-26.04 defaults to JDK 25, which breaks the RN/AGP native build
# (Kotlin falls back to JVM_23, the foojay toolchain + CMake configure
# fail). Pin Temurin 17 for a deterministic Android build.
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
with:
distribution: temurin
java-version: "17"
- name: 💾 Cache Gradle global
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: |
~/.gradle/caches/modules-2
~/.gradle/wrapper
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-gradle-
- name: 🛠️ Generate project files - name: 🛠️ Generate project files
run: bun run prebuild run: bun run prebuild
@@ -132,66 +130,64 @@ jobs:
actions: write # dispatch artifact-comment.yml to refresh the PR comment actions: write # dispatch artifact-comment.yml to refresh the PR comment
steps: steps:
- parallel: - name: 🗑️ Free Disk Space
- name: 🗑️ Free Disk Space uses: BRAINSia/free-disk-space@7048ffbf50819342ac964ef3998a51c2564a8a75 # v2.1.3
uses: BRAINSia/free-disk-space@7048ffbf50819342ac964ef3998a51c2564a8a75 # v2.1.3 with:
with: tool-cache: false
tool-cache: false mandb: true
mandb: true android: false
android: false dotnet: true
dotnet: true haskell: true
haskell: true large-packages: false
large-packages: false docker-images: true
docker-images: true swap-storage: false
swap-storage: false
- name: 📥 Checkout code - name: 📥 Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
show-progress: false show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: ☕ Set up JDK 17 - name: 💾 Cache Bun dependencies
# ubuntu-26.04 defaults to JDK 25, which breaks the RN/AGP native build uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# (Kotlin falls back to JVM_23, the foojay toolchain + CMake configure with:
# fail). Pin Temurin 17 for a deterministic Android build. path: ~/.bun/install/cache
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
with: restore-keys: |
distribution: temurin ${{ runner.os }}-${{ runner.arch }}-bun-
java-version: "17"
- parallel:
- name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-bun-
- name: 💾 Cache Gradle global
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: |
~/.gradle/caches/modules-2
~/.gradle/wrapper
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-gradle-
- name: 📦 Install dependencies and reload submodules - name: 📦 Install dependencies and reload submodules
run: | run: |
bun install --frozen-lockfile bun install --frozen-lockfile
bun run submodule-reload bun run submodule-reload
- name: ☕ Set up JDK 17
# ubuntu-26.04 defaults to JDK 25, which breaks the RN/AGP native build
# (Kotlin falls back to JVM_23, the foojay toolchain + CMake configure
# fail). Pin Temurin 17 for a deterministic Android build.
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
with:
distribution: temurin
java-version: "17"
- name: 💾 Cache Gradle global
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: |
~/.gradle/caches/modules-2
~/.gradle/wrapper
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-gradle-
- name: 🛠️ Generate project files - name: 🛠️ Generate project files
run: bun run prebuild:tv run: bun run prebuild:tv
@@ -233,33 +229,19 @@ jobs:
actions: write # dispatch artifact-comment.yml to refresh the PR comment actions: write # dispatch artifact-comment.yml to refresh the PR comment
steps: steps:
- parallel: - name: 📥 Checkout code
- name: 📥 Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0
fetch-depth: 0 submodules: recursive
submodules: recursive show-progress: false
show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.6"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
eas-cache: true
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
@@ -277,6 +259,19 @@ jobs:
- name: 🛠️ Generate project files - name: 🛠️ Generate project files
run: bun run prebuild run: bun run prebuild
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.5"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
eas-cache: true
- name: 🚀 Build iOS app - name: 🚀 Build iOS app
env: env:
EXPO_TV: 0 EXPO_TV: 0
@@ -306,26 +301,19 @@ jobs:
actions: write # dispatch artifact-comment.yml to refresh the PR comment actions: write # dispatch artifact-comment.yml to refresh the PR comment
steps: steps:
- parallel: - name: 📥 Checkout code
- name: 📥 Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0
fetch-depth: 0 submodules: recursive
submodules: recursive show-progress: false
show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.6"
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
@@ -343,6 +331,12 @@ jobs:
- name: 🛠️ Generate project files - name: 🛠️ Generate project files
run: bun run prebuild run: bun run prebuild
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.5"
- name: 🚀 Build iOS app - name: 🚀 Build iOS app
env: env:
EXPO_TV: 0 EXPO_TV: 0
@@ -374,33 +368,19 @@ jobs:
actions: write # dispatch artifact-comment.yml to refresh the PR comment actions: write # dispatch artifact-comment.yml to refresh the PR comment
steps: steps:
- parallel: - name: 📥 Checkout code
- name: 📥 Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0
fetch-depth: 0 submodules: recursive
submodules: recursive show-progress: false
show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.6"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
eas-cache: true
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
@@ -418,6 +398,19 @@ jobs:
- name: 🛠️ Generate project files - name: 🛠️ Generate project files
run: bun run prebuild:tv run: bun run prebuild:tv
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.5"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
eas-cache: true
- name: 🚀 Build iOS app - name: 🚀 Build iOS app
env: env:
EXPO_TV: 1 EXPO_TV: 1
@@ -445,26 +438,19 @@ jobs:
actions: write # dispatch artifact-comment.yml to refresh the PR comment actions: write # dispatch artifact-comment.yml to refresh the PR comment
steps: steps:
- parallel: - name: 📥 Checkout code
- name: 📥 Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0
fetch-depth: 0 submodules: recursive
submodules: recursive show-progress: false
show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.6"
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
@@ -482,6 +468,12 @@ jobs:
- name: 🛠️ Generate project files - name: 🛠️ Generate project files
run: bun run prebuild:tv run: bun run prebuild:tv
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
# renovate: datasource=custom.xcode depName=xcode versioning=loose
xcode-version: "26.5"
- name: 🚀 Build iOS app - name: 🚀 Build iOS app
env: env:
EXPO_TV: 1 EXPO_TV: 1

View File

@@ -18,19 +18,19 @@ jobs:
contents: read contents: read
steps: steps:
- parallel: - name: 📥 Checkout repository
- name: 📥 Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: ref: ${{ github.event.pull_request.head.sha || github.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }} show-progress: false
show-progress: false submodules: recursive
submodules: recursive fetch-depth: 0
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0

View File

@@ -20,15 +20,14 @@ jobs:
issues: write issues: write
contents: read contents: read
steps: steps:
- parallel: - name: 📥 Checkout repository
- name: 📥 Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🔍 Detect duplicate issues - name: 🔍 Detect duplicate issues
run: bun scripts/detect-duplicate-issue.ts run: bun scripts/detect-duplicate-issue.ts

View File

@@ -2,7 +2,7 @@ name: 🚦 Security & Quality Gate
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, edited, synchronize, reopened]
branches: [develop, master] branches: [develop, master]
workflow_dispatch: workflow_dispatch:
push: push:
@@ -11,11 +11,39 @@ on:
permissions: permissions:
contents: read contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
validate_pr_title:
name: "📝 Validate PR Title"
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
permissions:
pull-requests: write
contents: read
steps:
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
if: always() && (steps.lint_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋🏼
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/).
**Error details:**
```
${{ steps.lint_pr_title.outputs.error_message }}
```
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
with:
header: pr-title-lint-error
delete: true
dependency-review: dependency-review:
name: 🔍 Vulnerable Dependencies name: 🔍 Vulnerable Dependencies
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
@@ -39,26 +67,18 @@ jobs:
name: 🚑 Expo Doctor Check name: 🚑 Expo Doctor Check
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- parallel: - name: 🛒 Checkout repository
- name: 🛒 Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
# renovate: datasource=npm depName=bun
bun-version: "1.3.14"
- name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with: with:
path: ~/.bun/install/cache ref: ${{ github.event.pull_request.head.sha || github.sha }}
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }} submodules: recursive
restore-keys: | fetch-depth: 0
${{ runner.os }}-${{ runner.arch }}-bun-
- name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
# renovate: datasource=npm depName=bun
bun-version: "1.3.14"
- name: 📦 Install dependencies (bun) - name: 📦 Install dependencies (bun)
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
@@ -83,26 +103,24 @@ jobs:
- "i18n:check" - "i18n:check"
steps: steps:
- parallel: - name: "📥 Checkout PR code"
- name: "📥 Checkout PR code" uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- name: "🍞 Setup Bun"
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
# renovate: datasource=npm depName=bun
bun-version: "1.3.14"
- name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with: with:
path: ~/.bun/install/cache ref: ${{ github.event.pull_request.head.sha || github.sha }}
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }} submodules: recursive
restore-keys: | fetch-depth: 0
${{ runner.os }}-${{ runner.arch }}-bun-
- name: "🟢 Setup Node.js"
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
# renovate: datasource=node-version depName=node versioning=node
node-version: "24.18.0"
- name: "🍞 Setup Bun"
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
# renovate: datasource=npm depName=bun
bun-version: "1.3.14"
- name: "📦 Install dependencies" - name: "📦 Install dependencies"
run: bun install --frozen-lockfile run: bun install --frozen-lockfile

View File

@@ -1,76 +0,0 @@
name: 📝 PR Title Comment
# Posts / updates / deletes the PR-title lint sticky comment with a write token,
# so it works on FORK PRs too (the pull_request-triggered pr-title.yml only has a
# read-only token on forks). workflow_run runs from the base branch with the base
# repo's token. This is comment-only — the required check stays pr-title.yml.
on:
workflow_run:
workflows: ["📝 PR Title"]
types: [completed]
permissions:
contents: read
actions: read # download the artifact from the triggering run
pull-requests: write # post/update/delete the sticky comment
concurrency:
group: pr-title-comment-${{ github.event.workflow_run.id }}
cancel-in-progress: true
jobs:
comment:
if: github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-26.04
steps:
- name: ⬇️ Download lint result
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh run download "${{ github.event.workflow_run.id }}" \
--repo "${{ github.repository }}" \
--name pr-title-result \
--dir result
- name: 🔎 Read result
id: result
run: |
echo "number=$(cat result/number)" >> "$GITHUB_OUTPUT"
if [ -s result/error ]; then
echo "has_error=true" >> "$GITHUB_OUTPUT"
else
echo "has_error=false" >> "$GITHUB_OUTPUT"
fi
- name: 📥 Load error message
if: steps.result.outputs.has_error == 'true'
run: |
{
echo "LINT_ERROR<<PR_TITLE_ERR_EOF"
cat result/error
echo
echo "PR_TITLE_ERR_EOF"
} >> "$GITHUB_ENV"
- name: 💬 Post title error
if: steps.result.outputs.has_error == 'true'
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
with:
number: ${{ steps.result.outputs.number }}
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋🏼
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/).
**Error details:**
```
${{ env.LINT_ERROR }}
```
- name: 🧹 Clear title error
if: steps.result.outputs.has_error == 'false'
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
with:
number: ${{ steps.result.outputs.number }}
header: pr-title-lint-error
delete: true

View File

@@ -1,42 +0,0 @@
name: 📝 PR Title
on:
pull_request:
types: [opened, edited, synchronize, reopened]
branches: [develop, master]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
validate_pr_title:
name: "📝 Validate PR Title"
runs-on: ubuntu-26.04
steps:
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Fork PRs only get a read-only GITHUB_TOKEN here, so the sticky comment
# is posted from the privileged pr-title-comment.yml (workflow_run). Hand
# off the result (PR number + lint error) as an artifact for it to read.
- if: always()
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
LINT_ERROR: ${{ steps.lint_pr_title.outputs.error_message }}
run: |
mkdir -p pr-title-result
printf '%s' "$PR_NUMBER" > pr-title-result/number
printf '%s' "$LINT_ERROR" > pr-title-result/error
- if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pr-title-result
path: pr-title-result/
retention-days: 1

View File

@@ -13,8 +13,6 @@ name: 🚀 Release (EAS build + submit)
concurrency: concurrency:
group: release-${{ github.ref }} group: release-${{ github.ref }}
cancel-in-progress: false cancel-in-progress: false
# Queue successive releases in order instead of dropping the extra pending run.
queue: max
on: on:
push: push:
@@ -65,26 +63,18 @@ jobs:
artifact_name: streamyfin-android-tv-apk artifact_name: streamyfin-android-tv-apk
steps: steps:
- parallel: - name: 📥 Checkout code
- name: 📥 Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: fetch-depth: 0
fetch-depth: 0 submodules: recursive
submodules: recursive show-progress: false
show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
eas-cache: true
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
@@ -99,6 +89,13 @@ jobs:
bun install --frozen-lockfile bun install --frozen-lockfile
bun run submodule-reload bun run submodule-reload
- name: 🏗️ Setup EAS
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
eas-cache: true
# tvOS uses credentialsSource: local — restore the gitignored # tvOS uses credentialsSource: local — restore the gitignored
# credentials.json + cert + provisioning profiles from secrets. # credentials.json + cert + provisioning profiles from secrets.
- name: 🔐 Restore tvOS signing credentials - name: 🔐 Restore tvOS signing credentials

View File

@@ -25,20 +25,19 @@ jobs:
contents: write contents: write
pull-requests: write pull-requests: write
steps: steps:
- parallel: - name: 📥 Checkout repository
- name: 📥 Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with:
with: # On `release` events GITHUB_SHA is the tagged commit — without this the
# On `release` events GITHUB_SHA is the tagged commit — without this the # script would regenerate the form from the tag's (stale) copy and the bot
# script would regenerate the form from the tag's (stale) copy and the bot # PR would revert any form edits made on develop since that release.
# PR would revert any form edits made on develop since that release. ref: develop
ref: develop
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
# renovate: datasource=npm depName=bun # renovate: datasource=npm depName=bun
bun-version: "1.3.14" bun-version: "1.3.14"
- name: 🔢 Populate version dropdown from GitHub releases - name: 🔢 Populate version dropdown from GitHub releases
id: populate id: populate

View File

@@ -109,7 +109,7 @@
"@types/react": "~19.2.10", "@types/react": "~19.2.10",
"@types/react-test-renderer": "19.1.0", "@types/react-test-renderer": "19.1.0",
"cross-env": "10.1.0", "cross-env": "10.1.0",
"expo-doctor": "1.20.0", "expo-doctor": "1.19.9",
"husky": "9.1.7", "husky": "9.1.7",
"lint-staged": "17.0.8", "lint-staged": "17.0.8",
"react-test-renderer": "19.2.3", "react-test-renderer": "19.2.3",
@@ -1015,7 +1015,7 @@
"expo-device": ["expo-device@56.0.4", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-ucVcGPkvBrl2QHuy7XcYex2Y6BETvJ6TREutZrwLGUDnlvbpKS8KfQoNZOpvkyo5Nmm9RrasYQ0CrXmBHho2mg=="], "expo-device": ["expo-device@56.0.4", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-ucVcGPkvBrl2QHuy7XcYex2Y6BETvJ6TREutZrwLGUDnlvbpKS8KfQoNZOpvkyo5Nmm9RrasYQ0CrXmBHho2mg=="],
"expo-doctor": ["expo-doctor@1.20.0", "", { "bin": { "expo-doctor": "bin/expo-doctor.js" } }, "sha512-YL0uGQGZ65tWOCOiDH6BFzBkx/9N46MyBUdRfa4t6iuG4tbLg/phRrhmuaNm99QJVMqYtt3ksRbFCzuYOuX20Q=="], "expo-doctor": ["expo-doctor@1.19.9", "", { "bin": { "expo-doctor": "bin/expo-doctor.js" } }, "sha512-SJW5HxEDQ9f5QdFvrUwfbdJZ4HI0EAAxsrJqrHBFjKBum+uSOcEIZPLRibwNQLTHOwTO1TWNLiMlF9sDUBWeYw=="],
"expo-file-system": ["expo-file-system@56.0.8", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-NrH41/8snGIBSbYicwVLB4txPdgCATd7ZYhMAGS3YJZ9GbnduhlAoV4/YCbGayjrbpE9bJb/6wegPL/zmvRMnQ=="], "expo-file-system": ["expo-file-system@56.0.8", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-NrH41/8snGIBSbYicwVLB4txPdgCATd7ZYhMAGS3YJZ9GbnduhlAoV4/YCbGayjrbpE9bJb/6wegPL/zmvRMnQ=="],

View File

@@ -1,43 +1,116 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import type React from "react"; import React from "react";
import { Platform, View } from "react-native"; import { Platform, View, type ViewStyle } from "react-native";
import { Text } from "@/components/common/Text";
import { scaleSize } from "@/utils/scaleSize";
export const WatchedIndicator: React.FC<{ item: BaseItemDto }> = ({ item }) => { const isAggregateType = (item: BaseItemDto) =>
if (Platform.isTV) { item.Type === "Series" || item.Type === "BoxSet";
// TV: Show white checkmark when watched
if ( // TV sizes are scaled relative to a 1920×1080 reference (see scaleSize).
item.UserData?.Played && const tvBadgeBase: ViewStyle = {
(item.Type === "Movie" || item.Type === "Episode") position: "absolute",
) { top: scaleSize(8),
right: scaleSize(8),
height: scaleSize(28),
borderRadius: scaleSize(14),
backgroundColor: "rgba(255,255,255,0.92)",
alignItems: "center",
justifyContent: "center",
};
// Mobile uses raw dp — no scaling.
const mobileBadgeBase: ViewStyle = {
position: "absolute",
top: 4,
right: 4,
height: 20,
borderRadius: 10,
backgroundColor: "#9333ea",
alignItems: "center",
justifyContent: "center",
};
/**
* Renders the unplayed-episode count badge for Series/BoxSet items that still
* have episodes left to watch. Returns null for non-aggregate types, fully
* watched items, or items with no unplayed count, so it is safe to mount
* unconditionally as an overlay (e.g. on top of the tvOS glass poster, where
* the watched checkmark is drawn natively and only the count needs RN).
*/
export const UnplayedCountBadge: React.FC<{ item: BaseItemDto }> = React.memo(
({ item }) => {
if (!isAggregateType(item)) return null;
if (item.UserData?.Played) return null;
const unplayed = item.UserData?.UnplayedItemCount ?? 0;
if (unplayed <= 0) return null;
if (Platform.isTV) {
return ( return (
<View <View
style={{ style={[
position: "absolute", tvBadgeBase,
top: 8, { minWidth: scaleSize(28), paddingHorizontal: scaleSize(7) },
right: 8, ]}
backgroundColor: "rgba(255,255,255,0.9)",
borderRadius: 14,
width: 28,
height: 28,
alignItems: "center",
justifyContent: "center",
}}
> >
<Ionicons name='checkmark' size={18} color='black' /> <Text
style={{
fontSize: scaleSize(15),
fontWeight: "700",
color: "black",
}}
>
{unplayed}
</Text>
</View> </View>
); );
} }
return null;
return (
<View style={[mobileBadgeBase, { minWidth: 20, paddingHorizontal: 5 }]}>
<Text style={{ fontSize: 12, fontWeight: "700", color: "white" }}>
{unplayed}
</Text>
</View>
);
},
);
export const WatchedIndicator: React.FC<{ item: BaseItemDto }> = ({ item }) => {
const isMovieOrEpisode = item.Type === "Movie" || item.Type === "Episode";
const isAggregate = isAggregateType(item);
const isPlayed = item.UserData?.Played === true;
if (Platform.isTV) {
// Fully watched → white checkmark badge (top-right)
if (isPlayed && (isMovieOrEpisode || isAggregate)) {
return (
<View style={[tvBadgeBase, { width: scaleSize(28) }]}>
<Ionicons name='checkmark' size={scaleSize(18)} color='black' />
</View>
);
}
// Series/BoxSet with remaining episodes → count badge
return <UnplayedCountBadge item={item} />;
} }
// Mobile: Show purple triangle for unwatched // Mobile: purple corner ribbon for unwatched Movie/Episode (existing behavior)
return ( return (
<> <>
{item.UserData?.Played === false && {isMovieOrEpisode && !isPlayed && (
(item.Type === "Movie" || item.Type === "Episode") && ( <View className='bg-purple-600 w-8 h-8 absolute -top-4 -right-4 rotate-45' />
<View className='bg-purple-600 w-8 h-8 absolute -top-4 -right-4 rotate-45' /> )}
)}
{/* Fully watched Series/BoxSet → small purple checkmark */}
{isAggregate && isPlayed && (
<View style={[mobileBadgeBase, { width: 20 }]}>
<Ionicons name='checkmark' size={13} color='white' />
</View>
)}
{/* Series/BoxSet with remaining episodes → count badge */}
<UnplayedCountBadge item={item} />
</> </>
); );
}; };

View File

@@ -1,8 +1,8 @@
import { type BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { type BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useState } from "react"; import { useState } from "react";
import { View, type ViewProps } from "react-native"; import { View, type ViewProps } from "react-native";
import { WatchedIndicator } from "@/components/WatchedIndicator";
import { ItemImage } from "../common/ItemImage"; import { ItemImage } from "../common/ItemImage";
import { WatchedIndicator } from "../WatchedIndicator";
interface Props extends ViewProps { interface Props extends ViewProps {
item: BaseItemDto; item: BaseItemDto;

View File

@@ -3,6 +3,7 @@ import { Image } from "expo-image";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useMemo } from "react"; import { useMemo } from "react";
import { View } from "react-native"; import { View } from "react-native";
import { WatchedIndicator } from "@/components/WatchedIndicator";
import { apiAtom } from "@/providers/JellyfinProvider"; import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl"; import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
@@ -52,6 +53,7 @@ const SeriesPoster: React.FC<MoviePosterProps> = ({ item }) => {
width: "100%", width: "100%",
}} }}
/> />
<WatchedIndicator item={item} />
</View> </View>
); );
}; };

View File

@@ -12,7 +12,10 @@ import {
} from "react-native"; } from "react-native";
import { ProgressBar } from "@/components/common/ProgressBar"; import { ProgressBar } from "@/components/common/ProgressBar";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { WatchedIndicator } from "@/components/WatchedIndicator"; import {
UnplayedCountBadge,
WatchedIndicator,
} from "@/components/WatchedIndicator";
import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { import {
@@ -427,6 +430,12 @@ export const TVPosterCard: React.FC<TVPosterCardProps> = ({
/> />
{PlayButtonOverlay} {PlayButtonOverlay}
{NowPlayingBadge} {NowPlayingBadge}
{/*
The glass view draws the watched checkmark natively but cannot show
an unplayed-episode count, so render it as an RN overlay on top.
Returns null when not applicable (non-series / fully watched).
*/}
<UnplayedCountBadge item={item} />
</View> </View>
); );
} }

View File

@@ -132,7 +132,7 @@
"@types/react": "~19.2.10", "@types/react": "~19.2.10",
"@types/react-test-renderer": "19.1.0", "@types/react-test-renderer": "19.1.0",
"cross-env": "10.1.0", "cross-env": "10.1.0",
"expo-doctor": "1.20.0", "expo-doctor": "1.19.9",
"husky": "9.1.7", "husky": "9.1.7",
"lint-staged": "17.0.8", "lint-staged": "17.0.8",
"react-test-renderer": "19.2.3", "react-test-renderer": "19.2.3",