mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2025-12-17 19:25:34 -06:00
[skip actions]
run prettier
This commit is contained in:
@@ -10,10 +10,13 @@ module.exports = {
|
|||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
node: true,
|
node: true,
|
||||||
|
jest: true,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'react/react-in-jsx-scope': 'off',
|
'react/react-in-jsx-scope': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
'react/prop-types': 'off',
|
'react/prop-types': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'error', // Disallow usage of any
|
'@typescript-eslint/no-explicit-any': 'error', // Disallow usage of any
|
||||||
'no-mixed-spaces-and-tabs': 'off', // refer https://github.com/prettier/prettier/issues/4199
|
'no-mixed-spaces-and-tabs': 'off', // refer https://github.com/prettier/prettier/issues/4199
|
||||||
|
|||||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
||||||
169
README.md
169
README.md
@@ -1,56 +1,65 @@
|
|||||||

|

|
||||||
|
|
||||||
# 🪼 Jellify
|
# 🪼 Jellify
|
||||||
|
|
||||||
[](https://github.com/anultravioletaurora/Jellify/actions/workflows/publish-beta.yml)
|
[](https://github.com/anultravioletaurora/Jellify/actions/workflows/publish-beta.yml)
|
||||||
|
|
||||||
### 🔗 Quick Links
|
### 🔗 Quick Links
|
||||||
|
|
||||||
[Discord Server](https://discord.gg/yf8fBatktn)
|
[Discord Server](https://discord.gg/yf8fBatktn)
|
||||||
|
|
||||||
[TestFlight](https://testflight.apple.com/join/etVSc7ZQ)
|
[TestFlight](https://testflight.apple.com/join/etVSc7ZQ)
|
||||||
|
|
||||||
### ℹ️ About
|
### ℹ️ About
|
||||||
|
|
||||||
> **jellify** (verb) - *to make gelatinous* <br>
|
> **jellify** (verb) - _to make gelatinous_ <br>
|
||||||
[see also](https://www.merriam-webster.com/dictionary/jellify)
|
> [see also](https://www.merriam-webster.com/dictionary/jellify)
|
||||||
|
|
||||||
*Jellify* is a free and open source music player for [Jellyfin](https://jellyfin.org/). Built with [React Native](https://reactnative.dev/), *Jellify* provides a user experience that feels familar to other popular music apps and a has featureset to match
|
_Jellify_ is a free and open source music player for [Jellyfin](https://jellyfin.org/). Built with [React Native](https://reactnative.dev/), _Jellify_ provides a user experience that feels familar to other popular music apps and a has featureset to match
|
||||||
|
|
||||||
> *Jellify* requires a connection to a [Jellyfin](https://jellyfin.org/) server to work.
|
> _Jellify_ requires a connection to a [Jellyfin](https://jellyfin.org/) server to work.
|
||||||
|
|
||||||
### 🤓 Background
|
### 🤓 Background
|
||||||
I was after a music app for Jellyfin that showcased my music with artwork, had a user interface congruent with what the big guys do, and had the ability to algorithmically curate music (not that you have to use *Jellify* that way). I also wanted to create a music app that could handle my extremely large music libraries (i.e., 100K+ songs) and not get bogged down.
|
|
||||||
|
|
||||||
This app was designed with me and my dad in mind, since I wanted to give him a sleek, one stop shop for live recordings of bands he likes (read: the Grateful Dead). The UI was designed so that he'd find it instantly familiar and useful. CarPlay / Android Auto support was also a must for us, as we both use CarPlay religiously.
|
I was after a music app for Jellyfin that showcased my music with artwork, had a user interface congruent with what the big guys do, and had the ability to algorithmically curate music (not that you have to use _Jellify_ that way). I also wanted to create a music app that could handle my extremely large music libraries (i.e., 100K+ songs) and not get bogged down.
|
||||||
|
|
||||||
**TL;DR** Designed to be lightweight and scalable, *Jellify* caters to those who want a mobile Jellyfin music experience similar to what's provided by the big music streaming services.
|
This app was designed with me and my dad in mind, since I wanted to give him a sleek, one stop shop for live recordings of bands he likes (read: the Grateful Dead). The UI was designed so that he'd find it instantly familiar and useful. CarPlay / Android Auto support was also a must for us, as we both use CarPlay religiously.
|
||||||
|
|
||||||
|
**TL;DR** Designed to be lightweight and scalable, _Jellify_ caters to those who want a mobile Jellyfin music experience similar to what's provided by the big music streaming services.
|
||||||
|
|
||||||
## 💡 Features
|
## 💡 Features
|
||||||
|
|
||||||
### ✨ Current
|
### ✨ Current
|
||||||
- Available via Testflight and Android APK
|
|
||||||
- APKs are associated with each [release](https://github.com/anultravioletaurora/Jellify/releases)
|
- Available via Testflight and Android APK
|
||||||
- Light and Dark modes
|
- APKs are associated with each [release](https://github.com/anultravioletaurora/Jellify/releases)
|
||||||
- Home screen access to previously played tracks, artists, and your playlists
|
- Light and Dark modes
|
||||||
- Quick access to similar artists and items for discovering music in your library
|
- Home screen access to previously played tracks, artists, and your playlists
|
||||||
- Jellyfin playback reporting and [Last.FM Plugin](https://github.com/jesseward/jellyfin-plugin-lastfm) support
|
- Quick access to similar artists and items for discovering music in your library
|
||||||
- Library of Favorited Music, not too dissimilar to how streaming services handle your 'library'
|
- Jellyfin playback reporting and [Last.FM Plugin](https://github.com/jesseward/jellyfin-plugin-lastfm) support
|
||||||
- Full playlist support, including creating, updating, and reordering
|
- Library of Favorited Music, not too dissimilar to how streaming services handle your 'library'
|
||||||
|
- Full playlist support, including creating, updating, and reordering
|
||||||
|
|
||||||
### 🛠 Roadmap
|
### 🛠 Roadmap
|
||||||
- [Offline Playback](https://github.com/anultravioletaurora/Jellify/issues/10)
|
|
||||||
- [CarPlay / Android Auto Support](https://github.com/anultravioletaurora/Jellify/issues/5)
|
- [Offline Playback](https://github.com/anultravioletaurora/Jellify/issues/10)
|
||||||
- [Support for Jellyfin Instant Mixes](https://github.com/anultravioletaurora/Jellify/issues/50)
|
- [CarPlay / Android Auto Support](https://github.com/anultravioletaurora/Jellify/issues/5)
|
||||||
- [Shared, Public, and Collaborative Playlists](https://github.com/anultravioletaurora/Jellify/issues/175)
|
- [Support for Jellyfin Instant Mixes](https://github.com/anultravioletaurora/Jellify/issues/50)
|
||||||
- [Web / Desktop support](https://github.com/anultravioletaurora/Jellify/issues/71)
|
- [Shared, Public, and Collaborative Playlists](https://github.com/anultravioletaurora/Jellify/issues/175)
|
||||||
- [Watch (Apple Watch / WearOS) Support](https://github.com/anultravioletaurora/Jellify/issues/61)
|
- [Web / Desktop support](https://github.com/anultravioletaurora/Jellify/issues/71)
|
||||||
- [TV (Android, Apple, Samsung) Support](https://github.com/anultravioletaurora/Jellify/issues/85)
|
- [Watch (Apple Watch / WearOS) Support](https://github.com/anultravioletaurora/Jellify/issues/61)
|
||||||
|
- [TV (Android, Apple, Samsung) Support](https://github.com/anultravioletaurora/Jellify/issues/85)
|
||||||
|
|
||||||
## 👀 Lemme see!
|
## 👀 Lemme see!
|
||||||
|
|
||||||
### Home
|
### Home
|
||||||
|
|
||||||
Home
|
Home
|
||||||
|
|
||||||
<img src="screenshots/home.png" alt="Jellify Home" width="275" height="600">
|
<img src="screenshots/home.png" alt="Jellify Home" width="275" height="600">
|
||||||
|
|
||||||
### Library
|
### Library
|
||||||
|
|
||||||
Library
|
Library
|
||||||
|
|
||||||
<img src="screenshots/library.png" alt="Library" width="275" height="600">
|
<img src="screenshots/library.png" alt="Library" width="275" height="600">
|
||||||
@@ -76,7 +85,7 @@ Album Artists
|
|||||||
<img src="screenshots/album_artists.png" alt="Album Artists" width="275" height="600">
|
<img src="screenshots/album_artists.png" alt="Album Artists" width="275" height="600">
|
||||||
|
|
||||||
Track Options
|
Track Options
|
||||||
|
|
||||||
<img src="screenshots/track_options.png" alt="Track Options" width="275" height="600">
|
<img src="screenshots/track_options.png" alt="Track Options" width="275" height="600">
|
||||||
|
|
||||||
Playlist
|
Playlist
|
||||||
@@ -84,9 +93,11 @@ Playlist
|
|||||||
<img src="screenshots/playlist.png" alt="Playlist" width="275" height="600">
|
<img src="screenshots/playlist.png" alt="Playlist" width="275" height="600">
|
||||||
|
|
||||||
### Search
|
### Search
|
||||||
|
|
||||||
<img src="screenshots/search.png" alt="Search" width="275" height="600">
|
<img src="screenshots/search.png" alt="Search" width="275" height="600">
|
||||||
|
|
||||||
### Player
|
### Player
|
||||||
|
|
||||||
<img src="screenshots/player.png" alt="Player" width="275" height="600">
|
<img src="screenshots/player.png" alt="Player" width="275" height="600">
|
||||||
|
|
||||||
<img src="screenshots/player_queue.png" alt="Queue" width="275" height="600">
|
<img src="screenshots/player_queue.png" alt="Queue" width="275" height="600">
|
||||||
@@ -94,14 +105,19 @@ Playlist
|
|||||||
<img src='screenshots/favorite_track.png' alt='Favorite Track' width='275' height='600”'>
|
<img src='screenshots/favorite_track.png' alt='Favorite Track' width='275' height='600”'>
|
||||||
|
|
||||||
### CarPlay (Sneak Preview)
|
### CarPlay (Sneak Preview)
|
||||||
|
|
||||||
<img src="screenshots/carplay_nowplaying.jpeg" alt="Now Playing (CarPlay)" width="500" height="350">
|
<img src="screenshots/carplay_nowplaying.jpeg" alt="Now Playing (CarPlay)" width="500" height="350">
|
||||||
|
|
||||||
### On the Server
|
### On the Server
|
||||||
|
|
||||||
<img src="https://github.com/user-attachments/assets/741884a2-b9b7-4081-b3a0-6655d08071dc" alt="Playback Tracking" width="300" height="200">
|
<img src="https://github.com/user-attachments/assets/741884a2-b9b7-4081-b3a0-6655d08071dc" alt="Playback Tracking" width="300" height="200">
|
||||||
|
|
||||||
## 🏗 Built with good stuff
|
## 🏗 Built with good stuff
|
||||||
|
|
||||||
[](https://reactjs.org “Go to React homepage”) [](https://typescriptlang.org “Go to TypeScript homepage”)
|
[](https://reactjs.org “Go to React homepage”) [](https://typescriptlang.org “Go to TypeScript homepage”)
|
||||||
|
|
||||||
### 🎨 Frontend
|
### 🎨 Frontend
|
||||||
|
|
||||||
[Tamagui](https://tamagui.dev/)\
|
[Tamagui](https://tamagui.dev/)\
|
||||||
[Burnt](https://github.com/nandorojo/burnt)\
|
[Burnt](https://github.com/nandorojo/burnt)\
|
||||||
[React Navigation](https://reactnavigation.org/)\
|
[React Navigation](https://reactnavigation.org/)\
|
||||||
@@ -109,9 +125,11 @@ Playlist
|
|||||||
[React Native Draggable Flatlist](https://github.com/computerjazz/react-native-draggable-flatlist)\
|
[React Native Draggable Flatlist](https://github.com/computerjazz/react-native-draggable-flatlist)\
|
||||||
[React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)\
|
[React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)\
|
||||||
[React Native Vector Icons](https://github.com/oblador/react-native-vector-icons)
|
[React Native Vector Icons](https://github.com/oblador/react-native-vector-icons)
|
||||||
- Specifically using [Material Community Icons](https://oblador.github.io/react-native-vector-icons/#MaterialCommunityIcons)
|
|
||||||
|
- Specifically using [Material Community Icons](https://oblador.github.io/react-native-vector-icons/#MaterialCommunityIcons)
|
||||||
|
|
||||||
### 🎛️ Backend
|
### 🎛️ Backend
|
||||||
|
|
||||||
[Expo SDK](https://expo.dev/)\
|
[Expo SDK](https://expo.dev/)\
|
||||||
[Jellyfin SDK](https://typescript-sdk.jellyfin.org/)\
|
[Jellyfin SDK](https://typescript-sdk.jellyfin.org/)\
|
||||||
[Tanstack Query](https://tanstack.com/query/latest/docs/framework/react/react-native)\
|
[Tanstack Query](https://tanstack.com/query/latest/docs/framework/react/react-native)\
|
||||||
@@ -122,78 +140,95 @@ Playlist
|
|||||||
[React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill)
|
[React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill)
|
||||||
|
|
||||||
### 👩💻 Monitoring
|
### 👩💻 Monitoring
|
||||||
|
|
||||||
[GlitchTip](https://glitchtip.com/)
|
[GlitchTip](https://glitchtip.com/)
|
||||||
|
|
||||||
### 💜 Love from Wisconsin 🧀
|
### 💜 Love from Wisconsin 🧀
|
||||||
|
|
||||||
This is undoubtedly a passion project of [mine](https://github.com/anultravioletaurora), and I've learned a lot from working on it (and the many failed attempts before it). I hope you enjoy using it! Feature requests and bug reports are welcome :)
|
This is undoubtedly a passion project of [mine](https://github.com/anultravioletaurora), and I've learned a lot from working on it (and the many failed attempts before it). I hope you enjoy using it! Feature requests and bug reports are welcome :)
|
||||||
|
|
||||||
## 🏃♀️Running Locally
|
## 🏃♀️Running Locally
|
||||||
|
|
||||||
### ⚛️ Universal Dependencies
|
### ⚛️ Universal Dependencies
|
||||||
- [Ruby](https://www.ruby-lang.org/en/documentation/installation/) for Fastlane
|
|
||||||
- [NodeJS v22](https://nodejs.org/en/download) for React Native
|
- [Ruby](https://www.ruby-lang.org/en/documentation/installation/) for Fastlane
|
||||||
|
- [NodeJS v22](https://nodejs.org/en/download) for React Native
|
||||||
|
|
||||||
### 🍎 iOS
|
### 🍎 iOS
|
||||||
|
|
||||||
#### Dependencies
|
#### Dependencies
|
||||||
- [Xcode](https://developer.apple.com/xcode/) for building
|
|
||||||
|
- [Xcode](https://developer.apple.com/xcode/) for building
|
||||||
|
|
||||||
#### Instructions
|
#### Instructions
|
||||||
|
|
||||||
##### Setup
|
##### Setup
|
||||||
- Clone this repository
|
|
||||||
- Run `npm run init` to initialize the project
|
- Clone this repository
|
||||||
- This will install `npm` packages, install `bundler` and required gems, and installs CocoaPods
|
- Run `npm run init` to initialize the project
|
||||||
- In the `ios` directory, run `fastlane match development --readonly` to fetch the development signing certificates
|
- This will install `npm` packages, install `bundler` and required gems, and installs CocoaPods
|
||||||
- *You will need access to the *Jellify Signing* private repository*
|
- In the `ios` directory, run `fastlane match development --readonly` to fetch the development signing certificates
|
||||||
|
- *You will need access to the *Jellify Signing* private repository*
|
||||||
|
|
||||||
##### Running
|
##### Running
|
||||||
- Run `npm run start` to start the dev server
|
|
||||||
- Open the `Jellify.xcodeworkspace` with Xcode, *not* the `Jellify.xcodeproject`
|
- Run `npm run start` to start the dev server
|
||||||
- Run either on a device or in the simulator
|
- Open the `Jellify.xcodeworkspace` with Xcode, _not_ the `Jellify.xcodeproject`
|
||||||
- *You will need to wait for Xcode to finish it's "Indexing" step*
|
- Run either on a device or in the simulator
|
||||||
|
- _You will need to wait for Xcode to finish it's "Indexing" step_
|
||||||
|
|
||||||
##### Building
|
##### Building
|
||||||
- To create a build, run `npm run fastlane:ios:build` to use fastlane to compile an `.ipa` for you
|
|
||||||
|
- To create a build, run `npm run fastlane:ios:build` to use fastlane to compile an `.ipa` for you
|
||||||
|
|
||||||
### 🤖 Android
|
### 🤖 Android
|
||||||
|
|
||||||
#### Dependencies
|
#### Dependencies
|
||||||
- [Android Studio](https://developer.android.com/studio)
|
|
||||||
- [Java Development Kit](https://www.oracle.com/th/java/technologies/downloads/)
|
- [Android Studio](https://developer.android.com/studio)
|
||||||
|
- [Java Development Kit](https://www.oracle.com/th/java/technologies/downloads/)
|
||||||
|
|
||||||
#### Instructions
|
#### Instructions
|
||||||
|
|
||||||
##### Setup
|
##### Setup
|
||||||
- Clone this repository
|
|
||||||
- Run `npm i` to install `npm` packages
|
- Clone this repository
|
||||||
|
- Run `npm i` to install `npm` packages
|
||||||
|
|
||||||
##### Running
|
##### Running
|
||||||
- Run `npm run start` to start the dev server
|
|
||||||
- Open the `android` folder with Android Studio
|
- Run `npm run start` to start the dev server
|
||||||
- *Android Studio should automatically grab the "Run Configurations" and initialize Gradle*
|
- Open the `android` folder with Android Studio
|
||||||
- Run either on a device or in the simulator
|
- _Android Studio should automatically grab the "Run Configurations" and initialize Gradle_
|
||||||
|
- Run either on a device or in the simulator
|
||||||
|
|
||||||
##### Building
|
##### Building
|
||||||
- To create a build, run `npm run fastlane:android:build` to use fastlane to compile an `.apk` for you
|
|
||||||
|
- To create a build, run `npm run fastlane:android:build` to use fastlane to compile an `.apk` for you
|
||||||
|
|
||||||
#### References
|
#### References
|
||||||
- [Setting up Android SDK](https://developer.android.com/about/versions/14/setup-sdk)
|
|
||||||
- [ANDROID_HOME not being set](https://stackoverflow.com/questions/26356359/error-android-home-is-not-set-and-android-command-not-in-your-path-you-must/54888107#54888107)
|
- [Setting up Android SDK](https://developer.android.com/about/versions/14/setup-sdk)
|
||||||
|
- [ANDROID_HOME not being set](https://stackoverflow.com/questions/26356359/error-android-home-is-not-set-and-android-command-not-in-your-path-you-must/54888107#54888107)
|
||||||
|
|
||||||
## 🙏 Special Thanks To
|
## 🙏 Special Thanks To
|
||||||
- The [Jellyfin Team](https://jellyfin.org/) for making this possible with their software, SDKs, and unequivocal helpfulness.
|
|
||||||
- Extra thanks to [Niels](https://github.com/nielsvanvelzen) and [Bill](https://github.com/thornbill)
|
- The [Jellyfin Team](https://jellyfin.org/) for making this possible with their software, SDKs, and unequivocal helpfulness.
|
||||||
- [James](https://github.com/jmshrv) and all other contributors of [Finamp](https://github.com/jmshrv/finamp). *Jellify* draws inspiration and wisdom from it, and is another fantastic music app for Jellyfin.
|
- Extra thanks to [Niels](https://github.com/nielsvanvelzen) and [Bill](https://github.com/thornbill)
|
||||||
- James’ [API Blog Post](https://jmshrv.com/posts/jellyfin-api/) proved to be exceptionally valuable during development
|
- [James](https://github.com/jmshrv) and all other contributors of [Finamp](https://github.com/jmshrv/finamp). _Jellify_ draws inspiration and wisdom from it, and is another fantastic music app for Jellyfin.
|
||||||
- The folks in the [Margelo Community Discord](https://discord.com/invite/6CSHz2qAvA) for their assistance
|
- James’ [API Blog Post](https://jmshrv.com/posts/jellyfin-api/) proved to be exceptionally valuable during development
|
||||||
- Extra thanks to [Ritesh](https://github.com/riteshshukla04) for your help, knowledge, and guidance
|
- The folks in the [Margelo Community Discord](https://discord.com/invite/6CSHz2qAvA) for their assistance
|
||||||
- [Nicolas Charpentier](https://github.com/charpeni) for his [React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill) module and for his assistance with getting Jest working
|
- Extra thanks to [Ritesh](https://github.com/riteshshukla04) for your help, knowledge, and guidance
|
||||||
- My fellow [contributors](https://github.com/anultravioletaurora/Jellify/graphs/contributors) who have poured so much heart and a lot of sweat into making *Jellify* a great experience
|
- [Nicolas Charpentier](https://github.com/charpeni) for his [React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill) module and for his assistance with getting Jest working
|
||||||
- Extra thanks to [John Grant](https://github.com/johngrantdev) for shaping and designing the user experience in many places
|
- My fellow [contributors](https://github.com/anultravioletaurora/Jellify/graphs/contributors) who have poured so much heart and a lot of sweat into making _Jellify_ a great experience
|
||||||
- The friends I made along the way that have been critical in fostering an amazing community around *Jellify*
|
- Extra thanks to [John Grant](https://github.com/johngrantdev) for shaping and designing the user experience in many places
|
||||||
- [Thalia](https://github.com/PercyGabriel1129)
|
- The friends I made along the way that have been critical in fostering an amazing community around _Jellify_
|
||||||
- [BotBlake](https://github.com/BotBlake)
|
- [Thalia](https://github.com/PercyGabriel1129)
|
||||||
- My long time friends that have heard me talk about *Jellify* for literally **eons**. Thank you for testing *Jellify* during it's infancy and for supporting me all the way back at the beginning of this project
|
- [BotBlake](https://github.com/BotBlake)
|
||||||
- Tony (iOS, Android)
|
- My long time friends that have heard me talk about _Jellify_ for literally **eons**. Thank you for testing _Jellify_ during it's infancy and for supporting me all the way back at the beginning of this project
|
||||||
- Trevor (Android)
|
- Tony (iOS, Android)
|
||||||
- [Laine](https://github.com/lainie-ftw) (Android)
|
- Trevor (Android)
|
||||||
- [Jordan](https://github.com/jordanbleu) (iOS)
|
- [Laine](https://github.com/lainie-ftw) (Android)
|
||||||
- My best(est) friend [Alyssa](https://www.instagram.com/uhh.lyssarae?igsh=MTRmczExempnbjBwZw==), for your design knowledge and for making various artwork for *Jellify*.
|
- [Jordan](https://github.com/jordanbleu) (iOS)
|
||||||
- You’ve been instrumental in shaping it’s user experience, my rock during development, and an overall inspiration in my life
|
- My best(est) friend [Alyssa](https://www.instagram.com/uhh.lyssarae?igsh=MTRmczExempnbjBwZw==), for your design knowledge and for making various artwork for _Jellify_.
|
||||||
|
- You’ve been instrumental in shaping it’s user experience, my rock during development, and an overall inspiration in my life
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
fastlane documentation
|
## fastlane documentation
|
||||||
----
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
@@ -29,8 +28,6 @@ Runs all the tests
|
|||||||
[bundle exec] fastlane android build
|
[bundle exec] fastlane android build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### android deploy
|
### android deploy
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -39,7 +36,7 @@ Runs all the tests
|
|||||||
|
|
||||||
Deploy a new version to the Google Play
|
Deploy a new version to the Google Play
|
||||||
|
|
||||||
----
|
---
|
||||||
|
|
||||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
{
|
{
|
||||||
"migIndex": 1,
|
"migIndex": 1,
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Black.otf",
|
"path": "assets/fonts/Aileron-Black.otf",
|
||||||
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
||||||
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Bold.otf",
|
"path": "assets/fonts/Aileron-Bold.otf",
|
||||||
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
||||||
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Heavy.otf",
|
"path": "assets/fonts/Aileron-Heavy.otf",
|
||||||
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
||||||
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Italic.otf",
|
"path": "assets/fonts/Aileron-Italic.otf",
|
||||||
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Light.otf",
|
"path": "assets/fonts/Aileron-Light.otf",
|
||||||
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-LightItalic.otf",
|
"path": "assets/fonts/Aileron-LightItalic.otf",
|
||||||
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Regular.otf",
|
"path": "assets/fonts/Aileron-Regular.otf",
|
||||||
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-SemiBold.otf",
|
"path": "assets/fonts/Aileron-SemiBold.otf",
|
||||||
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
||||||
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Thin.otf",
|
"path": "assets/fonts/Aileron-Thin.otf",
|
||||||
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
||||||
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-UltraLight.otf",
|
"path": "assets/fonts/Aileron-UltraLight.otf",
|
||||||
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
||||||
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
4
app.json
4
app.json
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name": "Jellify",
|
"name": "Jellify",
|
||||||
"displayName": "Jellify"
|
"displayName": "Jellify"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:@react-native/babel-preset'],
|
presets: ['module:@react-native/babel-preset'],
|
||||||
plugins: [
|
plugins: [
|
||||||
'react-native-boost/plugin',
|
'react-native-boost/plugin',
|
||||||
// react-native-reanimated/plugin has to be listed last
|
// react-native-reanimated/plugin has to be listed last
|
||||||
'react-native-reanimated/plugin',
|
'react-native-reanimated/plugin',
|
||||||
]
|
],
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ export default function BlurhashedImage({
|
|||||||
<View
|
<View
|
||||||
minHeight={height ?? width}
|
minHeight={height ?? width}
|
||||||
minWidth={width}
|
minWidth={width}
|
||||||
borderRadius={borderRadius ? borderRadius : 25}>
|
borderRadius={borderRadius ? borderRadius : 25}
|
||||||
|
>
|
||||||
{isSuccess ? (
|
{isSuccess ? (
|
||||||
<Image
|
<Image
|
||||||
source={{
|
source={{
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export default function Login(): React.JSX.Element {
|
|||||||
? 'ServerAuthentication'
|
? 'ServerAuthentication'
|
||||||
: 'LibrarySelection'
|
: 'LibrarySelection'
|
||||||
}
|
}
|
||||||
screenOptions={{ headerShown: false }}>
|
screenOptions={{ headerShown: false }}
|
||||||
|
>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='ServerAddress'
|
name='ServerAddress'
|
||||||
options={{
|
options={{
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
|||||||
type='single'
|
type='single'
|
||||||
disableDeactivation={true}
|
disableDeactivation={true}
|
||||||
value={libraryId}
|
value={libraryId}
|
||||||
onValueChange={setLibraryId}>
|
onValueChange={setLibraryId}
|
||||||
|
>
|
||||||
{libraries!
|
{libraries!
|
||||||
.filter((library) => library.CollectionType === 'music')
|
.filter((library) => library.CollectionType === 'music')
|
||||||
.map((library) => {
|
.map((library) => {
|
||||||
@@ -63,7 +64,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
|||||||
libraryId == library.Id!
|
libraryId == library.Id!
|
||||||
? getToken('$color.purpleGray')
|
? getToken('$color.purpleGray')
|
||||||
: 'unset'
|
: 'unset'
|
||||||
}>
|
}
|
||||||
|
>
|
||||||
<Text>{library.Name ?? 'Unnamed Library'}</Text>
|
<Text>{library.Name ?? 'Unnamed Library'}</Text>
|
||||||
</ToggleGroup.Item>
|
</ToggleGroup.Item>
|
||||||
)
|
)
|
||||||
@@ -88,7 +90,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
|||||||
playlistLibraryPrimaryImageId: playlistLibrary?.ImageTags!.Primary,
|
playlistLibraryPrimaryImageId: playlistLibrary?.ImageTags!.Primary,
|
||||||
})
|
})
|
||||||
setLoggedIn(true)
|
setLoggedIn(true)
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{`Let's Go!`}
|
{`Let's Go!`}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -96,7 +99,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
Client.switchUser()
|
Client.switchUser()
|
||||||
setUser(undefined)
|
setUser(undefined)
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Switch User
|
Switch User
|
||||||
</Button>
|
</Button>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ export const JellifyProvider: ({ children }: { children: ReactNode }) => React.J
|
|||||||
loggedIn,
|
loggedIn,
|
||||||
setLoggedIn,
|
setLoggedIn,
|
||||||
carPlayConnected,
|
carPlayConnected,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</JellifyContext.Provider>
|
</JellifyContext.Provider>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
import { fonts } from "@tamagui/config/v4";
|
import { fonts } from '@tamagui/config/v4'
|
||||||
import { createFont } from "tamagui";
|
import { createFont } from 'tamagui'
|
||||||
|
|
||||||
const aileronFace = {
|
const aileronFace = {
|
||||||
100: { normal: 'Aileron-UltraLight', italic: 'Aileron UltraLight Italic' },
|
100: { normal: 'Aileron-UltraLight', italic: 'Aileron UltraLight Italic' },
|
||||||
200: { normal: 'Aileron-Thin', italic: 'Aileron Thin Italic' },
|
200: { normal: 'Aileron-Thin', italic: 'Aileron Thin Italic' },
|
||||||
300: { normal: 'Aileron-Light', italic: 'Aileron Light Italic' },
|
300: { normal: 'Aileron-Light', italic: 'Aileron Light Italic' },
|
||||||
400: { normal: 'Aileron-Regular', italic: 'Aileron Italic'} ,
|
400: { normal: 'Aileron-Regular', italic: 'Aileron Italic' },
|
||||||
500: { normal: 'Aileron-Regular', italic: 'Aileron Italic' },
|
500: { normal: 'Aileron-Regular', italic: 'Aileron Italic' },
|
||||||
600: { normal: 'Aileron-SemiBold', italic: 'Aileron SemiBold Italic' },
|
600: { normal: 'Aileron-SemiBold', italic: 'Aileron SemiBold Italic' },
|
||||||
700: { normal: 'Aileron-Bold', italic: 'Aileron Bold Italic' },
|
700: { normal: 'Aileron-Bold', italic: 'Aileron Bold Italic' },
|
||||||
800: { normal: 'Aileron-Heavy', italic: 'Aileron Heavy Italic' },
|
800: { normal: 'Aileron-Heavy', italic: 'Aileron Heavy Italic' },
|
||||||
900: { normal: 'Aileron-Black', italic: 'Aileron-BlackItalic' }
|
900: { normal: 'Aileron-Black', italic: 'Aileron-BlackItalic' },
|
||||||
};
|
}
|
||||||
|
|
||||||
export const bodyFont = createFont({
|
export const bodyFont = createFont({
|
||||||
family: "Aileron-Bold",
|
family: 'Aileron-Bold',
|
||||||
size: fonts.body.size,
|
size: fonts.body.size,
|
||||||
lineHeight: fonts.body.lineHeight,
|
lineHeight: fonts.body.lineHeight,
|
||||||
weight: fonts.body.weight,
|
weight: fonts.body.weight,
|
||||||
letterSpacing: fonts.body.letterSpacing,
|
letterSpacing: fonts.body.letterSpacing,
|
||||||
face: aileronFace
|
face: aileronFace,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const headingFont = createFont({
|
export const headingFont = createFont({
|
||||||
family: "Aileron-Black",
|
family: 'Aileron-Black',
|
||||||
size: fonts.heading.size,
|
size: fonts.heading.size,
|
||||||
lineHeight: fonts.heading.lineHeight,
|
lineHeight: fonts.heading.lineHeight,
|
||||||
weight: fonts.heading.weight,
|
weight: fonts.heading.weight,
|
||||||
letterSpacing: fonts.heading.letterSpacing,
|
letterSpacing: fonts.heading.letterSpacing,
|
||||||
face: aileronFace
|
face: aileronFace,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
// Don't import react-native-gesture-handler on web
|
// Don't import react-native-gesture-handler on web
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
// Only import react-native-gesture-handler on native platforms
|
// Only import react-native-gesture-handler on native platforms
|
||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler'
|
||||||
|
|||||||
21
index.js
21
index.js
@@ -1,19 +1,20 @@
|
|||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler'
|
||||||
import {AppRegistry} from 'react-native';
|
import { AppRegistry } from 'react-native'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
import {name as appName} from './app.json';
|
import { name as appName } from './app.json'
|
||||||
import { PlaybackService } from './player/service'
|
import { PlaybackService } from './player/service'
|
||||||
import TrackPlayer from 'react-native-track-player';
|
import TrackPlayer from 'react-native-track-player'
|
||||||
import Client from './api/client';
|
import Client from './api/client'
|
||||||
|
|
||||||
// Initialize API client instance
|
// Initialize API client instance
|
||||||
Client.instance;
|
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||||
|
Client.instance
|
||||||
|
|
||||||
// Enable React Navigation freeze for detaching inactive screens
|
// Enable React Navigation freeze for detaching inactive screens
|
||||||
// enableFreeze();
|
// enableFreeze();
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
AppRegistry.registerComponent(appName, () => App)
|
||||||
AppRegistry.registerComponent('RNCarPlayScene', () => App);
|
AppRegistry.registerComponent('RNCarPlayScene', () => App)
|
||||||
|
|
||||||
// Register RNTP playback service for remote controls
|
// Register RNTP playback service for remote controls
|
||||||
TrackPlayer.registerPlaybackService(() => PlaybackService);
|
TrackPlayer.registerPlaybackService(() => PlaybackService)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"info": {
|
||||||
"version" : 1,
|
"version": 1,
|
||||||
"author" : "xcode"
|
"author": "xcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
fastlane documentation
|
## fastlane documentation
|
||||||
----
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do
|
|||||||
|
|
||||||
Push a new beta build to TestFlight
|
Push a new beta build to TestFlight
|
||||||
|
|
||||||
----
|
---
|
||||||
|
|
||||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
{
|
{
|
||||||
"migIndex": 1,
|
"migIndex": 1,
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Black.otf",
|
"path": "assets/fonts/Aileron-Black.otf",
|
||||||
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
||||||
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Bold.otf",
|
"path": "assets/fonts/Aileron-Bold.otf",
|
||||||
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
||||||
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Heavy.otf",
|
"path": "assets/fonts/Aileron-Heavy.otf",
|
||||||
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
||||||
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Italic.otf",
|
"path": "assets/fonts/Aileron-Italic.otf",
|
||||||
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Light.otf",
|
"path": "assets/fonts/Aileron-Light.otf",
|
||||||
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-LightItalic.otf",
|
"path": "assets/fonts/Aileron-LightItalic.otf",
|
||||||
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Regular.otf",
|
"path": "assets/fonts/Aileron-Regular.otf",
|
||||||
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-SemiBold.otf",
|
"path": "assets/fonts/Aileron-SemiBold.otf",
|
||||||
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
||||||
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-Thin.otf",
|
"path": "assets/fonts/Aileron-Thin.otf",
|
||||||
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
||||||
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-UltraLight.otf",
|
"path": "assets/fonts/Aileron-UltraLight.otf",
|
||||||
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
||||||
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// https://docs.swmansion.com/react-native-gesture-handler/docs/guides/testing
|
// https://docs.swmansion.com/react-native-gesture-handler/docs/guides/testing
|
||||||
module.exports = {
|
module.exports = {
|
||||||
preset: 'jest-expo',
|
preset: 'jest-expo',
|
||||||
setupFiles: ["./node_modules/react-native-gesture-handler/jestSetup.js"],
|
setupFiles: ['./node_modules/react-native-gesture-handler/jestSetup.js'],
|
||||||
setupFilesAfterEnv: [
|
setupFilesAfterEnv: [
|
||||||
"./jest/setup.js",
|
'./jest/setup.js',
|
||||||
"./jest/setup-carplay.js",
|
'./jest/setup-carplay.js',
|
||||||
"./jest/setup-blurhash.js",
|
'./jest/setup-blurhash.js',
|
||||||
"./jest/setup-reanimated.js",
|
'./jest/setup-reanimated.js',
|
||||||
"./tamagui.config.ts",
|
'./tamagui.config.ts',
|
||||||
],
|
],
|
||||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(@)?(react-native|react-native-.*|react-navigation|jellyfin|burnt|expo|expo-.*)/)',
|
'node_modules/(?!(@)?(react-native|react-native-.*|react-navigation|jellyfin|burnt|expo|expo-.*)/)',
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'react-native';
|
import 'react-native'
|
||||||
import React from 'react';
|
import React from 'react'
|
||||||
import App from '../App';
|
import App from '../App'
|
||||||
|
|
||||||
// Note: import explicitly to use the types shipped with jest.
|
// Note: import explicitly to use the types shipped with jest.
|
||||||
import {it} from '@jest/globals';
|
import { it } from '@jest/globals'
|
||||||
|
|
||||||
// Note: test renderer must be required after react-native.
|
// Note: test renderer must be required after react-native.
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer'
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
renderer.create(<App />);
|
renderer.create(<App />)
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
jest.mock('react-native-blurhash',() => {
|
jest.mock('react-native-blurhash', () => {
|
||||||
return {
|
return {
|
||||||
Blurhash: ()=> null
|
Blurhash: () => null,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
jest.mock('react-native-carplay', () => {
|
jest.mock('react-native-carplay', () => {
|
||||||
return {
|
return {
|
||||||
default: {
|
default: {
|
||||||
checkForConnection: jest.fn()
|
checkForConnection: jest.fn(),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
jest.mock('react-native-reanimated', () => ({
|
jest.mock('react-native-reanimated', () => ({
|
||||||
...jest.requireActual('react-native-reanimated/mock'),
|
...jest.requireActual('react-native-reanimated/mock'),
|
||||||
createAnimatedPropAdapter: jest.fn,
|
createAnimatedPropAdapter: jest.fn,
|
||||||
useReducedMotion: jest.fn,
|
useReducedMotion: jest.fn,
|
||||||
LayoutAnimationConfig: jest.fn,
|
LayoutAnimationConfig: jest.fn,
|
||||||
}));
|
}))
|
||||||
|
|||||||
197
jest/setup.js
197
jest/setup.js
@@ -1,110 +1,109 @@
|
|||||||
// https://github.com/react-native-device-info/react-native-device-info/issues/1360
|
// https://github.com/react-native-device-info/react-native-device-info/issues/1360
|
||||||
import mockRNDeviceInfo from 'react-native-device-info/jest/react-native-device-info-mock';
|
import mockRNDeviceInfo from 'react-native-device-info/jest/react-native-device-info-mock'
|
||||||
|
|
||||||
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter');
|
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter')
|
||||||
|
|
||||||
jest.mock('react-native-device-info', () => mockRNDeviceInfo);
|
jest.mock('react-native-device-info', () => mockRNDeviceInfo)
|
||||||
|
|
||||||
jest.mock('react-native-haptic-feedback', () => {
|
jest.mock('react-native-haptic-feedback', () => {
|
||||||
return {
|
return {
|
||||||
default: {
|
default: {
|
||||||
trigger: jest.fn()
|
trigger: jest.fn(),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
jest.mock('burnt', () => {
|
jest.mock('burnt', () => {
|
||||||
return {
|
return {
|
||||||
default: {
|
default: {
|
||||||
alert: jest.fn()
|
alert: jest.fn(),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// https://github.com/doublesymmetry/react-native-track-player/issues/501
|
// https://github.com/doublesymmetry/react-native-track-player/issues/501
|
||||||
jest.mock('react-native-track-player', () => {
|
jest.mock('react-native-track-player', () => {
|
||||||
return {
|
return {
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
default: {
|
default: {
|
||||||
addEventListener: () => ({
|
addEventListener: () => ({
|
||||||
remove: jest.fn(),
|
remove: jest.fn(),
|
||||||
}),
|
}),
|
||||||
registerEventHandler: jest.fn(),
|
registerEventHandler: jest.fn(),
|
||||||
registerPlaybackService: jest.fn(),
|
registerPlaybackService: jest.fn(),
|
||||||
setupPlayer: jest.fn().mockResolvedValue(undefined),
|
setupPlayer: jest.fn().mockResolvedValue(undefined),
|
||||||
destroy: jest.fn(),
|
destroy: jest.fn(),
|
||||||
updateOptions: jest.fn(),
|
updateOptions: jest.fn(),
|
||||||
reset: jest.fn(),
|
reset: jest.fn(),
|
||||||
add: jest.fn(),
|
add: jest.fn(),
|
||||||
remove: jest.fn(),
|
remove: jest.fn(),
|
||||||
skip: jest.fn(),
|
skip: jest.fn(),
|
||||||
skipToNext: jest.fn(),
|
skipToNext: jest.fn(),
|
||||||
skipToPrevious: jest.fn(),
|
skipToPrevious: jest.fn(),
|
||||||
removeUpcomingTracks: jest.fn(),
|
removeUpcomingTracks: jest.fn(),
|
||||||
// playback commands
|
// playback commands
|
||||||
play: jest.fn(),
|
play: jest.fn(),
|
||||||
pause: jest.fn(),
|
pause: jest.fn(),
|
||||||
stop: jest.fn(),
|
stop: jest.fn(),
|
||||||
seekTo: jest.fn(),
|
seekTo: jest.fn(),
|
||||||
setVolume: jest.fn(),
|
setVolume: jest.fn(),
|
||||||
setRate: jest.fn(),
|
setRate: jest.fn(),
|
||||||
// player getters
|
// player getters
|
||||||
getQueue: jest.fn(),
|
getQueue: jest.fn(),
|
||||||
getTrack: jest.fn(),
|
getTrack: jest.fn(),
|
||||||
getActiveTrackIndex: jest.fn(),
|
getActiveTrackIndex: jest.fn(),
|
||||||
getCurrentTrack: jest.fn(),
|
getCurrentTrack: jest.fn(),
|
||||||
getVolume: jest.fn(),
|
getVolume: jest.fn(),
|
||||||
getDuration: jest.fn(),
|
getDuration: jest.fn(),
|
||||||
getPosition: jest.fn(),
|
getPosition: jest.fn(),
|
||||||
getBufferedPosition: jest.fn(),
|
getBufferedPosition: jest.fn(),
|
||||||
getState: jest.fn(),
|
getState: jest.fn(),
|
||||||
getRate: jest.fn(),
|
getRate: jest.fn(),
|
||||||
},
|
},
|
||||||
useProgress: () => ({
|
useProgress: () => ({
|
||||||
position: 100,
|
position: 100,
|
||||||
buffered: 150,
|
buffered: 150,
|
||||||
duration: 200,
|
duration: 200,
|
||||||
}),
|
}),
|
||||||
Capability: {
|
Capability: {
|
||||||
Play: 1,
|
Play: 1,
|
||||||
PlayFromId: 2,
|
PlayFromId: 2,
|
||||||
PlayFromSearch: 4,
|
PlayFromSearch: 4,
|
||||||
Pause: 8,
|
Pause: 8,
|
||||||
Stop: 16,
|
Stop: 16,
|
||||||
SeekTo: 32,
|
SeekTo: 32,
|
||||||
Skip: 64,
|
Skip: 64,
|
||||||
SkipToNext: 128,
|
SkipToNext: 128,
|
||||||
SkipToPrevious: 256,
|
SkipToPrevious: 256,
|
||||||
},
|
},
|
||||||
IOSCategoryOptions: {
|
IOSCategoryOptions: {
|
||||||
MixWithOthers: 'mixWithOthers',
|
MixWithOthers: 'mixWithOthers',
|
||||||
DuckOthers: 'duckOthers',
|
DuckOthers: 'duckOthers',
|
||||||
InterruptSpokenAudioAndMixWithOthers:
|
InterruptSpokenAudioAndMixWithOthers: 'interruptSpokenAudioAndMixWithOthers',
|
||||||
'interruptSpokenAudioAndMixWithOthers',
|
AllowBluetooth: 'allowBluetooth',
|
||||||
AllowBluetooth: 'allowBluetooth',
|
AllowBluetoothA2DP: 'allowBluetoothA2DP',
|
||||||
AllowBluetoothA2DP: 'allowBluetoothA2DP',
|
AllowAirPlay: 'allowAirPlay',
|
||||||
AllowAirPlay: 'allowAirPlay',
|
DefaultToSpeaker: 'defaultToSpeaker',
|
||||||
DefaultToSpeaker: 'defaultToSpeaker',
|
},
|
||||||
},
|
IOSCategoryMode: {
|
||||||
IOSCategoryMode: {
|
Default: 'default',
|
||||||
Default: 'default',
|
GameChat: 'gameChat',
|
||||||
GameChat: 'gameChat',
|
Measurement: 'measurement',
|
||||||
Measurement: 'measurement',
|
MoviePlayback: 'moviePlayback',
|
||||||
MoviePlayback: 'moviePlayback',
|
SpokenAudio: 'spokenAudio',
|
||||||
SpokenAudio: 'spokenAudio',
|
VideoChat: 'videoChat',
|
||||||
VideoChat: 'videoChat',
|
VideoRecording: 'videoRecording',
|
||||||
VideoRecording: 'videoRecording',
|
VoiceChat: 'voiceChat',
|
||||||
VoiceChat: 'voiceChat',
|
VoicePrompt: 'voicePrompt',
|
||||||
VoicePrompt: 'voicePrompt',
|
},
|
||||||
},
|
IOSCategory: {
|
||||||
IOSCategory: {
|
Playback: 'playback',
|
||||||
Playback: 'playback',
|
PlaybackAndRecord: 'playbackAndRecord',
|
||||||
PlaybackAndRecord: 'playbackAndRecord',
|
MultiRoute: 'multiRoute',
|
||||||
MultiRoute: 'multiRoute',
|
Ambient: 'ambient',
|
||||||
Ambient: 'ambient',
|
SoloAmbient: 'soloAmbient',
|
||||||
SoloAmbient: 'soloAmbient',
|
Record: 'record',
|
||||||
Record: 'record',
|
PlayAndRecord: 'playAndRecord',
|
||||||
PlayAndRecord: 'playAndRecord',
|
},
|
||||||
},
|
}
|
||||||
}
|
})
|
||||||
});
|
|
||||||
|
|||||||
@@ -2,18 +2,14 @@
|
|||||||
const { getDefaultConfig } = require('@react-native/metro-config')
|
const { getDefaultConfig } = require('@react-native/metro-config')
|
||||||
|
|
||||||
const config = getDefaultConfig(__dirname, {
|
const config = getDefaultConfig(__dirname, {
|
||||||
// [Web-only]: Enables CSS support in Metro.
|
// [Web-only]: Enables CSS support in Metro.
|
||||||
isCSSEnabled: true,
|
isCSSEnabled: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Expo 49 issue: default metro config needs to include "mjs"
|
// Expo 49 issue: default metro config needs to include "mjs"
|
||||||
// https://github.com/expo/expo/issues/23180
|
// https://github.com/expo/expo/issues/23180
|
||||||
config.resolver.sourceExts.push('mjs')
|
config.resolver.sourceExts.push('mjs')
|
||||||
|
|
||||||
config.watchFolders = [
|
config.watchFolders = ['components', 'api', 'player']
|
||||||
"components",
|
|
||||||
"api",
|
|
||||||
"player"
|
|
||||||
]
|
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
|
|
||||||
export class ArtistModel {
|
export class ArtistModel {
|
||||||
|
name?: string | undefined | null
|
||||||
|
|
||||||
name?: string | undefined | null;
|
constructor(itemDto: BaseItemDto) {
|
||||||
|
this.name = itemDto.Name
|
||||||
constructor(itemDto : BaseItemDto) {
|
}
|
||||||
|
}
|
||||||
this.name = itemDto.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
43398
package-lock.json
generated
43398
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
226
package.json
226
package.json
@@ -1,114 +1,114 @@
|
|||||||
{
|
{
|
||||||
"name": "jellify",
|
"name": "jellify",
|
||||||
"version": "0.10.91",
|
"version": "0.10.91",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"init": "npm i",
|
"init": "npm i",
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
"ios": "react-native run-ios",
|
"ios": "react-native run-ios",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"start": "react-native start",
|
"start": "react-native start",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"clean:ios": "cd ios && pod deintegrate",
|
"clean:ios": "cd ios && pod deintegrate",
|
||||||
"clean:android": "cd android && rm -rf app/ build/",
|
"clean:android": "cd android && rm -rf app/ build/",
|
||||||
"pod:install": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=0 bundle exec pod install",
|
"pod:install": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=0 bundle exec pod install",
|
||||||
"pod:install-new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
|
"pod:install-new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
|
||||||
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
|
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
|
||||||
"fastlane:ios:beta": "cd ios && bundle exec fastlane beta",
|
"fastlane:ios:beta": "cd ios && bundle exec fastlane beta",
|
||||||
"fastlane:android:build": "cd android && bundle install && bundle exec fastlane build",
|
"fastlane:android:build": "cd android && bundle install && bundle exec fastlane build",
|
||||||
"androidBuild": "cd android && ./gradlew clean && ./gradlew assembleRelease && cd .. && echo 'find apk in android/app/build/outputs/apk/release'",
|
"androidBuild": "cd android && ./gradlew clean && ./gradlew assembleRelease && cd .. && echo 'find apk in android/app/build/outputs/apk/release'",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jellyfin/sdk": "^0.11.0",
|
"@jellyfin/sdk": "^0.11.0",
|
||||||
"@react-native-community/blur": "^4.4.1",
|
"@react-native-community/blur": "^4.4.1",
|
||||||
"@react-native-community/cli": "^15.1.3",
|
"@react-native-community/cli": "^15.1.3",
|
||||||
"@react-native-masked-view/masked-view": "^0.3.2",
|
"@react-native-masked-view/masked-view": "^0.3.2",
|
||||||
"@react-navigation/bottom-tabs": "^7.2.0",
|
"@react-navigation/bottom-tabs": "^7.2.0",
|
||||||
"@react-navigation/native": "^7.0.14",
|
"@react-navigation/native": "^7.0.14",
|
||||||
"@react-navigation/native-stack": "^7.1.1",
|
"@react-navigation/native-stack": "^7.1.1",
|
||||||
"@react-navigation/stack": "^7.1.0",
|
"@react-navigation/stack": "^7.1.0",
|
||||||
"@tamagui/config": "^1.124.17",
|
"@tamagui/config": "^1.124.17",
|
||||||
"@tamagui/toast": "^1.124.17",
|
"@tamagui/toast": "^1.124.17",
|
||||||
"@tanstack/query-sync-storage-persister": "^5.66.0",
|
"@tanstack/query-sync-storage-persister": "^5.66.0",
|
||||||
"@tanstack/react-query": "^5.66.0",
|
"@tanstack/react-query": "^5.66.0",
|
||||||
"@tanstack/react-query-persist-client": "^5.66.0",
|
"@tanstack/react-query-persist-client": "^5.66.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bundle": "^2.1.0",
|
"bundle": "^2.1.0",
|
||||||
"bundler": "^0.8.0",
|
"bundler": "^0.8.0",
|
||||||
"burnt": "^0.12.2",
|
"burnt": "^0.12.2",
|
||||||
"expo": "^52.0.0",
|
"expo": "^52.0.0",
|
||||||
"expo-image": "^2.0.7",
|
"expo-image": "^2.0.7",
|
||||||
"gem": "^2.4.3",
|
"gem": "^2.4.3",
|
||||||
"invert-color": "^2.0.0",
|
"invert-color": "^2.0.0",
|
||||||
"jest-expo": "^52.0.6",
|
"jest-expo": "^52.0.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"npm-bundle": "^3.0.3",
|
"npm-bundle": "^3.0.3",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-freeze": "^1.0.4",
|
"react-freeze": "^1.0.4",
|
||||||
"react-native": "0.77.0",
|
"react-native": "0.77.0",
|
||||||
"react-native-background-actions": "^4.0.1",
|
"react-native-background-actions": "^4.0.1",
|
||||||
"react-native-blurhash": "^2.1.1",
|
"react-native-blurhash": "^2.1.1",
|
||||||
"react-native-boost": "^0.5.5",
|
"react-native-boost": "^0.5.5",
|
||||||
"react-native-carplay": "^2.4.1-beta.0",
|
"react-native-carplay": "^2.4.1-beta.0",
|
||||||
"react-native-device-info": "^14.0.4",
|
"react-native-device-info": "^14.0.4",
|
||||||
"react-native-draggable-flatlist": "^4.0.1",
|
"react-native-draggable-flatlist": "^4.0.1",
|
||||||
"react-native-file-access": "^3.1.1",
|
"react-native-file-access": "^3.1.1",
|
||||||
"react-native-gesture-handler": "^2.23.0",
|
"react-native-gesture-handler": "^2.23.0",
|
||||||
"react-native-haptic-feedback": "^2.3.3",
|
"react-native-haptic-feedback": "^2.3.3",
|
||||||
"react-native-mmkv": "^2.12.2",
|
"react-native-mmkv": "^2.12.2",
|
||||||
"react-native-reanimated": "^3.17.2",
|
"react-native-reanimated": "^3.17.2",
|
||||||
"react-native-safe-area-context": "^5.2.0",
|
"react-native-safe-area-context": "^5.2.0",
|
||||||
"react-native-screens": "^4.6.0",
|
"react-native-screens": "^4.6.0",
|
||||||
"react-native-swipeable-item": "^2.0.9",
|
"react-native-swipeable-item": "^2.0.9",
|
||||||
"react-native-text-ticker": "^1.14.0",
|
"react-native-text-ticker": "^1.14.0",
|
||||||
"react-native-track-player": "^4.1.1",
|
"react-native-track-player": "^4.1.1",
|
||||||
"react-native-url-polyfill": "^2.0.0",
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"react-native-uuid": "^2.0.3",
|
"react-native-uuid": "^2.0.3",
|
||||||
"react-native-vector-icons": "^10.2.0",
|
"react-native-vector-icons": "^10.2.0",
|
||||||
"ruby": "^0.6.1",
|
"ruby": "^0.6.1",
|
||||||
"tamagui": "^1.124.17"
|
"tamagui": "^1.124.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
"@babel/preset-env": "^7.25.3",
|
"@babel/preset-env": "^7.25.3",
|
||||||
"@babel/runtime": "^7.25.0",
|
"@babel/runtime": "^7.25.0",
|
||||||
"@react-native-community/cli-platform-android": "15.1.3",
|
"@react-native-community/cli-platform-android": "15.1.3",
|
||||||
"@react-native-community/cli-platform-ios": "15.1.3",
|
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||||
"@react-native/babel-preset": "0.77.0",
|
"@react-native/babel-preset": "0.77.0",
|
||||||
"@react-native/eslint-config": "0.77.0",
|
"@react-native/eslint-config": "0.77.0",
|
||||||
"@react-native/metro-config": "0.77.0",
|
"@react-native/metro-config": "0.77.0",
|
||||||
"@react-native/typescript-config": "0.77.0",
|
"@react-native/typescript-config": "0.77.0",
|
||||||
"@types/jest": "^29.5.13",
|
"@types/jest": "^29.5.13",
|
||||||
"@types/lodash": "^4.17.10",
|
"@types/lodash": "^4.17.10",
|
||||||
"@types/react": "^18.2.6",
|
"@types/react": "^18.2.6",
|
||||||
"@types/react-native-vector-icons": "^6.4.18",
|
"@types/react-native-vector-icons": "^6.4.18",
|
||||||
"@types/react-test-renderer": "^18.3.1",
|
"@types/react-test-renderer": "^18.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
||||||
"@typescript-eslint/parser": "^8.29.1",
|
"@typescript-eslint/parser": "^8.29.1",
|
||||||
"babel-plugin-module-resolver": "^5.0.2",
|
"babel-plugin-module-resolver": "^5.0.2",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^10.1.2",
|
"eslint-config-prettier": "^10.1.2",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-prettier": "^5.2.6",
|
"eslint-plugin-prettier": "^5.2.6",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-native": "^5.0.0",
|
"eslint-plugin-react-native": "^5.0.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"jest": "^29.6.3",
|
"jest": "^29.6.3",
|
||||||
"jscodeshift": "^0.15.2",
|
"jscodeshift": "^0.15.2",
|
||||||
"lint-staged": "^15.5.0",
|
"lint-staged": "^15.5.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"react-native-cli-bump-version": "^1.5.1",
|
"react-native-cli-bump-version": "^1.5.1",
|
||||||
"react-test-renderer": "18.3.1",
|
"react-test-renderer": "18.3.1",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.7.3"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
"prettier --write",
|
"prettier --write",
|
||||||
"eslint --fix"
|
"eslint --fix"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const UPDATE_INTERVAL: number = 7 // 8 milliseconds
|
export const UPDATE_INTERVAL: number = 7 // 8 milliseconds
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Capability } from "react-native-track-player";
|
import { Capability } from 'react-native-track-player'
|
||||||
|
|
||||||
export const CAPABILITIES: Capability[] = [
|
export const CAPABILITIES: Capability[] = [
|
||||||
Capability.Pause,
|
Capability.Pause,
|
||||||
Capability.Play,
|
Capability.Play,
|
||||||
Capability.PlayFromId,
|
Capability.PlayFromId,
|
||||||
Capability.SeekTo,
|
Capability.SeekTo,
|
||||||
// Capability.JumpForward,
|
// Capability.JumpForward,
|
||||||
// Capability.JumpBackward,
|
// Capability.JumpBackward,
|
||||||
Capability.SkipToNext,
|
Capability.SkipToNext,
|
||||||
Capability.SkipToPrevious,
|
Capability.SkipToPrevious,
|
||||||
// Capability.Like,
|
// Capability.Like,
|
||||||
// Capability.Dislike
|
// Capability.Dislike
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,61 +1,71 @@
|
|||||||
import { Progress, State } from "react-native-track-player";
|
import { Progress, State } from 'react-native-track-player'
|
||||||
import { JellifyTrack } from "../types/JellifyTrack";
|
import { JellifyTrack } from '../types/JellifyTrack'
|
||||||
import { PlaystateApi } from "@jellyfin/sdk/lib/generated-client/api/playstate-api";
|
import { PlaystateApi } from '@jellyfin/sdk/lib/generated-client/api/playstate-api'
|
||||||
import { convertSecondsToRunTimeTicks } from "../helpers/runtimeticks";
|
import { convertSecondsToRunTimeTicks } from '../helpers/runtimeticks'
|
||||||
|
|
||||||
export async function handlePlaybackState(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, state: State) {
|
export async function handlePlaybackState(
|
||||||
switch (state) {
|
sessionId: string,
|
||||||
case (State.Playing) : {
|
playstateApi: PlaystateApi,
|
||||||
console.debug("Report playback started")
|
track: JellifyTrack,
|
||||||
await playstateApi.reportPlaybackStart({
|
state: State,
|
||||||
playbackStartInfo: {
|
) {
|
||||||
SessionId: sessionId,
|
switch (state) {
|
||||||
ItemId: track.item.Id,
|
case State.Playing: {
|
||||||
}
|
console.debug('Report playback started')
|
||||||
});
|
await playstateApi.reportPlaybackStart({
|
||||||
break;
|
playbackStartInfo: {
|
||||||
}
|
SessionId: sessionId,
|
||||||
|
ItemId: track.item.Id,
|
||||||
case (State.Ended) :
|
},
|
||||||
case (State.Paused) :
|
})
|
||||||
case (State.Stopped) : {
|
break
|
||||||
console.debug("Report playback stopped")
|
}
|
||||||
await playstateApi.reportPlaybackStopped({
|
|
||||||
playbackStopInfo: {
|
|
||||||
SessionId: sessionId,
|
|
||||||
ItemId: track.item.Id,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default : {
|
case State.Ended:
|
||||||
return;
|
case State.Paused:
|
||||||
}
|
case State.Stopped: {
|
||||||
}
|
console.debug('Report playback stopped')
|
||||||
|
await playstateApi.reportPlaybackStopped({
|
||||||
|
playbackStopInfo: {
|
||||||
|
SessionId: sessionId,
|
||||||
|
ItemId: track.item.Id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handlePlaybackProgressUpdated(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, progress: Progress) {
|
export async function handlePlaybackProgressUpdated(
|
||||||
if (Math.floor(progress.duration - progress.position) === 5) {
|
sessionId: string,
|
||||||
console.debug("Track finished, scrobbling...");
|
playstateApi: PlaystateApi,
|
||||||
await playstateApi.reportPlaybackStopped({
|
track: JellifyTrack,
|
||||||
playbackStopInfo: {
|
progress: Progress,
|
||||||
SessionId: sessionId,
|
) {
|
||||||
ItemId: track.item.Id,
|
if (Math.floor(progress.duration - progress.position) === 5) {
|
||||||
PositionTicks: convertSecondsToRunTimeTicks(track.duration!)
|
console.debug('Track finished, scrobbling...')
|
||||||
}
|
await playstateApi.reportPlaybackStopped({
|
||||||
});
|
playbackStopInfo: {
|
||||||
} else {
|
SessionId: sessionId,
|
||||||
// DO NOTHING, reporting playback will just eat up power
|
ItemId: track.item.Id,
|
||||||
// Jellyfin can keep track of progress, we're going to intentionally
|
PositionTicks: convertSecondsToRunTimeTicks(track.duration!),
|
||||||
// only give it the "greatest hits" (i.e., anything that involves user interaction)
|
},
|
||||||
// console.debug("Reporting playback position");
|
})
|
||||||
// await playstateApi.reportPlaybackProgress({
|
} else {
|
||||||
// playbackProgressInfo: {
|
// DO NOTHING, reporting playback will just eat up power
|
||||||
// SessionId: sessionId,
|
// Jellyfin can keep track of progress, we're going to intentionally
|
||||||
// ItemId: track.ItemId,
|
// only give it the "greatest hits" (i.e., anything that involves user interaction)
|
||||||
// PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
// console.debug("Reporting playback position");
|
||||||
// }
|
// await playstateApi.reportPlaybackProgress({
|
||||||
// });
|
// playbackProgressInfo: {
|
||||||
}
|
// SessionId: sessionId,
|
||||||
|
// ItemId: track.ItemId,
|
||||||
|
// PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { isEmpty } from "lodash";
|
import { isEmpty } from 'lodash'
|
||||||
import { QueuingType } from "../../enums/queuing-type";
|
import { QueuingType } from '../../enums/queuing-type'
|
||||||
import { JellifyTrack } from "../../types/JellifyTrack";
|
import { JellifyTrack } from '../../types/JellifyTrack'
|
||||||
import { getActiveTrackIndex } from "react-native-track-player/lib/src/trackPlayer";
|
import { getActiveTrackIndex } from 'react-native-track-player/lib/src/trackPlayer'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds and returns the index of the player queue to insert additional tracks into
|
* Finds and returns the index of the player queue to insert additional tracks into
|
||||||
@@ -9,29 +9,26 @@ import { getActiveTrackIndex } from "react-native-track-player/lib/src/trackPlay
|
|||||||
* @returns The index to insert songs to play next at
|
* @returns The index to insert songs to play next at
|
||||||
*/
|
*/
|
||||||
export const findPlayNextIndexStart = async (playQueue: JellifyTrack[]) => {
|
export const findPlayNextIndexStart = async (playQueue: JellifyTrack[]) => {
|
||||||
if (isEmpty(playQueue))
|
if (isEmpty(playQueue)) return 0
|
||||||
return 0;
|
|
||||||
|
|
||||||
return (await getActiveTrackIndex())! + 1;
|
return (await getActiveTrackIndex())! + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds and returns the index of the play queue to insert user queue tracks into
|
* Finds and returns the index of the play queue to insert user queue tracks into
|
||||||
* @param playQueue The current player queue
|
* @param playQueue The current player queue
|
||||||
* @returns The index to insert songs to add to the user queue
|
* @returns The index to insert songs to add to the user queue
|
||||||
*/
|
*/
|
||||||
export const findPlayQueueIndexStart = async (playQueue: JellifyTrack[]) => {
|
export const findPlayQueueIndexStart = async (playQueue: JellifyTrack[]) => {
|
||||||
|
if (isEmpty(playQueue)) return 0
|
||||||
|
|
||||||
if (isEmpty(playQueue))
|
const activeIndex = await getActiveTrackIndex()
|
||||||
return 0;
|
|
||||||
|
|
||||||
const activeIndex = await getActiveTrackIndex();
|
if (playQueue.findIndex((track) => track.QueuingType === QueuingType.FromSelection) === -1)
|
||||||
|
return activeIndex! + 1
|
||||||
|
|
||||||
if (playQueue.findIndex(track => track.QueuingType === QueuingType.FromSelection) === -1)
|
return playQueue.findIndex(
|
||||||
return activeIndex! + 1
|
(queuedTrack, index) =>
|
||||||
|
queuedTrack.QueuingType === QueuingType.FromSelection && index > activeIndex!,
|
||||||
return playQueue.findIndex((queuedTrack, index) =>
|
)
|
||||||
queuedTrack.QueuingType === QueuingType.FromSelection &&
|
}
|
||||||
index > activeIndex!
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
import _ from "lodash";
|
import _ from 'lodash'
|
||||||
import { JellifyTrack } from "../../types/JellifyTrack";
|
import { JellifyTrack } from '../../types/JellifyTrack'
|
||||||
|
|
||||||
export function buildNewQueue(existingQueue: JellifyTrack[], tracksToInsert: JellifyTrack[], insertIndex: number) {
|
export function buildNewQueue(
|
||||||
|
existingQueue: JellifyTrack[],
|
||||||
|
tracksToInsert: JellifyTrack[],
|
||||||
|
insertIndex: number,
|
||||||
|
) {
|
||||||
|
console.debug(`Building new queue`)
|
||||||
|
|
||||||
console.debug(`Building new queue`);
|
let newQueue: JellifyTrack[] = []
|
||||||
|
|
||||||
let newQueue : JellifyTrack[] = [];
|
if (_.isEmpty(existingQueue)) newQueue = tracksToInsert
|
||||||
|
else {
|
||||||
|
newQueue = _.cloneDeep(existingQueue).splice(insertIndex, 0, ...tracksToInsert)
|
||||||
|
}
|
||||||
|
|
||||||
if (_.isEmpty(existingQueue))
|
console.debug(`Built new queue of ${newQueue.length} items`)
|
||||||
newQueue = tracksToInsert;
|
|
||||||
else {
|
|
||||||
newQueue = _.cloneDeep(existingQueue).splice(insertIndex, 0, ...tracksToInsert);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.debug(`Built new queue of ${newQueue.length} items`);
|
return newQueue
|
||||||
|
}
|
||||||
return newQueue;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import TrackPlayer, { RatingType } from "react-native-track-player"
|
import TrackPlayer, { RatingType } from 'react-native-track-player'
|
||||||
import { CAPABILITIES } from "../constants";
|
import { CAPABILITIES } from '../constants'
|
||||||
|
|
||||||
export const useUpdateOptions = async (isFavorite: boolean) => {
|
export const useUpdateOptions = async (isFavorite: boolean) => {
|
||||||
return await TrackPlayer.updateOptions({
|
return await TrackPlayer.updateOptions({
|
||||||
progressUpdateEventInterval: 1,
|
progressUpdateEventInterval: 1,
|
||||||
capabilities: CAPABILITIES,
|
capabilities: CAPABILITIES,
|
||||||
notificationCapabilities: CAPABILITIES,
|
notificationCapabilities: CAPABILITIES,
|
||||||
compactCapabilities: CAPABILITIES,
|
compactCapabilities: CAPABILITIES,
|
||||||
ratingType: RatingType.Heart,
|
ratingType: RatingType.Heart,
|
||||||
likeOptions: {
|
likeOptions: {
|
||||||
isActive: isFavorite,
|
isActive: isFavorite,
|
||||||
title: "Favorite"
|
title: 'Favorite',
|
||||||
},
|
},
|
||||||
dislikeOptions: {
|
dislikeOptions: {
|
||||||
isActive: !isFavorite,
|
isActive: !isFavorite,
|
||||||
title: "Unfavorite"
|
title: 'Unfavorite',
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import { JellifyTrack } from "../types/JellifyTrack";
|
import { JellifyTrack } from '../types/JellifyTrack'
|
||||||
import { QueuingType } from "../enums/queuing-type";
|
import { QueuingType } from '../enums/queuing-type'
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
import { Queue } from "./types/queue-item";
|
import { Queue } from './types/queue-item'
|
||||||
|
|
||||||
export interface QueueMutation {
|
export interface QueueMutation {
|
||||||
track: BaseItemDto;
|
track: BaseItemDto
|
||||||
index?: number | undefined;
|
index?: number | undefined
|
||||||
tracklist: BaseItemDto[];
|
tracklist: BaseItemDto[]
|
||||||
queue: Queue;
|
queue: Queue
|
||||||
queuingType?: QueuingType | undefined;
|
queuingType?: QueuingType | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddToQueueMutation {
|
export interface AddToQueueMutation {
|
||||||
track: BaseItemDto,
|
track: BaseItemDto
|
||||||
queuingType?: QueuingType | undefined;
|
queuingType?: QueuingType | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueueOrderMutation {
|
export interface QueueOrderMutation {
|
||||||
newOrder: JellifyTrack[];
|
newOrder: JellifyTrack[]
|
||||||
from: number;
|
from: number
|
||||||
to: number;
|
to: number
|
||||||
}
|
}
|
||||||
|
|||||||
1087
player/provider.tsx
1087
player/provider.tsx
File diff suppressed because it is too large
Load Diff
@@ -1,71 +1,66 @@
|
|||||||
import Client from "../api/client";
|
import Client from '../api/client'
|
||||||
import { JellifyTrack } from "../types/JellifyTrack";
|
import { JellifyTrack } from '../types/JellifyTrack'
|
||||||
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api'
|
||||||
import TrackPlayer, { Event, RatingType } from "react-native-track-player";
|
import TrackPlayer, { Event, RatingType } from 'react-native-track-player'
|
||||||
import { getActiveTrack, getActiveTrackIndex } from "react-native-track-player/lib/src/trackPlayer";
|
import { getActiveTrack, getActiveTrackIndex } from 'react-native-track-player/lib/src/trackPlayer'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jellify Playback Service.
|
* Jellify Playback Service.
|
||||||
*
|
*
|
||||||
* Sets up event listeners for remote control events and
|
* Sets up event listeners for remote control events and
|
||||||
* runs for the duration of the app lifecycle
|
* runs for the duration of the app lifecycle
|
||||||
*/
|
*/
|
||||||
export async function PlaybackService() {
|
export async function PlaybackService() {
|
||||||
|
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
|
||||||
|
await TrackPlayer.play()
|
||||||
|
})
|
||||||
|
TrackPlayer.addEventListener(Event.RemotePause, async () => {
|
||||||
|
await TrackPlayer.pause()
|
||||||
|
})
|
||||||
|
|
||||||
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
|
TrackPlayer.addEventListener(Event.RemoteNext, async () => {
|
||||||
await TrackPlayer.play();
|
await TrackPlayer.skipToNext()
|
||||||
});
|
})
|
||||||
TrackPlayer.addEventListener(Event.RemotePause, async () => {
|
|
||||||
await TrackPlayer.pause();
|
|
||||||
});
|
|
||||||
|
|
||||||
TrackPlayer.addEventListener(Event.RemoteNext, async () => {
|
TrackPlayer.addEventListener(Event.RemotePrevious, async () => {
|
||||||
await TrackPlayer.skipToNext();
|
await TrackPlayer.skipToPrevious()
|
||||||
});
|
})
|
||||||
|
|
||||||
TrackPlayer.addEventListener(Event.RemotePrevious, async () => {
|
TrackPlayer.addEventListener(Event.RemoteSeek, async (event) => {
|
||||||
await TrackPlayer.skipToPrevious();
|
await TrackPlayer.seekTo(event.position)
|
||||||
});
|
})
|
||||||
|
|
||||||
TrackPlayer.addEventListener(Event.RemoteSeek, async (event) => {
|
// TrackPlayer.addEventListener(Event.RemoteJumpForward, async (event) => {
|
||||||
await TrackPlayer.seekTo(event.position);
|
// await TrackPlayer.seekBy(event.interval)
|
||||||
});
|
// });
|
||||||
|
|
||||||
// TrackPlayer.addEventListener(Event.RemoteJumpForward, async (event) => {
|
// TrackPlayer.addEventListener(Event.RemoteJumpBackward, async (event) => {
|
||||||
// await TrackPlayer.seekBy(event.interval)
|
// await TrackPlayer.seekBy(-event.interval)
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// TrackPlayer.addEventListener(Event.RemoteJumpBackward, async (event) => {
|
TrackPlayer.addEventListener(Event.RemoteLike, async () => {
|
||||||
// await TrackPlayer.seekBy(-event.interval)
|
const nowPlaying = (await getActiveTrack()) as JellifyTrack
|
||||||
// });
|
const nowPlayingIndex = await getActiveTrackIndex()
|
||||||
|
|
||||||
TrackPlayer.addEventListener(Event.RemoteLike, async () => {
|
await getUserLibraryApi(Client.api!).markFavoriteItem({
|
||||||
|
itemId: nowPlaying.item.Id!,
|
||||||
|
})
|
||||||
|
|
||||||
const nowPlaying = await getActiveTrack() as JellifyTrack;
|
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
||||||
const nowPlayingIndex = await getActiveTrackIndex();
|
rating: RatingType.Heart,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
await getUserLibraryApi(Client.api!)
|
TrackPlayer.addEventListener(Event.RemoteDislike, async () => {
|
||||||
.markFavoriteItem({
|
const nowPlaying = (await getActiveTrack()) as JellifyTrack
|
||||||
itemId: nowPlaying.item.Id!
|
const nowPlayingIndex = await getActiveTrackIndex()
|
||||||
});
|
|
||||||
|
|
||||||
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
await getUserLibraryApi(Client.api!).markFavoriteItem({
|
||||||
rating: RatingType.Heart
|
itemId: nowPlaying.item.Id!,
|
||||||
})
|
})
|
||||||
});
|
|
||||||
|
|
||||||
TrackPlayer.addEventListener(Event.RemoteDislike, async () => {
|
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
||||||
|
rating: undefined,
|
||||||
const nowPlaying = await getActiveTrack() as JellifyTrack;
|
})
|
||||||
const nowPlayingIndex = await getActiveTrackIndex();
|
})
|
||||||
|
}
|
||||||
await getUserLibraryApi(Client.api!)
|
|
||||||
.markFavoriteItem({
|
|
||||||
itemId: nowPlaying.item.Id!
|
|
||||||
});
|
|
||||||
|
|
||||||
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
|
||||||
rating: undefined
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
|
|
||||||
export type Queue = BaseItemDto | "Recently Played" | "Search" | "Favorite Tracks";
|
export type Queue = BaseItemDto | 'Recently Played' | 'Search' | 'Favorite Tracks'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
import { QueuingType } from "../../enums/queuing-type"
|
import { QueuingType } from '../../enums/queuing-type'
|
||||||
|
|
||||||
export interface QueuingRequest {
|
export interface QueuingRequest {
|
||||||
song: BaseItemDto
|
song: BaseItemDto
|
||||||
queuingType: QueuingType
|
queuingType: QueuingType
|
||||||
atIndex?: number
|
atIndex?: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
project: {
|
project: {
|
||||||
ios: {},
|
ios: {},
|
||||||
android: {},
|
android: {},
|
||||||
},
|
},
|
||||||
assets: ['./assets/fonts/'],
|
assets: ['./assets/fonts/'],
|
||||||
dependencies: {
|
dependencies: {
|
||||||
'react-native-carplay': {
|
'react-native-carplay': {
|
||||||
platforms: {
|
platforms: {
|
||||||
android: null, // Disable autolinking for Android
|
android: null, // Disable autolinking for Android
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["config:recommended"]
|
||||||
"config:recommended"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,66 +3,66 @@ import { createTamagui, createTokens } from 'tamagui' // or '@tamagui/core'
|
|||||||
import { headingFont, bodyFont } from './fonts.config'
|
import { headingFont, bodyFont } from './fonts.config'
|
||||||
|
|
||||||
const tokens = createTokens({
|
const tokens = createTokens({
|
||||||
...TamaguiTokens,
|
...TamaguiTokens,
|
||||||
color: {
|
color: {
|
||||||
danger: "#ff0000",
|
danger: '#ff0000',
|
||||||
purpleDark: "#0C0622",
|
purpleDark: '#0C0622',
|
||||||
purple: "#100538",
|
purple: '#100538',
|
||||||
purpleGray: "#66617B",
|
purpleGray: '#66617B',
|
||||||
amethyst: "#7E72AF",
|
amethyst: '#7E72AF',
|
||||||
grape: "#5638BB",
|
grape: '#5638BB',
|
||||||
telemagenta: "#cc2f71",
|
telemagenta: '#cc2f71',
|
||||||
white: "#ffffff",
|
white: '#ffffff',
|
||||||
black: "#000000"
|
black: '#000000',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const jellifyConfig = createTamagui({
|
const jellifyConfig = createTamagui({
|
||||||
animations,
|
animations,
|
||||||
fonts:{
|
fonts: {
|
||||||
heading: headingFont,
|
heading: headingFont,
|
||||||
body: bodyFont,
|
body: bodyFont,
|
||||||
},
|
},
|
||||||
media,
|
media,
|
||||||
shorthands,
|
shorthands,
|
||||||
tokens,
|
tokens,
|
||||||
themes: {
|
themes: {
|
||||||
dark: {
|
dark: {
|
||||||
shadowColor: tokens.color.purple,
|
shadowColor: tokens.color.purple,
|
||||||
background: tokens.color.purpleDark,
|
background: tokens.color.purpleDark,
|
||||||
backgroundActive: tokens.color.amethyst,
|
backgroundActive: tokens.color.amethyst,
|
||||||
backgroundPress: tokens.color.amethyst,
|
backgroundPress: tokens.color.amethyst,
|
||||||
backgroundFocus: tokens.color.amethyst,
|
backgroundFocus: tokens.color.amethyst,
|
||||||
backgroundHover: tokens.color.purpleGray,
|
backgroundHover: tokens.color.purpleGray,
|
||||||
borderColor: tokens.color.amethyst,
|
borderColor: tokens.color.amethyst,
|
||||||
color: tokens.color.white
|
color: tokens.color.white,
|
||||||
},
|
},
|
||||||
dark_inverted_purple: {
|
dark_inverted_purple: {
|
||||||
color: tokens.color.purpleDark,
|
color: tokens.color.purpleDark,
|
||||||
borderColor: tokens.color.amethyst,
|
borderColor: tokens.color.amethyst,
|
||||||
background: tokens.color.amethyst
|
background: tokens.color.amethyst,
|
||||||
},
|
},
|
||||||
light: {
|
light: {
|
||||||
background: tokens.color.white,
|
background: tokens.color.white,
|
||||||
backgroundActive: tokens.color.amethyst,
|
backgroundActive: tokens.color.amethyst,
|
||||||
borderColor: tokens.color.purpleGray,
|
borderColor: tokens.color.purpleGray,
|
||||||
color: tokens.color.purpleDark
|
color: tokens.color.purpleDark,
|
||||||
},
|
},
|
||||||
light_inverted_purple: {
|
light_inverted_purple: {
|
||||||
color: tokens.color.purpleDark,
|
color: tokens.color.purpleDark,
|
||||||
borderColor: tokens.color.purpleDark,
|
borderColor: tokens.color.purpleDark,
|
||||||
background: tokens.color.purpleGray
|
background: tokens.color.purpleGray,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
export type JellifyConfig = typeof jellifyConfig
|
export type JellifyConfig = typeof jellifyConfig
|
||||||
|
|
||||||
declare module 'tamagui' {
|
declare module 'tamagui' {
|
||||||
// or '@tamagui/core'
|
// or '@tamagui/core'
|
||||||
// overrides TamaguiCustomConfig so your custom types
|
// overrides TamaguiCustomConfig so your custom types
|
||||||
// work everywhere you import `tamagui`
|
// work everywhere you import `tamagui`
|
||||||
interface TamaguiCustomConfig extends JellifyConfig {}
|
interface TamaguiCustomConfig extends JellifyConfig {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default jellifyConfig
|
export default jellifyConfig
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "@react-native/typescript-config/tsconfig.json",
|
"extends": "@react-native/typescript-config/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": ["./*"]
|
||||||
"./*"
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
export interface JellifyLibrary {
|
export interface JellifyLibrary {
|
||||||
musicLibraryId: string;
|
musicLibraryId: string
|
||||||
musicLibraryName?: string | undefined;
|
musicLibraryName?: string | undefined
|
||||||
musicLibraryPrimaryImageId?: string | undefined;
|
musicLibraryPrimaryImageId?: string | undefined
|
||||||
playlistLibraryId?: string | undefined;
|
playlistLibraryId?: string | undefined
|
||||||
playlistLibraryPrimaryImageId?: string | undefined;
|
playlistLibraryPrimaryImageId?: string | undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export interface JellifyServer {
|
export interface JellifyServer {
|
||||||
url: string;
|
url: string
|
||||||
address: string;
|
address: string
|
||||||
name: string;
|
name: string
|
||||||
version: string;
|
version: string
|
||||||
startUpComplete: boolean;
|
startUpComplete: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,34 @@
|
|||||||
import { PitchAlgorithm, RatingType, Track, TrackType } from "react-native-track-player"
|
import { PitchAlgorithm, RatingType, Track, TrackType } from 'react-native-track-player'
|
||||||
import { QueuingType } from "../enums/queuing-type";
|
import { QueuingType } from '../enums/queuing-type'
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
|
|
||||||
export interface JellifyTrack extends Track {
|
export interface JellifyTrack extends Track {
|
||||||
url: string;
|
url: string
|
||||||
type?: TrackType | undefined;
|
type?: TrackType | undefined
|
||||||
userAgent?: string | undefined;
|
userAgent?: string | undefined
|
||||||
contentType?: string | undefined;
|
contentType?: string | undefined
|
||||||
pitchAlgorithm?: PitchAlgorithm | undefined;
|
pitchAlgorithm?: PitchAlgorithm | undefined
|
||||||
headers?: { [key: string]: any; } | undefined;
|
|
||||||
|
|
||||||
title?: string | undefined;
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
album?: string | undefined;
|
headers?: { [key: string]: any } | undefined
|
||||||
artist?: string | undefined;
|
|
||||||
duration?: number | undefined;
|
|
||||||
artwork?: string | undefined;
|
|
||||||
description?: string | undefined;
|
|
||||||
genre?: string | undefined;
|
|
||||||
date?: string | undefined;
|
|
||||||
rating?: RatingType | undefined;
|
|
||||||
isLiveStream?: boolean | undefined;
|
|
||||||
|
|
||||||
item: BaseItemDto;
|
title?: string | undefined
|
||||||
|
album?: string | undefined
|
||||||
|
artist?: string | undefined
|
||||||
|
duration?: number | undefined
|
||||||
|
artwork?: string | undefined
|
||||||
|
description?: string | undefined
|
||||||
|
genre?: string | undefined
|
||||||
|
date?: string | undefined
|
||||||
|
rating?: RatingType | undefined
|
||||||
|
isLiveStream?: boolean | undefined
|
||||||
|
|
||||||
/**
|
item: BaseItemDto
|
||||||
* Represents the type of queuing for this song, be it that it was
|
|
||||||
* queued from the selection chosen, queued by the user directly, or marked
|
/**
|
||||||
* to play next by the user
|
* Represents the type of queuing for this song, be it that it was
|
||||||
*/
|
* queued from the selection chosen, queued by the user directly, or marked
|
||||||
QueuingType?: QueuingType | undefined ;
|
* to play next by the user
|
||||||
}
|
*/
|
||||||
|
QueuingType?: QueuingType | undefined
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface JellifyUser {
|
export interface JellifyUser {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
accessToken: string;
|
accessToken: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user