chore: standardize development environment and cleanup config files

Removes IDE-specific configuration files and establishes Bun as the primary package manager.

Updates project documentation to emphasize Bun usage throughout the development workflow and enhances VS Code settings for better TypeScript performance.

Optimizes EAS build configuration with caching strategies and resource allocation improvements.

Cleans up unused imports and improves TypeScript configuration for better development experience.
This commit is contained in:
Uruk
2025-09-22 02:45:10 +02:00
parent 388342147e
commit 5e9755ea3c
10 changed files with 333 additions and 99 deletions

View File

@@ -1,14 +0,0 @@
{
"permissions": {
"allow": [
"Bash(find:*)",
"Bash(bun install:*)",
"Bash(bunx expo prebuild:*)",
"Bash(bunx expo run:*)",
"Bash(npx expo prebuild:*)",
"Bash(npx expo run:*)",
"Bash(xcodebuild:*)"
],
"deny": []
}
}

View File

@@ -1,7 +0,0 @@
---
description: Don't write code directly in the ios folder.
globs:
alwaysApply: true
---
We never write code directly in the ios folder. This code is generated by expo plugins.

View File

@@ -3,58 +3,93 @@
## Project Overview ## Project Overview
Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native). Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native).
It supports mobile (iOS/Android) and TV platforms, and integrates with Jellyfin and Jellyseerr APIs. It supports mobile (iOS/Android) and TV platforms, integrates with Jellyfin and Jellyseerr APIs,
and provides seamless media streaming with offline capabilities and Chromecast support.
## Main Technologies ## Main Technologies
- React Native (Expo) - **Runtime**: Bun (JavaScript/TypeScript execution)
- TypeScript - **Framework**: React Native (Expo)
- React Query - **Language**: TypeScript (strict mode)
- Jotai (state management) - **State Management**: Jotai (global state) + React Query (server state)
- Jellyfin SDK (TypeScript) - **API SDK**: Jellyfin SDK (TypeScript)
- BiomeJS (code formatting/linting) - **Navigation**: Expo Router (file-based routing)
- EAS (Expo Application Services) - **Code Quality**: BiomeJS (formatting/linting)
- Shell scripting (for automation) - **Build Platform**: EAS (Expo Application Services)
- GitHub Actions (CI/CD) - **CI/CD**: GitHub Actions with Bun
## Package Management
**CRITICAL: ALWAYS use `bun` for all package management operations**
- **NEVER use `npm`, `yarn`, `bunx` or `npx` commands**
- Use `bun install` instead of `npm install` or `yarn install`
- Use `bun add <package>` instead of `npm install <package>`
- Use `bun remove <package>` instead of `npm uninstall <package>`
- Use `bun run <script>` instead of `npm run <script>`
- Use `bunx <command>` instead of `npx <command>`
- For Expo: use `bunx create-expo-app` or `bunx @expo/cli`
## Code Structure ## Code Structure
- `app/` Main application code (screens, navigation, etc.) - `app/` Main application code (screens, navigation, etc.)
- `components/` Reusable UI components - `components/` Reusable UI components
- `providers/` Context and API providers (e.g., JellyfinProvider.tsx) - `providers/` Context and API providers (e.g., JellyfinProvider.tsx)
- `utils/` Utility functions and atoms - `utils/` Utility functions and Jotai atoms
- `assets/` Images and static assets - `assets/` Images and static assets
- `scripts/` Automation scripts (Node.js, Bash) - `scripts/` Automation scripts (Node.js, Bash)
- `plugins/` Expo/Metro plugins - `plugins/` Expo/Metro plugins
- `README.md` Project documentation
## Coding Conventions ## Coding Standards
- Use TypeScript for all new code. - Use TypeScript for ALL files (no .js files)
- Prefer functional React components. - Prefer functional React components with hooks
- Use hooks for state and side effects. - Use Jotai atoms for global state management
- Use Jotai for global state. - Use React Query for server state and caching
- Use React Query for data fetching/caching. - Follow BiomeJS formatting and linting rules
- Use BiomeJS for formatting and linting. - Use `const` over `let`, avoid `var` entirely
- Follow the established folder structure for screens/components. - Implement proper error boundaries
- Use React.memo() for performance optimization
- Handle both mobile and TV navigation patterns
## API Usage ## API Integration
- Use the Jellyfin SDK for all server interactions. - Use Jellyfin SDK for all server interactions
- Use the `apiAtom` and `userAtom` from `JellyfinProvider` for authenticated API calls. - Access authenticated APIs via `apiAtom` and `userAtom` from JellyfinProvider
- For navigation, use `expo-router`. - Implement proper loading states and error handling
- Use React Query for caching and background updates
- Handle offline scenarios gracefully
## Performance Optimization
- Leverage Bun's superior runtime performance
- Optimize FlatList components with proper props
- Use lazy loading for non-critical components
- Implement proper image caching strategies
- Monitor bundle size and use tree-shaking effectively
## Testing
- Use Bun's built-in test runner when possible
- Test files: `*.test.ts` or `*.test.tsx`
- Run tests with: `bun test`
- Mock external APIs in tests
- Focus on testing business logic and custom hooks
## Commit Messages ## Commit Messages
- Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) (e.g., `feat:`, `fix:`, `chore:`). Use [Conventional Commits](https://www.conventionalcommits.org/):
- Example: `feat(player): add Chromecast support` Exemple:
- `feat(player): add Chromecast support`
- `fix(auth): handle expired JWT tokens`
- `chore(deps): update Jellyfin SDK`
## Special Instructions ## Special Instructions
- When suggesting code, prefer using existing atoms, hooks, and utility functions. - Prioritize cross-platform compatibility (mobile + TV)
- When adding new features, ensure they are accessible via both mobile and TV navigation if relevant. - Ensure accessibility for TV remote navigation
- When updating dependencies or scripts, check for compatibility with Expo and EAS. - Use existing atoms, hooks, and utilities before creating new ones
- Maintain compatibility with Expo and EAS workflows
--- - Always verify Bun compatibility when suggesting new dependencies
**Copilot: Please use these instructions to provide context-aware suggestions and code completions for this repository.** **Copilot: Please use these instructions to provide context-aware suggestions and code completions for this repository.**

74
.gitignore vendored
View File

@@ -1,27 +1,16 @@
# Dependencies and Package Managers
node_modules/ node_modules/
.expo/ bun.lock
dist/ bun.lockb
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
modules/vlc-player/android/build
# macOS
.DS_Store
expo-env.d.ts
Streamyfin.app
*.mp4
Streamyfin.app
package-lock.json package-lock.json
# Expo and React Native Build Artifacts
.expo/
dist/
web-build/
.tsbuildinfo
# Platform-specific Build Directories
/ios /ios
/android /android
/iostv /iostv
@@ -29,21 +18,48 @@ package-lock.json
/androidmobile /androidmobile
/androidtv /androidtv
# Module-specific Builds
modules/vlc-player/android/build
modules/player/android modules/player/android
modules/hls-downloader/android/build
pc-api-7079014811501811218-719-3b9f15aeccf8.json # Generated Applications
credentials.json Streamyfin.app
*.apk *.apk
*.ipa *.ipa
.continuerc.json *.aab
# Certificates and Keys
*.jks
*.p8
*.p12
*.key
*.mobileprovision
# Debug and Temporary Files
npm-debug.*
*.orig.*
*.mp4
# OS-specific Files
# macOS
.DS_Store
# IDE and Editor Files
.vscode/ .vscode/
.idea/ .idea/
.ruby-lsp .ruby-lsp
modules/hls-downloader/android/build
streamyfin-4fec1-firebase-adminsdk.json # Environment and Configuration
expo-env.d.ts
.continuerc.json
.env .env
.env.local .env.local
*.aab
/version-backup-* # Secrets and Credentials
bun.lockb pc-api-7079014811501811218-719-3b9f15aeccf8.json
credentials.json
streamyfin-4fec1-firebase-adminsdk.json
# Version and Backup Files
/version-backup-*

178
.vscode/settings.json vendored
View File

@@ -1,24 +1,180 @@
{ {
// ==========================================
// FORMATTING & LINTING
// ==========================================
// Biome as default formatter
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.formatOnType": false,
// Language-specific formatters
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "biomejs.biome", "editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true "editor.formatOnSave": true
}, },
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true
},
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome", "editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true "editor.formatOnSave": true
}, },
"prettier.printWidth": 120,
"[swift]": {
"editor.defaultFormatter": "sswg.swift-lang"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true
},
"[javascriptreact]": { "[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome", "editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true "editor.formatOnSave": true
} },
"[json]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true
},
"[swift]": {
"editor.insertSpaces": true,
"editor.tabSize": 2
},
// ==========================================
// TYPESCRIPT & JAVASCRIPT
// ==========================================
// TypeScript performance optimizations
"typescript.preferences.includePackageJsonAutoImports": "auto",
"typescript.suggest.autoImports": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.preferences.preferTypeOnlyAutoImports": true,
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.preferences.includeCompletionsForImportStatements": true,
"typescript.preferences.includeCompletionsWithSnippetText": true,
// JavaScript settings
"javascript.preferences.importModuleSpecifier": "relative",
"javascript.suggest.autoImports": true,
"javascript.updateImportsOnFileMove.enabled": "always",
// ==========================================
// REACT NATIVE & EXPO
// ==========================================
// File associations for React Native
"files.associations": {
"*.expo.ts": "typescript",
"*.expo.tsx": "typescriptreact",
"*.expo.js": "javascript",
"*.expo.jsx": "javascriptreact",
"metro.config.js": "javascript",
"babel.config.js": "javascript",
"app.config.js": "javascript",
"eas.json": "jsonc"
},
// React Native specific settings
"emmet.includeLanguages": {
"typescriptreact": "html",
"javascriptreact": "html"
},
"emmet.triggerExpansionOnTab": true,
// Exclude build directories from search
"search.exclude": {
"**/node_modules": true
},
// ==========================================
// EDITOR PERFORMANCE & UX
// ==========================================
// Performance optimizations
"editor.largeFileOptimizations": true,
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/**": true,
"**/.expo/**": true,
"**/ios/**": true,
"**/android/**": true,
"**/build/**": true,
"**/dist/**": true
},
// Better editor behavior
"editor.suggestSelection": "first",
"editor.quickSuggestions": {
"strings": true,
"comments": true,
"other": true
},
"editor.snippetSuggestions": "top",
"editor.tabCompletion": "on",
"editor.wordBasedSuggestions": "off",
// ==========================================
// TERMINAL & DEVELOPMENT
// ==========================================
// Terminal settings for Bun
"terminal.integrated.shell.windows": "C:\\Windows\\System32\\cmd.exe",
"terminal.integrated.env.windows": {
"PATH": "${env:PATH};./node_modules/.bin"
},
// ==========================================
// WORKSPACE & NAVIGATION
// ==========================================
// Better workspace navigation
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"*.ts": "${capture}.js",
"*.tsx": "${capture}.js",
"*.js": "${capture}.js,${capture}.js.map,${capture}.min.js,${capture}.d.ts",
"*.jsx": "${capture}.js",
"package.json": "package-lock.json,yarn.lock,bun.lock,bun.lockb,.yarnrc,.yarnrc.yml",
"tsconfig.json": "tsconfig.*.json",
".env": ".env.*",
"app.json": "app.config.js,eas.json,expo-env.d.ts",
"README.md": "LICENSE.txt,SECURITY.md,CODE_OF_CONDUCT.md,CONTRIBUTING.md"
},
// Better breadcrumbs and navigation
"breadcrumbs.enabled": true,
"outline.showVariables": true,
"outline.showConstants": true,
// ==========================================
// GIT & VERSION CONTROL
// ==========================================
// Git integration
"git.autofetch": true,
"git.enableSmartCommit": true,
"git.confirmSync": false,
"git.ignoreLimitWarning": true,
// ==========================================
// CODE QUALITY & ERRORS
// ==========================================
// Better error detection
"typescript.validate.enable": true,
"javascript.validate.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
// Problem matcher for better error display
"typescript.tsc.autoDetect": "on",
// ==========================================
// LEGACY SETTINGS (keeping for compatibility)
// ==========================================
"prettier.printWidth": 120
} }

View File

@@ -1,13 +1,12 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api"; import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { router } from "expo-router"; import { router } from "expo-router";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useEffect, useMemo, useRef } from "react"; import { useEffect, useMemo, useRef } from "react";
import { TouchableOpacity, type ViewProps } from "react-native"; import { TouchableOpacity, type ViewProps } from "react-native";
import { useDownload } from "@/providers/DownloadProvider"; import { useDownload } from "@/providers/DownloadProvider";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import ContinueWatchingPoster from "../ContinueWatchingPoster"; import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { import {
HorizontalScroll, HorizontalScroll,

View File

@@ -11,7 +11,6 @@ import {
type SeasonIndexState, type SeasonIndexState,
} from "@/components/series/SeasonDropdown"; } from "@/components/series/SeasonDropdown";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import { runtimeTicksToSeconds } from "@/utils/time"; import { runtimeTicksToSeconds } from "@/utils/time";
import ContinueWatchingPoster from "../ContinueWatchingPoster"; import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { Text } from "../common/Text"; import { Text } from "../common/Text";
@@ -101,7 +100,7 @@ export const SeasonPicker: React.FC<Props> = ({ item }) => {
enabled: !!api && !!user?.Id && !!item.Id && !!selectedSeasonId, enabled: !!api && !!user?.Id && !!item.Id && !!selectedSeasonId,
}); });
const queryClient = useQueryClient(); const _queryClient = useQueryClient();
// Used for height calculation // Used for height calculation
const [nrOfEpisodes, setNrOfEpisodes] = useState(0); const [nrOfEpisodes, setNrOfEpisodes] = useState(0);

View File

@@ -4,6 +4,11 @@
}, },
"build": { "build": {
"development": { "development": {
"resourceClass": "medium",
"cache": {
"key": "development-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"environment": "development", "environment": "development",
"developmentClient": true, "developmentClient": true,
"distribution": "internal", "distribution": "internal",
@@ -15,6 +20,11 @@
} }
}, },
"development_tv": { "development_tv": {
"resourceClass": "medium",
"cache": {
"key": "development-tv-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"environment": "development", "environment": "development",
"developmentClient": true, "developmentClient": true,
"distribution": "internal", "distribution": "internal",
@@ -27,6 +37,11 @@
} }
}, },
"development-simulator": { "development-simulator": {
"resourceClass": "medium",
"cache": {
"key": "development-simulator-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"environment": "development", "environment": "development",
"developmentClient": true, "developmentClient": true,
"distribution": "internal", "distribution": "internal",
@@ -38,19 +53,38 @@
} }
}, },
"preview": { "preview": {
"resourceClass": "large",
"cache": {
"key": "preview-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"distribution": "internal", "distribution": "internal",
"env": { "env": {
"EXPO_PUBLIC_WRITE_DEBUG": "1" "EXPO_PUBLIC_WRITE_DEBUG": "1"
} }
}, },
"production": { "production": {
"resourceClass": "large",
"cache": {
"key": "production-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"environment": "production", "environment": "production",
"channel": "0.36.0", "channel": "0.36.0",
"android": { "android": {
"buildType": "aab",
"image": "latest"
},
"ios": {
"image": "latest" "image": "latest"
} }
}, },
"production-apk": { "production-apk": {
"resourceClass": "large",
"cache": {
"key": "production-apk-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"environment": "production", "environment": "production",
"channel": "0.36.0", "channel": "0.36.0",
"android": { "android": {
@@ -59,6 +93,11 @@
} }
}, },
"production-apk-tv": { "production-apk-tv": {
"resourceClass": "large",
"cache": {
"key": "production-apk-tv-{{ checksum \"bun.lock\" }}-{{ checksum \"package.json\" }}",
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
},
"environment": "production", "environment": "production",
"channel": "0.36.0", "channel": "0.36.0",
"android": { "android": {

View File

@@ -12,8 +12,9 @@ import {
} from "./VlcPlayer.types"; } from "./VlcPlayer.types";
import VlcPlayerView from "./VlcPlayerView"; import VlcPlayerView from "./VlcPlayerView";
export { export { VlcPlayerView };
VlcPlayerView,
export type {
VlcPlayerViewProps, VlcPlayerViewProps,
VlcPlayerViewRef, VlcPlayerViewRef,
PlaybackStatePayload, PlaybackStatePayload,

View File

@@ -6,7 +6,17 @@
"jsxImportSource": "react", "jsxImportSource": "react",
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
} },
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}, },
"include": [ "include": [
"app/**/*", "app/**/*",