Compare commits
67 Commits
v0.0.1-alp
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eab7793f5 | ||
|
|
66eb4bd61d | ||
|
|
3312e99911 | ||
|
|
3ae967e137 | ||
|
|
d18daf027e | ||
|
|
0ae7e3ba0c | ||
|
|
e918f089e2 | ||
|
|
491f019fb3 | ||
|
|
0af730dabe | ||
|
|
fadfabc868 | ||
|
|
de73fe6358 | ||
|
|
6f3ed9c988 | ||
|
|
ff484cebed | ||
|
|
1e14ed3271 | ||
|
|
954fe38531 | ||
|
|
83cf0e4d56 | ||
|
|
3b98eb4bf1 | ||
|
|
49bb4dfaab | ||
|
|
9f9e61017e | ||
|
|
65c7db9911 | ||
|
|
481a411e4c | ||
|
|
8eeddebad7 | ||
|
|
974c73e70a | ||
|
|
0988e10a09 | ||
|
|
9ea0753912 | ||
|
|
6cf421f24f | ||
|
|
0df9d4a621 | ||
|
|
6a1d75c27c | ||
|
|
c88158cf16 | ||
|
|
3026fdf4da | ||
|
|
12b53eca4a | ||
|
|
3ff971a773 | ||
|
|
8fac21f4d8 | ||
|
|
6e85013b5a | ||
|
|
edce0329cf | ||
|
|
be40d8a404 | ||
|
|
9138946ba9 | ||
|
|
ea11a7d317 | ||
|
|
91344300c8 | ||
|
|
52146a6e12 | ||
|
|
0712e6b8ca | ||
|
|
2155320f6b | ||
|
|
4ff1e173ba | ||
|
|
c1f6a2984a | ||
|
|
8ba3080734 | ||
|
|
fe22661036 | ||
|
|
3762d9166b | ||
|
|
48dd8c23aa | ||
|
|
86a4d11f67 | ||
|
|
57b79bf4e2 | ||
|
|
9c24dede18 | ||
|
|
6978a4dfea | ||
|
|
ea91b083c3 | ||
|
|
715e9e3580 | ||
|
|
f1a084bc1e | ||
|
|
425b19734c | ||
|
|
60bbbc7193 | ||
|
|
8aafd3f0d6 | ||
|
|
fde4d7ecb3 | ||
|
|
db2801d77d | ||
|
|
218c72d9e8 | ||
|
|
4d3f6f30d1 | ||
|
|
90a61f258d | ||
|
|
3a1fcd4594 | ||
|
|
b9501265f9 | ||
|
|
3680cad5fc | ||
|
|
0690aae374 |
55
.github/workflows/fastlane.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Fastlane
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
# build-ios:
|
||||
# runs-on: macos-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - name: Install Node dependencies
|
||||
# run: npm install
|
||||
# - name: Install CocoaPods dependencies
|
||||
# run: pod install --project-directory=./ios
|
||||
# - name: Run fastlane setup
|
||||
# env:
|
||||
# APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
|
||||
# TEAM_ID: ${{ secrets.TEAM_ID }}
|
||||
# run: |
|
||||
# cd ios
|
||||
# fastlane beta --verbose
|
||||
# - name: Upload artifact
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: my-artifact
|
||||
# path: output/*.ipa
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set outputs
|
||||
id: vars
|
||||
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
|
||||
- name: Set up Ruby 2.6
|
||||
uses: actions/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6.x
|
||||
- name: Install fastlane
|
||||
run: |
|
||||
gem install bundler
|
||||
bundle install -j 6
|
||||
- name: Install Node dependencies
|
||||
run: npm install
|
||||
- name: Generate APK
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
run: fastlane android beta
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: jellyfin-audio-player-android-${{ steps.vars.outputs.sha_short }}.apk
|
||||
path: android/app/build/outputs/**/*.apk
|
||||
8
.gitignore
vendored
@@ -61,3 +61,11 @@ buck-out/
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
|
||||
build/
|
||||
fastlane/report.xml
|
||||
fastlane/Appfile
|
||||
certificates/
|
||||
|
||||
.env
|
||||
sentry.properties
|
||||
12
Gemfile
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
||||
|
||||
# gem "rails"
|
||||
|
||||
gem "fastlane", "~> 2.153"
|
||||
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
||||
203
Gemfile.lock
Normal file
@@ -0,0 +1,203 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.3)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.421.0)
|
||||
aws-sdk-core (3.111.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.41.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.87.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.0.3)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
declarative (0.0.20)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.6.3)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (3.2.1)
|
||||
excon (0.78.1)
|
||||
faraday (1.3.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ruby2_keywords
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday_middleware (1.0.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.1)
|
||||
fastlane (2.172.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.37.0, < 0.39.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-sentry (1.8.0)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.38.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.9)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.12)
|
||||
google-apis-core (0.2.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.14)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
rexml
|
||||
signet (~> 0.14)
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.1.0)
|
||||
google-apis-core (~> 0.1)
|
||||
google-apis-storage_v1 (0.1.0)
|
||||
google-apis-core (~> 0.1)
|
||||
google-cloud-core (1.5.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.4.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-errors (1.0.1)
|
||||
google-cloud-storage (1.30.0)
|
||||
addressable (~> 2.5)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.1)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (~> 0.9)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (0.15.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.14)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
json (2.5.1)
|
||||
jwt (2.2.2)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.0.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
os (1.1.1)
|
||||
plist (3.6.0)
|
||||
public_suffix (4.0.6)
|
||||
rake (13.0.3)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.4)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.4)
|
||||
rubyzip (2.3.0)
|
||||
security (0.1.3)
|
||||
signet (0.14.1)
|
||||
addressable (~> 2.3)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
webrick (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.19.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane (~> 2.153)
|
||||
fastlane-plugin-sentry
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
66
README.md
@@ -1,25 +1,69 @@
|
||||
# Jellyfin Audio Player
|
||||
This is a React Native-based audio streaming app for Jellyfin.
|
||||

|
||||

|
||||
|
||||
This is a [React Native](https://reactnative.dev/)-based audio streaming app for [Jellyfin](https://jellyfin.org/). Jellyfin is a community-based piece of software that allows you to stream your media library over the internet. By means of React Native, Jellyfin Audio Player allows you to stream your Jellyfin Music library, with full support for background audio and casting (ie. Airplay and Chromecast).
|
||||
|
||||
## ❗️Now open for beta testing on iOS
|
||||
Please follow this link to enroll for the TestFlight beta release of Jellyfin Audio Player: https://testflight.apple.com/join/cf2AMDpx.
|
||||
|
||||
|||||
|
||||
|-|-|-|-|
|
||||
|
||||
## Features
|
||||
* Sorting by recent albums
|
||||
* Browsing through all available albums
|
||||
* Searching based on album and artist names
|
||||
* Queuing tracks and albums
|
||||
* AirPlay and Chromecast support
|
||||
* Background audio
|
||||
* Native Dark Mode
|
||||
|
||||
### Features being considered
|
||||
* Downloading music for offline playback
|
||||
* Searching based on track names
|
||||
* Looping and shuffling queue
|
||||
|
||||
## Getting Started
|
||||
What you want get started on depends on your intentions. As there is no build for general availability yet, you will need to build this project yourself.
|
||||
This piece of software is in alpha. I am working on getting this app in ~~TestFlight and~~ Google Play Developer Console, but this is contingent on keys being available. In the meantime, IPAs and APK are intermittenly released on the [Releases page](https://github.com/leinelissen/jellyfin-audio-player/releases). Alternatively, you can build this app from source using the build instructions.
|
||||
|
||||
### Using the app
|
||||
You will need to setup your Jellyfin account for the application to be able to pull in all your audio. To do this, go over to the "Settings" tab and click the "Set Jellyfin server"-button. A modal will pop up in which you will enter your Jellyfin server url, after which you enter your credentials in the provided browser view. When the app detects your credentials, they will automatically be remembered by the app.
|
||||
You will need to setup your Jellyfin account for the application to be able to pull in all your audio. To do this, go over to the "Settings" tab and click the "Set Jellyfin server"-button. A modal will pop up in which you will enter your Jellyfin server url, after which you enter your credentials in the provided browser view. When the app detects your credentials, they will automatically be remembered for the future.
|
||||
|
||||
## Building from source
|
||||
### Prerequisites
|
||||
This project is built on React Native, and first of all requires [NodeJS](https://nodejs.org/en/) to be installed. After installing it and cloning this repository, don't forget ton run `npm install` on your command line, so that all Node dependencies are installed.
|
||||
|
||||
#### iOS Prerequisites
|
||||
[XCode](https://developer.apple.com/download/) is required to build the iOS application. It also comes bundles with iOS simulators which make development exceedingly easy. This does mean that iOS development is limited to macs.
|
||||
|
||||
#### Android prerequisites
|
||||
[Android Studio](https://developer.android.com/studio/install) is recommended for development as it includes the Android SDK as well as Android Simulators for devleopment. At the very least, installing the Android SDK is neccessary for building any version of the app.
|
||||
|
||||
### Development Build
|
||||
This app has been mainly developed for iOS, but should mostly function on Android as well. To get started, do the following:
|
||||
1. Clone this repository
|
||||
2. Install [NodeJS](https://nodejs.org/en/) and [XCode](https://developer.apple.com/download/)
|
||||
3. `npm install`
|
||||
4. `npm run ios`
|
||||
As soon as all prerequisites are covered, you can start development in either iOS or Android simulators by running the following
|
||||
```
|
||||
npm run ios
|
||||
npm run android
|
||||
```
|
||||
|
||||
### Production Build
|
||||
Follow step 1-3 from the development build, then do the following:
|
||||
This project is configured using [Fastlane](https://docs.fastlane.tools/), which allows for easy IPA and APK generation. To get started with this, make sure you install Fastlane first either using bundler (see below), or alternatively via e.g. Homebrew ([see supported methods](https://docs.fastlane.tools/getting-started/ios/setup/)).
|
||||
```
|
||||
npm run build:ios
|
||||
gem install bundler
|
||||
bundle install -j 6
|
||||
```
|
||||
Then open `ios/JellyfinAudioPlayer.xcworkspace` and build the project in XCode.
|
||||
When fastlane is setup, you can run either commands for generating IPA (iOS) or APK (Android) bundles.
|
||||
```
|
||||
fastlane ios beta
|
||||
fastlane android beta
|
||||
```
|
||||
|
||||
## Licensing and Credits
|
||||
This work is licensed under the MIT license and was built by Lei Nelissen.
|
||||
|
||||
<a href="https://bmd.studio">
|
||||
<img src="./docs/images/bmd-logo-icon.png" alt="BMD Studio" width="150" height="150" />
|
||||
</a>
|
||||
|
||||
An Apple Developer license, as well as various forms of support are graciously provided for by [BMD Studio](https://bmd.studio), experts in finding creative solutions for difficult problems.
|
||||
@@ -79,9 +79,11 @@ import com.android.build.OutputFile
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: false, // clean and rebuild if changing
|
||||
entryFile: 'index.js'
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
apply from: "../../node_modules/@sentry/react-native/sentry.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
@@ -134,6 +136,7 @@ android {
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
multiDexEnabled true
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
@@ -164,13 +167,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst "lib/armeabi-v7a/libc++_shared.so"
|
||||
pickFirst "lib/arm64-v8a/libc++_shared.so"
|
||||
pickFirst "lib/x86/libc++_shared.so"
|
||||
pickFirst "lib/x86_64/libc++_shared.so"
|
||||
}
|
||||
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 3.6 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
@@ -1,9 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:textColor">#000000</item>
|
||||
<!-- <item name="android:textColor">#000000</item> -->
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -25,4 +25,4 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.33.1
|
||||
FLIPPER_VERSION=0.54.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
2
app.json
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "JellyfinAudioPlayer",
|
||||
"displayName": "Audio Player"
|
||||
"displayName": "Jellyfin Player"
|
||||
}
|
||||
@@ -1,19 +1,36 @@
|
||||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
presets: [
|
||||
'module:metro-react-native-babel-preset',
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
// root: ['./src'],
|
||||
root: ['.'],
|
||||
extensions: [
|
||||
'.ios.ts',
|
||||
'.android.ts',
|
||||
'.ts',
|
||||
'.ios.tsx',
|
||||
'.android.tsx',
|
||||
'.tsx',
|
||||
'.jsx',
|
||||
'.js',
|
||||
'.json',
|
||||
],
|
||||
alias: {
|
||||
store: './src/store',
|
||||
components: './src/components',
|
||||
utility: './src/utility',
|
||||
screens: './src/screens',
|
||||
assets: './src/assets',
|
||||
'@localisation': './src/localisation',
|
||||
CONSTANTS: './src/CONSTANTS',
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'module:react-native-dotenv'
|
||||
]
|
||||
]
|
||||
};
|
||||
|
||||
BIN
docs/images/bmd-logo-icon.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
4
fastlane/Appfile
Normal file
@@ -0,0 +1,4 @@
|
||||
package_name("nl.moeilijkedingen.jellyfinaudioplayer")
|
||||
app_identifier("nl.moeilijkedingen.jellyfinaudioplayer")
|
||||
apple_id("lei@moeilijkedingen.nl")
|
||||
team_id("238P3C58WC")
|
||||
86
fastlane/Fastfile
Normal file
@@ -0,0 +1,86 @@
|
||||
default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
lane :alpha do
|
||||
get_certificates(
|
||||
development: true,
|
||||
output_path: 'certificates/'
|
||||
)
|
||||
build_app(
|
||||
scheme: "Jellyfin Player",
|
||||
export_method: "development",
|
||||
output_directory: "build",
|
||||
workspace: "ios/JellyfinAudioPlayer.xcworkspace"
|
||||
)
|
||||
end
|
||||
lane :beta do
|
||||
get_certificates(
|
||||
output_path: 'certificates/'
|
||||
)
|
||||
get_provisioning_profile(
|
||||
output_path: 'certificates/',
|
||||
filename: "provisioning.mobileprovision",
|
||||
fail_on_name_taken: true,
|
||||
)
|
||||
update_code_signing_settings(
|
||||
use_automatic_signing: true,
|
||||
path: "ios/JellyfinAudioPlayer.xcodeproj"
|
||||
)
|
||||
increment_build_number(
|
||||
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj"
|
||||
)
|
||||
build_app(
|
||||
scheme: "Jellyfin Player",
|
||||
output_directory: "build",
|
||||
workspace: "ios/JellyfinAudioPlayer.xcworkspace",
|
||||
export_method: "app-store",
|
||||
)
|
||||
upload_to_testflight
|
||||
build_number = get_build_number(
|
||||
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj"
|
||||
)
|
||||
Dir.chdir("..") do
|
||||
sh(
|
||||
"npx", "react-native", "bundle",
|
||||
"--dev", "false",
|
||||
"--platform", "ios",
|
||||
"--entry-file", "index.js",
|
||||
"--bundle-output", "build/index.ios.bundle",
|
||||
"--sourcemap-output", "build/index.ios.bundle.map"
|
||||
)
|
||||
end
|
||||
|
||||
sentry_create_release(
|
||||
version: "1.0+#{build_number}",
|
||||
app_identifier: 'nl.moeilijkedingen.jellyfinaudioplayer',
|
||||
finalize: true
|
||||
)
|
||||
sentry_upload_dsym(
|
||||
dsym_path: 'build/Jellyfin Player.app.dSYM.zip',
|
||||
info_plist: 'ios/JellyfinAudioPlayer/Info.plist',
|
||||
)
|
||||
sentry_upload_file(
|
||||
version: "1.0+#{build_number}",
|
||||
app_identifier: 'nl.moeilijkedingen.jellyfinaudioplayer',
|
||||
dist: build_number,
|
||||
file: 'build/index.ios.bundle',
|
||||
)
|
||||
sentry_upload_sourcemap(
|
||||
version: "1.0+#{build_number}",
|
||||
app_identifier: 'nl.moeilijkedingen.jellyfinaudioplayer',
|
||||
dist: build_number,
|
||||
sourcemap: 'build/index.ios.bundle.map',
|
||||
rewrite: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
platform :android do
|
||||
desc "Generate beta build"
|
||||
lane :beta do
|
||||
gradle(
|
||||
task: "clean assembleRelease",
|
||||
project_dir: "android"
|
||||
)
|
||||
end
|
||||
end
|
||||
5
fastlane/Pluginfile
Normal file
@@ -0,0 +1,5 @@
|
||||
# Autogenerated by fastlane
|
||||
#
|
||||
# Ensure this file is checked in to source control!
|
||||
|
||||
gem 'fastlane-plugin-sentry'
|
||||
43
fastlane/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
fastlane documentation
|
||||
================
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
Install _fastlane_ using
|
||||
```
|
||||
[sudo] gem install fastlane -NV
|
||||
```
|
||||
or alternatively using `brew install fastlane`
|
||||
|
||||
# Available Actions
|
||||
## iOS
|
||||
### ios alpha
|
||||
```
|
||||
fastlane ios alpha
|
||||
```
|
||||
|
||||
### ios beta
|
||||
```
|
||||
fastlane ios beta
|
||||
```
|
||||
|
||||
|
||||
----
|
||||
|
||||
## Android
|
||||
### android beta
|
||||
```
|
||||
fastlane android beta
|
||||
```
|
||||
Generate beta build
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
|
||||
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
|
||||
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||
@@ -1,18 +1,17 @@
|
||||
// import React from 'react';
|
||||
|
||||
// // if (process.env.NODE_ENV === 'development') {
|
||||
// // const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
||||
// // whyDidYouRender(React, {
|
||||
// // trackAllPureComponents: true,
|
||||
// // });
|
||||
// // }
|
||||
|
||||
import 'react-native-gesture-handler';
|
||||
import { AppRegistry } from 'react-native';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import App from './src/components/App';
|
||||
import { name as appName } from './app.json';
|
||||
import PlaybackService from './src/utility/PlaybackService';
|
||||
import * as Sentry from '@sentry/react-native';
|
||||
import { SENTRY_DSN } from '@env';
|
||||
|
||||
if (SENTRY_DSN) {
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN
|
||||
});
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
TrackPlayer.registerPlaybackService(() => PlaybackService);
|
||||
8
ios/File.swift
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// File.swift
|
||||
// JellyfinAudioPlayer
|
||||
//
|
||||
// Created by Lei Nelissen on 02/11/2020.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
4
ios/JellyfinAudioPlayer-Bridging-Header.h
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -12,15 +12,9 @@
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
2DCD954D1E0B4F2C00145EB5 /* JellyfinAudioPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* JellyfinAudioPlayerTests.m */; };
|
||||
463612208457EBB4B723000A /* libPods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 842AB597D56E84A4ACDC4735 /* libPods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.a */; };
|
||||
4F46F441249A56FF00308470 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; };
|
||||
9C3FCC29E5AD02738E8E3F28 /* libPods-JellyfinAudioPlayer-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D43C7610851B9666193E3F6 /* libPods-JellyfinAudioPlayer-tvOSTests.a */; };
|
||||
4FA1B23D2550A94C007A035E /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA1B23C2550A94C007A035E /* File.swift */; };
|
||||
A807E2BB233D6F9347D8A95C /* libPods-JellyfinAudioPlayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 71370E61E2CC6BD9372ADCF3 /* libPods-JellyfinAudioPlayer.a */; };
|
||||
A9112B4690DCDB63E46B6C30 /* libPods-JellyfinAudioPlayer-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E35451F7979C52C1692C4C9F /* libPods-JellyfinAudioPlayer-tvOS.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -31,17 +25,9 @@
|
||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||
remoteInfo = JellyfinAudioPlayer;
|
||||
};
|
||||
2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;
|
||||
remoteInfo = "JellyfinAudioPlayer-tvOS";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
||||
00E356EE1AD99517003FC87E /* JellyfinAudioPlayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JellyfinAudioPlayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* JellyfinAudioPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JellyfinAudioPlayerTests.m; sourceTree = "<group>"; };
|
||||
@@ -53,9 +39,9 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = JellyfinAudioPlayer/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = JellyfinAudioPlayer/main.m; sourceTree = "<group>"; };
|
||||
2710519FCC41B05FDE6738DF /* Pods-JellyfinAudioPlayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinAudioPlayer.release.xcconfig"; path = "Target Support Files/Pods-JellyfinAudioPlayer/Pods-JellyfinAudioPlayer.release.xcconfig"; sourceTree = "<group>"; };
|
||||
2D02E47B1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JellyfinAudioPlayer-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2D02E4901E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "JellyfinAudioPlayer-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
39572B38534BBDBB596C8C95 /* Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.release.xcconfig"; path = "Target Support Files/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
4FA1B23B2550A94B007A035E /* JellyfinAudioPlayer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JellyfinAudioPlayer-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
4FA1B23C2550A94C007A035E /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
||||
590BEA7DE65819C5B5FDAD06 /* Pods-JellyfinAudioPlayer-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinAudioPlayer-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-JellyfinAudioPlayer-tvOSTests/Pods-JellyfinAudioPlayer-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
71370E61E2CC6BD9372ADCF3 /* libPods-JellyfinAudioPlayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JellyfinAudioPlayer.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7D43C7610851B9666193E3F6 /* libPods-JellyfinAudioPlayer-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JellyfinAudioPlayer-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -87,22 +73,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D02E4781E0B4A5D006451C7 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A9112B4690DCDB63E46B6C30 /* libPods-JellyfinAudioPlayer-tvOS.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D02E48D1E0B4A5D006451C7 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9C3FCC29E5AD02738E8E3F28 /* libPods-JellyfinAudioPlayer-tvOSTests.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -126,13 +96,14 @@
|
||||
13B07FAE1A68108700A75B9A /* JellyfinAudioPlayer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
4FA1B23C2550A94C007A035E /* File.swift */,
|
||||
4FA1B23B2550A94B007A035E /* JellyfinAudioPlayer-Bridging-Header.h */,
|
||||
);
|
||||
name = JellyfinAudioPlayer;
|
||||
sourceTree = "<group>";
|
||||
@@ -192,8 +163,6 @@
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* Jellyfin Player.app */,
|
||||
00E356EE1AD99517003FC87E /* JellyfinAudioPlayerTests.xctest */,
|
||||
2D02E47B1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOS.app */,
|
||||
2D02E4901E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOSTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -209,6 +178,7 @@
|
||||
00E356EA1AD99517003FC87E /* Sources */,
|
||||
00E356EB1AD99517003FC87E /* Frameworks */,
|
||||
00E356EC1AD99517003FC87E /* Resources */,
|
||||
BDE784ECF29EF861DBFF49D7 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -230,6 +200,8 @@
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
B9FB8FC65CEFF9AFAC71127E /* [CP] Copy Pods Resources */,
|
||||
1DC46C84C90B4D84A18AC142 /* Upload Debug Symbols to Sentry */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -240,45 +212,6 @@
|
||||
productReference = 13B07F961A680F5B00A75B9A /* Jellyfin Player.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
2D02E47A1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "JellyfinAudioPlayer-tvOS" */;
|
||||
buildPhases = (
|
||||
05D2621E5320B14963E6BA49 /* [CP] Check Pods Manifest.lock */,
|
||||
FD10A7F122414F3F0027D42C /* Start Packager */,
|
||||
2D02E4771E0B4A5D006451C7 /* Sources */,
|
||||
2D02E4781E0B4A5D006451C7 /* Frameworks */,
|
||||
2D02E4791E0B4A5D006451C7 /* Resources */,
|
||||
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "JellyfinAudioPlayer-tvOS";
|
||||
productName = "JellyfinAudioPlayer-tvOS";
|
||||
productReference = 2D02E47B1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOS.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
2D02E48F1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOSTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "JellyfinAudioPlayer-tvOSTests" */;
|
||||
buildPhases = (
|
||||
BFD53620BC35198B520DBD0A /* [CP] Check Pods Manifest.lock */,
|
||||
2D02E48C1E0B4A5D006451C7 /* Sources */,
|
||||
2D02E48D1E0B4A5D006451C7 /* Frameworks */,
|
||||
2D02E48E1E0B4A5D006451C7 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "JellyfinAudioPlayer-tvOSTests";
|
||||
productName = "JellyfinAudioPlayer-tvOSTests";
|
||||
productReference = 2D02E4901E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOSTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -289,24 +222,15 @@
|
||||
TargetAttributes = {
|
||||
00E356ED1AD99517003FC87E = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
DevelopmentTeam = HD2D35G9Y4;
|
||||
DevelopmentTeam = 238P3C58WC;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
DevelopmentTeam = HD2D35G9Y4;
|
||||
LastSwiftMigration = 1120;
|
||||
};
|
||||
2D02E47A1E0B4A5D006451C7 = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = HD2D35G9Y4;
|
||||
DevelopmentTeam = 238P3C58WC;
|
||||
LastSwiftMigration = 1210;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
2D02E48F1E0B4A5D006451C7 = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = HD2D35G9Y4;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 2D02E47A1E0B4A5D006451C7;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "JellyfinAudioPlayer" */;
|
||||
@@ -324,8 +248,6 @@
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* JellyfinAudioPlayer */,
|
||||
00E356ED1AD99517003FC87E /* JellyfinAudioPlayerTests */,
|
||||
2D02E47A1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOS */,
|
||||
2D02E48F1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOSTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -342,27 +264,11 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4F46F441249A56FF00308470 /* main.jsbundle in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D02E4791E0B4A5D006451C7 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D02E48E1E0B4A5D006451C7 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@@ -371,51 +277,50 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle React Native code and images";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/main.jsbundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport EXTRA_PACKAGER_ARGS=\"--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map\"\nexport NODE_BINARY=$(which node)\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||
};
|
||||
05D2621E5320B14963E6BA49 /* [CP] Check Pods Manifest.lock */ = {
|
||||
1DC46C84C90B4D84A18AC142 /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
name = "Upload Debug Symbols to Sentry";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-JellyfinAudioPlayer-tvOS-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
shellScript = "export SENTRY_PROPERTIES=sentry.properties\n../node_modules/@sentry/cli/bin/sentry-cli upload-dsym";
|
||||
};
|
||||
B9FB8FC65CEFF9AFAC71127E /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer/Pods-JellyfinAudioPlayer-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer/Pods-JellyfinAudioPlayer-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle React Native Code And Images";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
|
||||
};
|
||||
BBD71961640F29097BE9932A /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -438,26 +343,22 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
BFD53620BC35198B520DBD0A /* [CP] Check Pods Manifest.lock */ = {
|
||||
BDE784ECF29EF861DBFF49D7 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-JellyfinAudioPlayer-tvOSTests-checkManifestLockResult.txt",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E68FAF43791AC236CF4BF8CB /* [CP] Check Pods Manifest.lock */ = {
|
||||
@@ -501,25 +402,6 @@
|
||||
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
FD10A7F122414F3F0027D42C /* Start Packager */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Start Packager";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -536,27 +418,11 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
|
||||
4FA1B23D2550A94C007A035E /* File.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D02E4771E0B4A5D006451C7 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */,
|
||||
2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D02E48C1E0B4A5D006451C7 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2DCD954D1E0B4F2C00145EB5 /* JellyfinAudioPlayerTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -565,11 +431,6 @@
|
||||
target = 13B07F861A680F5B00A75B9A /* JellyfinAudioPlayer */;
|
||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||
};
|
||||
2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 2D02E47A1E0B4A5D006451C7 /* JellyfinAudioPlayer-tvOS */;
|
||||
targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -589,8 +450,10 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8DAD3DCD6450C4255A20940E /* Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
@@ -613,9 +476,11 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 39572B38534BBDBB596C8C95 /* Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
INFOPLIST_FILE = JellyfinAudioPlayerTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
@@ -626,6 +491,8 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "915c5213-22f6-4f9d-8065-2a06300f9bfb";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JellyfinAudioPlayer.app/JellyfinAudioPlayer";
|
||||
};
|
||||
name = Release;
|
||||
@@ -636,8 +503,9 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
@@ -650,8 +518,10 @@
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nl.moeilijkedingen.jellyfinaudioplayer;
|
||||
PRODUCT_NAME = "Jellyfin Player";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "JellyfinAudioPlayer-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -664,8 +534,9 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
INFOPLIST_FILE = JellyfinAudioPlayer/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
@@ -673,127 +544,16 @@
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nl.moeilijkedingen.jellyfinaudioplayer;
|
||||
PRODUCT_NAME = "Jellyfin Player";
|
||||
PROVISIONING_PROFILE = "915c5213-22f6-4f9d-8065-2a06300f9bfb";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "JellyfinAudioPlayer-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2D02E4971E0B4A5E006451C7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = DEB1E90737138C4BD6E5323D /* Pods-JellyfinAudioPlayer-tvOS.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = "JellyfinAudioPlayer-tvOS/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.JellyfinAudioPlayer-tvOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2D02E4981E0B4A5E006451C7 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B179EFCC51AF0F281E73F0B8 /* Pods-JellyfinAudioPlayer-tvOS.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = "JellyfinAudioPlayer-tvOS/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.JellyfinAudioPlayer-tvOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.2;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2D02E4991E0B4A5E006451C7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 86AD8F7165272727927424B9 /* Pods-JellyfinAudioPlayer-tvOSTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = "JellyfinAudioPlayer-tvOSTests/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.JellyfinAudioPlayer-tvOSTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JellyfinAudioPlayer-tvOS.app/JellyfinAudioPlayer-tvOS";
|
||||
TVOS_DEPLOYMENT_TARGET = 10.1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2D02E49A1E0B4A5E006451C7 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 590BEA7DE65819C5B5FDAD06 /* Pods-JellyfinAudioPlayer-tvOSTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = HD2D35G9Y4;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = "JellyfinAudioPlayer-tvOSTests/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.JellyfinAudioPlayer-tvOSTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JellyfinAudioPlayer-tvOS.app/JellyfinAudioPlayer-tvOS";
|
||||
TVOS_DEPLOYMENT_TARGET = 10.1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -822,10 +582,12 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@@ -841,7 +603,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
@@ -850,6 +612,7 @@
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "Jellyfin Player";
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -882,10 +645,12 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
@@ -894,7 +659,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
@@ -902,6 +667,7 @@
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_NAME = "Jellyfin Player";
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -928,24 +694,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "JellyfinAudioPlayer-tvOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2D02E4971E0B4A5E006451C7 /* Debug */,
|
||||
2D02E4981E0B4A5E006451C7 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "JellyfinAudioPlayer-tvOSTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2D02E4991E0B4A5E006451C7 /* Debug */,
|
||||
2D02E49A1E0B4A5E006451C7 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "JellyfinAudioPlayer" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
|
||||
BuildableName = "JellyfinAudioPlayer-tvOS.app"
|
||||
BlueprintName = "JellyfinAudioPlayer-tvOS"
|
||||
ReferencedContainer = "container:JellyfinAudioPlayer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
|
||||
BuildableName = "JellyfinAudioPlayer-tvOSTests.xctest"
|
||||
BlueprintName = "JellyfinAudioPlayer-tvOSTests"
|
||||
ReferencedContainer = "container:JellyfinAudioPlayer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
|
||||
BuildableName = "JellyfinAudioPlayer-tvOS.app"
|
||||
BlueprintName = "JellyfinAudioPlayer-tvOS"
|
||||
ReferencedContainer = "container:JellyfinAudioPlayer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
|
||||
BuildableName = "JellyfinAudioPlayer-tvOS.app"
|
||||
BlueprintName = "JellyfinAudioPlayer-tvOS"
|
||||
ReferencedContainer = "container:JellyfinAudioPlayer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -36,7 +36,11 @@ static void InitializeFlipper(UIApplication *application) {
|
||||
moduleName:@"JellyfinAudioPlayer"
|
||||
initialProperties:nil];
|
||||
|
||||
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
|
||||
if (@available(iOS 13.0, *)) {
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
} else {
|
||||
rootView.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [UIViewController new];
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>7</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
@@ -42,7 +42,7 @@
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
<string>processing</string>
|
||||
<string>fetch</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
94
ios/Podfile
@@ -1,104 +1,24 @@
|
||||
platform :ios, '10.0'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
|
||||
def add_flipper_pods!(versions = {})
|
||||
versions['Flipper'] ||= '~> 0.33.1'
|
||||
versions['DoubleConversion'] ||= '1.1.7'
|
||||
versions['Flipper-Folly'] ||= '~> 2.1'
|
||||
versions['Flipper-Glog'] ||= '0.3.6'
|
||||
versions['Flipper-PeerTalk'] ||= '~> 0.0.4'
|
||||
versions['Flipper-RSocket'] ||= '~> 1.0'
|
||||
|
||||
pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug'
|
||||
|
||||
# List all transitive dependencies for FlipperKit pods
|
||||
# to avoid them being linked in Release builds
|
||||
pod 'Flipper', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug'
|
||||
pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug'
|
||||
pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug'
|
||||
pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug'
|
||||
pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug'
|
||||
pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
|
||||
end
|
||||
|
||||
# Post Install processing for Flipper
|
||||
def flipper_post_install(installer)
|
||||
installer.pods_project.targets.each do |target|
|
||||
if target.name == 'YogaKit'
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['SWIFT_VERSION'] = '4.1'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
|
||||
target 'JellyfinAudioPlayer' do
|
||||
# Pods for JellyfinAudioPlayer
|
||||
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
|
||||
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
|
||||
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
|
||||
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
|
||||
pod 'React', :path => '../node_modules/react-native/'
|
||||
pod 'React-Core', :path => '../node_modules/react-native/'
|
||||
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
|
||||
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
|
||||
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
|
||||
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
|
||||
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
|
||||
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
|
||||
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
|
||||
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
|
||||
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
|
||||
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
|
||||
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
|
||||
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
|
||||
|
||||
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
|
||||
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
|
||||
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
|
||||
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
|
||||
pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
|
||||
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
|
||||
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
|
||||
|
||||
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
|
||||
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
|
||||
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
||||
config = use_native_modules!
|
||||
use_react_native!(:path => config["reactNativePath"])
|
||||
|
||||
target 'JellyfinAudioPlayerTests' do
|
||||
inherit! :complete
|
||||
# Pods for testing
|
||||
end
|
||||
|
||||
use_native_modules!
|
||||
|
||||
# Enables Flipper.
|
||||
#
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||
# you should disable these next few lines.
|
||||
add_flipper_pods!
|
||||
use_flipper!
|
||||
post_install do |installer|
|
||||
flipper_post_install(installer)
|
||||
end
|
||||
end
|
||||
|
||||
target 'JellyfinAudioPlayer-tvOS' do
|
||||
# Pods for JellyfinAudioPlayer-tvOS
|
||||
|
||||
target 'JellyfinAudioPlayer-tvOSTests' do
|
||||
inherit! :search_paths
|
||||
# Pods for testing
|
||||
installer.pods_project.build_configurations.each do |config|
|
||||
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
|
||||
end
|
||||
end
|
||||
end
|
||||
609
ios/Podfile.lock
@@ -3,66 +3,66 @@ PODS:
|
||||
- CocoaAsyncSocket (7.6.4)
|
||||
- CocoaLibEvent (1.0.0)
|
||||
- DoubleConversion (1.1.6)
|
||||
- FBLazyVector (0.62.2)
|
||||
- FBReactNativeSpec (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTRequired (= 0.62.2)
|
||||
- RCTTypeSafety (= 0.62.2)
|
||||
- React-Core (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- Flipper (0.33.1):
|
||||
- Flipper-Folly (~> 2.1)
|
||||
- Flipper-RSocket (~> 1.0)
|
||||
- FBLazyVector (0.63.4)
|
||||
- FBReactNativeSpec (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.63.4)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- Flipper (0.54.0):
|
||||
- Flipper-Folly (~> 2.2)
|
||||
- Flipper-RSocket (~> 1.1)
|
||||
- Flipper-DoubleConversion (1.1.7)
|
||||
- Flipper-Folly (2.2.0):
|
||||
- Flipper-Folly (2.3.0):
|
||||
- boost-for-react-native
|
||||
- CocoaLibEvent (~> 1.0)
|
||||
- Flipper-DoubleConversion
|
||||
- Flipper-Glog
|
||||
- OpenSSL-Universal (= 1.0.2.19)
|
||||
- OpenSSL-Universal (= 1.0.2.20)
|
||||
- Flipper-Glog (0.3.6)
|
||||
- Flipper-PeerTalk (0.0.4)
|
||||
- Flipper-RSocket (1.1.0):
|
||||
- Flipper-Folly (~> 2.2)
|
||||
- FlipperKit (0.33.1):
|
||||
- FlipperKit/Core (= 0.33.1)
|
||||
- FlipperKit/Core (0.33.1):
|
||||
- Flipper (~> 0.33.1)
|
||||
- FlipperKit (0.54.0):
|
||||
- FlipperKit/Core (= 0.54.0)
|
||||
- FlipperKit/Core (0.54.0):
|
||||
- Flipper (~> 0.54.0)
|
||||
- FlipperKit/CppBridge
|
||||
- FlipperKit/FBCxxFollyDynamicConvert
|
||||
- FlipperKit/FBDefines
|
||||
- FlipperKit/FKPortForwarding
|
||||
- FlipperKit/CppBridge (0.33.1):
|
||||
- Flipper (~> 0.33.1)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.33.1):
|
||||
- Flipper-Folly (~> 2.1)
|
||||
- FlipperKit/FBDefines (0.33.1)
|
||||
- FlipperKit/FKPortForwarding (0.33.1):
|
||||
- FlipperKit/CppBridge (0.54.0):
|
||||
- Flipper (~> 0.54.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
|
||||
- Flipper-Folly (~> 2.2)
|
||||
- FlipperKit/FBDefines (0.54.0)
|
||||
- FlipperKit/FKPortForwarding (0.54.0):
|
||||
- CocoaAsyncSocket (~> 7.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.33.1)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.33.1):
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.54.0)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||
- YogaKit (~> 1.18)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.33.1)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.33.1):
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitReactPlugin (0.33.1):
|
||||
- FlipperKit/FlipperKitReactPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.33.1):
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.33.1):
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitNetworkPlugin
|
||||
- Folly (2018.10.22.00):
|
||||
- Folly (2020.01.13.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- Folly/Default (= 2018.10.22.00)
|
||||
- Folly/Default (= 2020.01.13.00)
|
||||
- glog
|
||||
- Folly/Default (2018.10.22.00):
|
||||
- Folly/Default (2020.01.13.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- glog
|
||||
@@ -76,263 +76,277 @@ PODS:
|
||||
- libwebp/mux (1.1.0):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.1.0)
|
||||
- OpenSSL-Universal (1.0.2.19):
|
||||
- OpenSSL-Universal/Static (= 1.0.2.19)
|
||||
- OpenSSL-Universal/Static (1.0.2.19)
|
||||
- RCTRequired (0.62.2)
|
||||
- RCTTypeSafety (0.62.2):
|
||||
- FBLazyVector (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTRequired (= 0.62.2)
|
||||
- React-Core (= 0.62.2)
|
||||
- React (0.62.2):
|
||||
- React-Core (= 0.62.2)
|
||||
- React-Core/DevSupport (= 0.62.2)
|
||||
- React-Core/RCTWebSocket (= 0.62.2)
|
||||
- React-RCTActionSheet (= 0.62.2)
|
||||
- React-RCTAnimation (= 0.62.2)
|
||||
- React-RCTBlob (= 0.62.2)
|
||||
- React-RCTImage (= 0.62.2)
|
||||
- React-RCTLinking (= 0.62.2)
|
||||
- React-RCTNetwork (= 0.62.2)
|
||||
- React-RCTSettings (= 0.62.2)
|
||||
- React-RCTText (= 0.62.2)
|
||||
- React-RCTVibration (= 0.62.2)
|
||||
- React-Core (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- OpenSSL-Universal (1.0.2.20):
|
||||
- OpenSSL-Universal/Static (= 1.0.2.20)
|
||||
- OpenSSL-Universal/Static (1.0.2.20)
|
||||
- RCTRequired (0.63.4)
|
||||
- RCTTypeSafety (0.63.4):
|
||||
- FBLazyVector (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.63.4)
|
||||
- React-Core (= 0.63.4)
|
||||
- React (0.63.4):
|
||||
- React-Core (= 0.63.4)
|
||||
- React-Core/DevSupport (= 0.63.4)
|
||||
- React-Core/RCTWebSocket (= 0.63.4)
|
||||
- React-RCTActionSheet (= 0.63.4)
|
||||
- React-RCTAnimation (= 0.63.4)
|
||||
- React-RCTBlob (= 0.63.4)
|
||||
- React-RCTImage (= 0.63.4)
|
||||
- React-RCTLinking (= 0.63.4)
|
||||
- React-RCTNetwork (= 0.63.4)
|
||||
- React-RCTSettings (= 0.63.4)
|
||||
- React-RCTText (= 0.63.4)
|
||||
- React-RCTVibration (= 0.63.4)
|
||||
- React-callinvoker (0.63.4)
|
||||
- React-Core (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default (= 0.62.2)
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-Core/Default (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/CoreModulesHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/CoreModulesHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/Default (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/Default (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/DevSupport (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/DevSupport (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default (= 0.62.2)
|
||||
- React-Core/RCTWebSocket (= 0.62.2)
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-jsinspector (= 0.62.2)
|
||||
- React-Core/Default (= 0.63.4)
|
||||
- React-Core/RCTWebSocket (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-jsinspector (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTActionSheetHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTActionSheetHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTAnimationHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTAnimationHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTBlobHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTBlobHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTImageHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTImageHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTLinkingHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTLinkingHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTNetworkHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTNetworkHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTSettingsHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTSettingsHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTTextHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTTextHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTVibrationHeaders (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTVibrationHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTWebSocket (0.62.2):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTWebSocket (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default (= 0.62.2)
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsiexecutor (= 0.62.2)
|
||||
- React-Core/Default (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-CoreModules (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTTypeSafety (= 0.62.2)
|
||||
- React-Core/CoreModulesHeaders (= 0.62.2)
|
||||
- React-RCTImage (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-cxxreact (0.62.2):
|
||||
- React-CoreModules (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/CoreModulesHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-RCTImage (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-cxxreact (0.63.4):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsinspector (= 0.62.2)
|
||||
- React-jsi (0.62.2):
|
||||
- React-callinvoker (= 0.63.4)
|
||||
- React-jsinspector (= 0.63.4)
|
||||
- React-jsi (0.63.4):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsi/Default (= 0.62.2)
|
||||
- React-jsi/Default (0.62.2):
|
||||
- React-jsi/Default (= 0.63.4)
|
||||
- React-jsi/Default (0.63.4):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsiexecutor (0.62.2):
|
||||
- React-jsiexecutor (0.63.4):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-jsinspector (0.62.2)
|
||||
- react-native-safe-area-context (3.0.7):
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsinspector (0.63.4)
|
||||
- react-native-appearance (0.3.4):
|
||||
- React
|
||||
- react-native-slider (3.0.0):
|
||||
- react-native-safe-area-context (3.1.9):
|
||||
- React-Core
|
||||
- react-native-slider (3.0.3):
|
||||
- React
|
||||
- react-native-track-player (1.2.3):
|
||||
- React
|
||||
- react-native-webview (10.3.2):
|
||||
- React
|
||||
- React-RCTActionSheet (0.62.2):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.62.2)
|
||||
- React-RCTAnimation (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTTypeSafety (= 0.62.2)
|
||||
- React-Core/RCTAnimationHeaders (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-RCTBlob (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTBlobHeaders (= 0.62.2)
|
||||
- React-Core/RCTWebSocket (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- React-RCTNetwork (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-RCTImage (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTTypeSafety (= 0.62.2)
|
||||
- React-Core/RCTImageHeaders (= 0.62.2)
|
||||
- React-RCTNetwork (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-RCTLinking (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- React-Core/RCTLinkingHeaders (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-RCTNetwork (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTTypeSafety (= 0.62.2)
|
||||
- React-Core/RCTNetworkHeaders (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-RCTSettings (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- RCTTypeSafety (= 0.62.2)
|
||||
- React-Core/RCTSettingsHeaders (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- React-RCTText (0.62.2):
|
||||
- React-Core/RCTTextHeaders (= 0.62.2)
|
||||
- React-RCTVibration (0.62.2):
|
||||
- FBReactNativeSpec (= 0.62.2)
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React-Core/RCTVibrationHeaders (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
||||
- ReactCommon/callinvoker (0.62.2):
|
||||
- react-native-webview (11.2.1):
|
||||
- React-Core
|
||||
- React-RCTActionSheet (0.63.4):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.63.4)
|
||||
- React-RCTAnimation (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTAnimationHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTBlob (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTBlobHeaders (= 0.63.4)
|
||||
- React-Core/RCTWebSocket (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-RCTNetwork (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTImage (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTImageHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-RCTNetwork (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTLinking (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- React-Core/RCTLinkingHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTNetwork (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTNetworkHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTSettings (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTSettingsHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTText (0.63.4):
|
||||
- React-Core/RCTTextHeaders (= 0.63.4)
|
||||
- React-RCTVibration (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTVibrationHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (0.63.4):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- ReactCommon/turbomodule/core (0.62.2):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React-Core (= 0.62.2)
|
||||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- ReactCommon/callinvoker (= 0.62.2)
|
||||
- RNCAsyncStorage (1.11.0):
|
||||
- React
|
||||
- React-callinvoker (= 0.63.4)
|
||||
- React-Core (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- RNCAsyncStorage (1.12.1):
|
||||
- React-Core
|
||||
- RNCMaskedView (0.1.10):
|
||||
- React
|
||||
- RNCPicker (1.6.5):
|
||||
- React
|
||||
- RNFastImage (8.1.5):
|
||||
- React
|
||||
- SDWebImage (~> 5.0)
|
||||
- SDWebImageWebPCoder (~> 0.4.1)
|
||||
- RNGestureHandler (1.6.1):
|
||||
- React
|
||||
- RNReanimated (1.9.0):
|
||||
- React
|
||||
- RNScreens (2.9.0):
|
||||
- React
|
||||
- RNCPicker (1.8.1):
|
||||
- React-Core
|
||||
- RNFastImage (8.3.4):
|
||||
- React-Core
|
||||
- SDWebImage (~> 5.8)
|
||||
- SDWebImageWebPCoder (~> 0.6.1)
|
||||
- RNGestureHandler (1.9.0):
|
||||
- React-Core
|
||||
- RNLocalize (2.0.1):
|
||||
- React-Core
|
||||
- RNReanimated (1.13.2):
|
||||
- React-Core
|
||||
- RNScreens (2.17.1):
|
||||
- React-Core
|
||||
- RNSentry (2.2.0):
|
||||
- React-Core
|
||||
- Sentry (= 6.1.4)
|
||||
- RNSVG (12.1.0):
|
||||
- React
|
||||
- SDWebImage (5.8.1):
|
||||
- SDWebImage/Core (= 5.8.1)
|
||||
- SDWebImage/Core (5.8.1)
|
||||
- SDWebImageWebPCoder (0.4.1):
|
||||
- SDWebImage (5.10.4):
|
||||
- SDWebImage/Core (= 5.10.4)
|
||||
- SDWebImage/Core (5.10.4)
|
||||
- SDWebImageWebPCoder (0.6.1):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.5)
|
||||
- SDWebImage/Core (~> 5.7)
|
||||
- Sentry (6.1.4):
|
||||
- Sentry/Core (= 6.1.4)
|
||||
- Sentry/Core (6.1.4)
|
||||
- Yoga (1.14.0)
|
||||
- YogaKit (1.18.1):
|
||||
- Yoga (~> 1.14)
|
||||
@@ -341,30 +355,31 @@ DEPENDENCIES:
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
|
||||
- Flipper (~> 0.33.1)
|
||||
- Flipper (~> 0.54.0)
|
||||
- Flipper-DoubleConversion (= 1.1.7)
|
||||
- Flipper-Folly (~> 2.1)
|
||||
- Flipper-Folly (~> 2.2)
|
||||
- Flipper-Glog (= 0.3.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- Flipper-RSocket (~> 1.0)
|
||||
- FlipperKit (~> 0.33.1)
|
||||
- FlipperKit/Core (~> 0.33.1)
|
||||
- FlipperKit/CppBridge (~> 0.33.1)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.33.1)
|
||||
- FlipperKit/FBDefines (~> 0.33.1)
|
||||
- FlipperKit/FKPortForwarding (~> 0.33.1)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (~> 0.33.1)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (~> 0.33.1)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.33.1)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (~> 0.33.1)
|
||||
- FlipperKit/FlipperKitReactPlugin (~> 0.33.1)
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.33.1)
|
||||
- FlipperKit/SKIOSNetworkPlugin (~> 0.33.1)
|
||||
- Flipper-RSocket (~> 1.1)
|
||||
- FlipperKit (~> 0.54.0)
|
||||
- FlipperKit/Core (~> 0.54.0)
|
||||
- FlipperKit/CppBridge (~> 0.54.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0)
|
||||
- FlipperKit/FBDefines (~> 0.54.0)
|
||||
- FlipperKit/FKPortForwarding (~> 0.54.0)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0)
|
||||
- FlipperKit/FlipperKitReactPlugin (~> 0.54.0)
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0)
|
||||
- FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
|
||||
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||
- React (from `../node_modules/react-native/`)
|
||||
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
|
||||
- React-Core (from `../node_modules/react-native/`)
|
||||
- React-Core/DevSupport (from `../node_modules/react-native/`)
|
||||
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
|
||||
@@ -373,6 +388,7 @@ DEPENDENCIES:
|
||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-appearance (from `../node_modules/react-native-appearance`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- react-native-track-player (from `../node_modules/react-native-track-player`)
|
||||
@@ -386,15 +402,16 @@ DEPENDENCIES:
|
||||
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
|
||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
|
||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
|
||||
- "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)"
|
||||
- "RNCPicker (from `../node_modules/@react-native-community/picker`)"
|
||||
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNLocalize (from `../node_modules/react-native-localize`)
|
||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
- "RNSentry (from `../node_modules/@sentry/react-native`)"
|
||||
- RNSVG (from `../node_modules/react-native-svg`)
|
||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
@@ -414,6 +431,7 @@ SPEC REPOS:
|
||||
- OpenSSL-Universal
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
- YogaKit
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -433,6 +451,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/Libraries/TypeSafety"
|
||||
React:
|
||||
:path: "../node_modules/react-native/"
|
||||
React-callinvoker:
|
||||
:path: "../node_modules/react-native/ReactCommon/callinvoker"
|
||||
React-Core:
|
||||
:path: "../node_modules/react-native/"
|
||||
React-CoreModules:
|
||||
@@ -445,6 +465,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
React-jsinspector:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
react-native-appearance:
|
||||
:path: "../node_modules/react-native-appearance"
|
||||
react-native-safe-area-context:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-slider:
|
||||
@@ -483,10 +505,14 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-fast-image"
|
||||
RNGestureHandler:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNLocalize:
|
||||
:path: "../node_modules/react-native-localize"
|
||||
RNReanimated:
|
||||
:path: "../node_modules/react-native-reanimated"
|
||||
RNScreens:
|
||||
:path: "../node_modules/react-native-screens"
|
||||
RNSentry:
|
||||
:path: "../node_modules/@sentry/react-native"
|
||||
RNSVG:
|
||||
:path: "../node_modules/react-native-svg"
|
||||
Yoga:
|
||||
@@ -496,56 +522,61 @@ SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
|
||||
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
|
||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||
FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
|
||||
FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
|
||||
Flipper: 6c1f484f9a88d30ab3e272800d53688439e50f69
|
||||
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
||||
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
|
||||
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
|
||||
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
|
||||
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
|
||||
Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a
|
||||
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
|
||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
|
||||
FlipperKit: 6dc9b8f4ef60d9e5ded7f0264db299c91f18832e
|
||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
|
||||
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
|
||||
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
|
||||
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
|
||||
RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
|
||||
RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
|
||||
React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
|
||||
React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05
|
||||
React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0
|
||||
React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103
|
||||
React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
|
||||
React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
|
||||
React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
|
||||
react-native-safe-area-context: c39fc20a20cd66ebf1d56c6f8b8711142fbfee98
|
||||
react-native-slider: 05f11678260cb27c3d00a2dd1558b623be3ec8d2
|
||||
OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
|
||||
RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e
|
||||
RCTTypeSafety: 8c9c544ecbf20337d069e4ae7fd9a377aadf504b
|
||||
React: b0a957a2c44da4113b0c4c9853d8387f8e64e615
|
||||
React-callinvoker: c3f44dd3cb195b6aa46621fff95ded79d59043fe
|
||||
React-Core: d3b2a1ac9a2c13c3bcde712d9281fc1c8a5b315b
|
||||
React-CoreModules: 0581ff36cb797da0943d424f69e7098e43e9be60
|
||||
React-cxxreact: c1480d4fda5720086c90df537ee7d285d4c57ac3
|
||||
React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31
|
||||
React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949
|
||||
React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a
|
||||
react-native-appearance: fc2014516054585d531e07aa0b40ab0de1d2be85
|
||||
react-native-safe-area-context: 86612d2c9a9e94e288319262d10b5f93f0b395f5
|
||||
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
||||
react-native-track-player: ba2416753b58f3cdf9db5a07daa65876d659f925
|
||||
react-native-webview: e2c0bce9a1a7c7edd4eb30f0c3016fce216245ce
|
||||
React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
|
||||
React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
|
||||
React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
|
||||
React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3
|
||||
React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2
|
||||
React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44
|
||||
React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a
|
||||
React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
|
||||
React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
|
||||
ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
|
||||
RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
|
||||
react-native-webview: dbe6c1ad149740f0e2d84a963f1d3c3e77f2d99c
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
||||
React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0
|
||||
React-RCTImage: c1b1f2d3f43a4a528c8946d6092384b5c880d2f0
|
||||
React-RCTLinking: 35ae4ab9dc0410d1fcbdce4d7623194a27214fb2
|
||||
React-RCTNetwork: 29ec2696f8d8cfff7331fac83d3e893c95ef43ae
|
||||
React-RCTSettings: 60f0691bba2074ef394f95d4c2265ec284e0a46a
|
||||
React-RCTText: 5c51df3f08cb9dedc6e790161195d12bac06101c
|
||||
React-RCTVibration: ae4f914cfe8de7d4de95ae1ea6cc8f6315d73d9d
|
||||
ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b
|
||||
RNCAsyncStorage: cb9a623793918c6699586281f0b51cbc38f046f9
|
||||
RNCMaskedView: f5c7d14d6847b7b44853f7acb6284c1da30a3459
|
||||
RNCPicker: 55b9b4240d0a9eba8733d02616775d4040de2e7d
|
||||
RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9
|
||||
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
|
||||
RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
|
||||
RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
|
||||
RNCPicker: 914b557e20b3b8317b084aca9ff4b4edb95f61e4
|
||||
RNFastImage: d4870d58f5936111c56218dbd7fcfc18e65b58ff
|
||||
RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b
|
||||
RNLocalize: dcf0fdb332b37b0d24178e876a7ce4dbbc9c838d
|
||||
RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad
|
||||
RNScreens: b6c9607e6fe47c1b6e2f1910d2acd46dd7ecea3a
|
||||
RNSentry: 6aeba1adc242fd22a6826acae92f430697b47a9c
|
||||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||
SDWebImage: e3eae2eda88578db0685a0c88597fdadd9433f05
|
||||
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
|
||||
Yoga: 3ebccbdd559724312790e7742142d062476b698e
|
||||
SDWebImage: c666b97e1fa9c64b4909816a903322018f0a9c84
|
||||
SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
|
||||
Sentry: 9d055e2de30a77685e86b219acf02e59b82091fc
|
||||
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 34db80daea4bf9020013707ff198a8e43d5808ec
|
||||
PODFILE CHECKSUM: d99c1202f98b3f7477b6a86c9226010b36143469
|
||||
|
||||
COCOAPODS: 1.9.1
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
10592
package-lock.json
generated
88
package.json
@@ -1,66 +1,72 @@
|
||||
{
|
||||
"name": "JellyfinAudioPlayer",
|
||||
"version": "0.0.1",
|
||||
"main": "src/index.ts",
|
||||
"main": "src/index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"ios": "react-native run-ios --scheme \"Jellyfin Player\"",
|
||||
"start": "react-native start",
|
||||
"test": "jest",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||
"build:ios": "react-native bundle --entry-file='index.ts' --bundle-output='./ios/main.jsbundle' --dev=false --platform='ios'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/async-storage": "^1.11.0",
|
||||
"@react-native-community/async-storage": "^1.12.1",
|
||||
"@react-native-community/masked-view": "^0.1.10",
|
||||
"@react-native-community/picker": "^1.6.5",
|
||||
"@react-native-community/slider": "^3.0.0",
|
||||
"@react-navigation/bottom-tabs": "^5.6.1",
|
||||
"@react-navigation/native": "^5.6.1",
|
||||
"@react-navigation/stack": "^5.6.2",
|
||||
"@reduxjs/toolkit": "^1.4.0",
|
||||
"@types/lodash": "^4.14.157",
|
||||
"@react-native-community/picker": "^1.8.1",
|
||||
"@react-native-community/slider": "^3.0.3",
|
||||
"@react-navigation/bottom-tabs": "^5.11.7",
|
||||
"@react-navigation/native": "^5.9.2",
|
||||
"@react-navigation/stack": "^5.14.2",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@sentry/react-native": "^2.2.0",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/redux-logger": "^3.0.8",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"date-fns": "^2.14.0",
|
||||
"fuse.js": "^6.4.0",
|
||||
"lodash": "^4.17.15",
|
||||
"react": "16.11.0",
|
||||
"react-native": "0.62.2",
|
||||
"react-native-fast-image": "^8.1.5",
|
||||
"react-native-gesture-handler": "^1.6.1",
|
||||
"react-native-reanimated": "^1.9.0",
|
||||
"react-native-safe-area-context": "^3.0.7",
|
||||
"react-native-screens": "^2.9.0",
|
||||
"@types/styled-components": "^5.1.7",
|
||||
"date-fns": "^2.17.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"i18n-js": "^3.8.0",
|
||||
"lodash": "^4.17.20",
|
||||
"react": "^17.0.1",
|
||||
"react-native": "^0.63.4",
|
||||
"react-native-appearance": "^0.3.4",
|
||||
"react-native-dotenv": "^2.5.1",
|
||||
"react-native-fast-image": "^8.3.4",
|
||||
"react-native-gesture-handler": "^1.9.0",
|
||||
"react-native-localize": "^2.0.1",
|
||||
"react-native-reanimated": "^1.13.2",
|
||||
"react-native-safe-area-context": "^3.1.9",
|
||||
"react-native-screens": "^2.17.1",
|
||||
"react-native-svg": "^12.1.0",
|
||||
"react-native-svg-transformer": "^0.14.3",
|
||||
"react-native-track-player": "github:leinelissen/react-native-track-player",
|
||||
"react-native-webview": "^10.3.2",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-native-webview": "^11.2.1",
|
||||
"react-redux": "^7.2.2",
|
||||
"redux": "^4.0.5",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-persist": "^6.0.0",
|
||||
"styled-components": "^5.1.1"
|
||||
"styled-components": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.4",
|
||||
"@babel/runtime": "^7.10.4",
|
||||
"@react-native-community/eslint-config": "^1.0.0",
|
||||
"@types/jest": "^24.0.24",
|
||||
"@types/react-native": "^0.62.17",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/react-test-renderer": "16.9.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.27.0",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"eslint": "^6.5.1",
|
||||
"eslint-plugin-react-hooks": "^4.0.5",
|
||||
"jest": "^24.9.0",
|
||||
"metro-react-native-babel-preset": "^0.58.0",
|
||||
"react-test-renderer": "16.11.0",
|
||||
"typescript": "^3.9.6"
|
||||
"@babel/core": "^7.12.13",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@react-native-community/eslint-config": "^2.0.0",
|
||||
"@types/i18n-js": "^3.8.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/react-native": "^0.63.48",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
"@types/react-test-renderer": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.14.2",
|
||||
"@typescript-eslint/parser": "^4.14.2",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^26.6.3",
|
||||
"metro-react-native-babel-preset": "^0.65.0",
|
||||
"react-test-renderer": "^17.0.1",
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native",
|
||||
|
||||
BIN
src/assets/app-icon-white.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
1
src/assets/repeat.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 106.69 89.45"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Regular-M"><path d="M0,41.16a5.37,5.37,0,0,0,10.74,0V37.94c0-7.76,5.37-12.89,13.38-12.89H60.94v10.2a4.05,4.05,0,0,0,4.39,4.3,5.27,5.27,0,0,0,3.32-1.22L87.11,23.1a4.1,4.1,0,0,0,0-6.55L68.65,1.22A5.57,5.57,0,0,0,65.33,0a4.09,4.09,0,0,0-4.39,4.35v10.1H25.15C9.77,14.45,0,23.29,0,37.16ZM45.75,54.25A4,4,0,0,0,41.36,50a5.25,5.25,0,0,0-3.27,1.17L19.63,66.36a4,4,0,0,0,0,6.54L38.09,88.23a5,5,0,0,0,3.27,1.22,4,4,0,0,0,4.39-4.29V75H81.54c15.38,0,25.15-8.89,25.15-22.7v-4a5.37,5.37,0,1,0-10.74,0v3.22c0,7.72-5.37,12.9-13.38,12.9H45.75Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 695 B |
1
src/assets/shuffle.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 114.69 90.97"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Symbols"><g id="Regular-M"><path d="M0,70.51c0,3.12,2.29,5.27,5.76,5.27H16.85c8.44,0,13.47-2.44,19.58-9.52L47.36,53.47l11,12.79c6,7.08,11.08,9.57,19.58,9.57h9V86.67A4,4,0,0,0,91.26,91a5.27,5.27,0,0,0,3.32-1.22L113,74.51A4.08,4.08,0,0,0,113,68L94.58,52.64a5.27,5.27,0,0,0-3.32-1.22,4,4,0,0,0-4.35,4.29v9.48H78.22c-5.08,0-8.2-1.62-12.16-6.25L54.39,45.31,66.06,31.74c4-4.69,7.08-6.3,12.16-6.3h8.69v9.81a4,4,0,0,0,4.35,4.3,5.27,5.27,0,0,0,3.32-1.22L113,23.1a4.09,4.09,0,0,0,0-6.55L94.58,1.22A5.27,5.27,0,0,0,91.26,0a4,4,0,0,0-4.35,4.3V14.84h-9c-8.5,0-13.57,2.49-19.58,9.57l-11,12.8L36.43,24.41c-6.11-7.08-11.14-9.57-19.58-9.57H5.76C2.29,14.84,0,17,0,20.17s2.34,5.32,5.76,5.32H17.09c4.69,0,7.67,1.61,11.67,6.25L40.43,45.31,28.76,58.94c-4.05,4.63-7,6.25-11.67,6.25H5.76C2.34,65.19,0,67.38,0,70.51Z"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 964 B |
@@ -2,19 +2,27 @@ import React, { Component } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { AppearanceProvider, Appearance, AppearanceListener } from 'react-native-appearance';
|
||||
import Routes from '../screens';
|
||||
import store, { persistedStore } from 'store';
|
||||
import {
|
||||
NavigationContainer,
|
||||
DefaultTheme,
|
||||
DarkTheme,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
interface State {
|
||||
isReady: boolean;
|
||||
colorScheme?: string;
|
||||
}
|
||||
|
||||
export default class App extends Component<State> {
|
||||
state = {
|
||||
isReady: false
|
||||
export default class App extends Component<{}, State> {
|
||||
state: State = {
|
||||
isReady: false,
|
||||
};
|
||||
|
||||
subscription = null;
|
||||
|
||||
async componentDidMount() {
|
||||
await TrackPlayer.setupPlayer();
|
||||
await TrackPlayer.updateOptions({
|
||||
@@ -27,11 +35,20 @@ export default class App extends Component<State> {
|
||||
TrackPlayer.CAPABILITY_SEEK_TO,
|
||||
]
|
||||
});
|
||||
this.setState({ isReady: true });
|
||||
this.subscription = Appearance.addChangeListener(this.setScheme);
|
||||
this.setState({ isReady: true, colorScheme: Appearance.getColorScheme() });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription?.remove();
|
||||
}
|
||||
|
||||
setScheme: AppearanceListener = ({ colorScheme }) => {
|
||||
this.setState({ colorScheme });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isReady } = this.state;
|
||||
const { isReady, colorScheme } = this.state;
|
||||
|
||||
if (!isReady) {
|
||||
return null;
|
||||
@@ -40,9 +57,11 @@ export default class App extends Component<State> {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistedStore}>
|
||||
<NavigationContainer>
|
||||
<Routes />
|
||||
</NavigationContainer>
|
||||
<AppearanceProvider>
|
||||
<NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<Routes />
|
||||
</NavigationContainer>
|
||||
</AppearanceProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
26
src/components/Colors.android.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { StyleSheet, PlatformColor } from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
|
||||
export const colors = StyleSheet.create({
|
||||
text: {
|
||||
color: PlatformColor('?attr/colorOnBackground'),
|
||||
},
|
||||
view: {
|
||||
backgroundColor: PlatformColor('?android:colorBackground'),
|
||||
},
|
||||
border: {
|
||||
borderColor: '#88888844'
|
||||
},
|
||||
activeBackground: {
|
||||
backgroundColor: `${THEME_COLOR}44`,
|
||||
},
|
||||
imageBackground: {
|
||||
backgroundColor: PlatformColor('?attr/colorBackgroundFloating'),
|
||||
},
|
||||
modal: {
|
||||
backgroundColor: PlatformColor('?attr/colorBackgroundFloating'),
|
||||
},
|
||||
input: {
|
||||
backgroundColor: PlatformColor('?attr/colorBackgroundFloating'),
|
||||
}
|
||||
});
|
||||
27
src/components/Colors.ios.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { StyleSheet, PlatformColor, DynamicColorIOS } from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
|
||||
export const colors = StyleSheet.create({
|
||||
text: {
|
||||
color: PlatformColor('label'),
|
||||
},
|
||||
view: {
|
||||
backgroundColor: PlatformColor('systemBackground'),
|
||||
},
|
||||
border: {
|
||||
borderColor: PlatformColor('systemGray5Color'),
|
||||
},
|
||||
activeBackground: {
|
||||
backgroundColor: DynamicColorIOS({ light: `${THEME_COLOR}16`, dark: `${THEME_COLOR}66` })
|
||||
},
|
||||
imageBackground: {
|
||||
backgroundColor: PlatformColor('systemGray5Color')
|
||||
},
|
||||
modal: {
|
||||
backgroundColor: DynamicColorIOS({ light: '#eeeeeeee', dark: '#222222ee' })
|
||||
},
|
||||
input: {
|
||||
backgroundColor: PlatformColor('systemGray5Color'),
|
||||
color: PlatformColor('label'),
|
||||
}
|
||||
});
|
||||
@@ -2,7 +2,6 @@ import styled from 'styled-components/native';
|
||||
|
||||
const Input = styled.TextInput`
|
||||
margin: 10px 0;
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
`;
|
||||
|
||||
@@ -3,20 +3,20 @@ import { TouchableOpacityProps, Text } from 'react-native';
|
||||
import ChevronRight from 'assets/chevron-right.svg';
|
||||
import styled from 'styled-components/native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { colors } from './Colors';
|
||||
|
||||
const BUTTON_SIZE = 14;
|
||||
|
||||
const Container = styled.TouchableOpacity`
|
||||
padding: 18px 0;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: #eee;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const ListButton: React.FC<TouchableOpacityProps> = ({ children, ...props }) => {
|
||||
return (
|
||||
<Container {...props}>
|
||||
<Container {...props} style={colors.border}>
|
||||
<Text style={{ color: THEME_COLOR, fontSize: 16 }}>{children}</Text>
|
||||
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={THEME_COLOR} />
|
||||
</Container>
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components/native';
|
||||
import { SafeAreaView } from 'react-native';
|
||||
import React, { useCallback } from 'react';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import { SafeAreaView, Pressable } from 'react-native';
|
||||
import { colors } from './Colors';
|
||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
|
||||
const Background = styled.View`
|
||||
background-color: #eeeeeeee;
|
||||
interface Props {
|
||||
fullSize?: boolean;
|
||||
}
|
||||
|
||||
const Background = styled(Pressable)`
|
||||
padding: 100px 25px;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const Container = styled.View`
|
||||
background-color: white;
|
||||
const Container = styled(Pressable)<Pick<Props, 'fullSize'>>`
|
||||
border-radius: 20px;
|
||||
flex: 1;
|
||||
margin: auto 0;
|
||||
|
||||
${props => props.fullSize && css`
|
||||
flex: 1;
|
||||
`}
|
||||
`;
|
||||
|
||||
const Modal: React.FC = ({ children }) => {
|
||||
const Modal: React.FC<Props> = ({ children, fullSize = true }) => {
|
||||
const navigation = useNavigation();
|
||||
const closeModal = useCallback(() => {
|
||||
navigation.dispatch(StackActions.popToTop());
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<Background>
|
||||
<SafeAreaView style={{ flex: 1}}>
|
||||
<Container>
|
||||
<Background style={colors.modal} onPress={closeModal}>
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Container style={colors.view} fullSize={fullSize}>
|
||||
{children}
|
||||
</Container>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { Pressable, ViewStyle } from 'react-native';
|
||||
|
||||
interface TouchableHandlerProps {
|
||||
id: string;
|
||||
onPress: (id: string) => void;
|
||||
onLongPress?: (id: string) => void;
|
||||
}
|
||||
|
||||
function TouchableStyles({ pressed }: { pressed: boolean }): ViewStyle {
|
||||
if (pressed) {
|
||||
return { opacity: 0.5 };
|
||||
} else {
|
||||
return { opacity: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a generic handler that accepts id as a prop, and return it when it is
|
||||
* pressed. This comes in handy with lists in which albums / tracks need to be selected.
|
||||
*/
|
||||
const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, children }) => {
|
||||
const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, onLongPress, children }) => {
|
||||
const handlePress = useCallback(() => {
|
||||
return onPress(id);
|
||||
}, [id, onPress]);
|
||||
|
||||
const handleLongPress = useCallback(() => {
|
||||
return onLongPress ? onLongPress(id) : undefined;
|
||||
}, [id, onLongPress]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<Pressable
|
||||
onPress={handlePress}
|
||||
onLongPress={handleLongPress}
|
||||
style={TouchableStyles}
|
||||
>
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,4 +4,9 @@ export const Header = styled.Text`
|
||||
margin: 24px 0 12px 0;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
export const SubHeader = styled.Text`
|
||||
font-size: 24px;
|
||||
margin: 12px 0;
|
||||
`;
|
||||
45
src/localisation/index.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import i18n from 'i18n-js';
|
||||
import { findBestAvailableLanguage } from 'react-native-localize';
|
||||
import { LocaleKeys } from './types';
|
||||
|
||||
// Lazy loaders for locale
|
||||
const localeGetters: Record<string, () => object> = {
|
||||
en: () => require('./lang/en/locale.json'),
|
||||
fr: () => require('./lang/fr/locale.json'),
|
||||
nl: () => require('./lang/nl/locale.json'),
|
||||
es: () => require('./lang/es/locale.json'),
|
||||
};
|
||||
|
||||
// Have RNLocalize pick the best locale from the languages on offer
|
||||
const locale = findBestAvailableLanguage(Object.keys(localeGetters));
|
||||
|
||||
// Check if the locale is correctly picked
|
||||
if (!locale || !locale.languageTag) {
|
||||
throw new Error('Invalid locale selected');
|
||||
}
|
||||
|
||||
// Set the key-value pairs for the different languages you want to support.
|
||||
i18n.translations = {
|
||||
en: localeGetters.en(),
|
||||
};
|
||||
|
||||
// If the locale is not english, we add it to the translations key s well
|
||||
if (locale.languageTag !== 'en') {
|
||||
i18n.translations[locale.languageTag] = localeGetters[locale.languageTag as string]();
|
||||
}
|
||||
|
||||
// Set the locale once at the beginning of your app.
|
||||
i18n.locale = locale.languageTag;
|
||||
|
||||
// Fallback to the default language for missing translation strings
|
||||
i18n.fallbacks = true;
|
||||
|
||||
/**
|
||||
* An i18n Typescript helper with autocomplete for the key argument
|
||||
* @param key string
|
||||
*/
|
||||
export function t(key: LocaleKeys) {
|
||||
return i18n.t(key);
|
||||
}
|
||||
|
||||
export default i18n;
|
||||
28
src/localisation/lang/en/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Play Next",
|
||||
"play-album": "Play Album",
|
||||
"queue": "Queue",
|
||||
"add-to-queue": "Add to Queue",
|
||||
"clear-queue": "Clear Queue",
|
||||
"no-results": "No results...",
|
||||
"album": "Album",
|
||||
"albums": "Albums",
|
||||
"all-albums": "All Albums",
|
||||
"search": "Search",
|
||||
"music": "Music",
|
||||
"now-playing": "Now Playing",
|
||||
"onboarding-welcome": "Welcome!",
|
||||
"onboarding-intro": "Jellyfin Audio Player will allow you to stream your music library from anywhere, with full support for background audio and casting.",
|
||||
"onboarding-cta": "In order to get started, you need a Jellyfin server. Click the button below to enter your Jellyfin server address and login to it.",
|
||||
"set-jellyfin-server": "Set Jellyfin Server",
|
||||
"set-jellyfin-server-instruction": "Please enter your Jellyfin server URL. Make sure to include the protocol and port",
|
||||
"settings": "Settings",
|
||||
"jellyfin-library": "Jellyfin Library",
|
||||
"jellyfin-server-url": "Jellyfin Server URL",
|
||||
"jellyfin-access-token": "Jellyfin Access Token",
|
||||
"jellyfin-user-id": "Jellyfin User ID",
|
||||
"setting-cache": "Cache",
|
||||
"setting-cache-description": "If you have updated your Jellyfin library, but the app is holding on to cached assets, you can forcefully clear the cache using this button. This will force the app to fetch the library from scratch.",
|
||||
"reset-cache": "Reset Cache",
|
||||
"recent-albums": "Recent Albums"
|
||||
}
|
||||
28
src/localisation/lang/es/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Reproducir siguente",
|
||||
"play-album": "Reproducir Álbum",
|
||||
"queue": "Cola",
|
||||
"add-to-queue": "Añadir a la cola",
|
||||
"clear-queue": "Limpiar cola",
|
||||
"no-results": "No hay resultados...",
|
||||
"album": "Álbum",
|
||||
"albums": "Álbumes",
|
||||
"all-albums": "Todos los Álbumes",
|
||||
"search": "Buscar",
|
||||
"music": "Música",
|
||||
"now-playing": "Reproduciendo ahora",
|
||||
"onboarding-welcome": "Bienvenido!",
|
||||
"onboarding-intro": "Jellyfin Audio Player te permitirá reproducir tu biblioteca musical desde cualquier sitio, con suporte completo para audio en segundo plano y casteo en otros dispositivos.",
|
||||
"onboarding-cta": "Para empezar necesitas un servidor de Jellyfin. Pulsa el botón de abajo para introducir la dirección del servidor y autentifícate con tus credenciales.",
|
||||
"set-jellyfin-server": "Introduce servidor de Jellyfin",
|
||||
"set-jellyfin-server-instruction": "Por favor introduce la URL de tu servidor de Jellyfin. Acuérdate de incluir protocolo y puerto",
|
||||
"settings": "Configuración",
|
||||
"jellyfin-library": "Biblioteca Jellyfin",
|
||||
"jellyfin-server-url": "Url del servidor Jellyfin",
|
||||
"jellyfin-access-token": "Token de acceso Jellyfin",
|
||||
"jellyfin-user-id": "ID de usuario Jellyfin",
|
||||
"setting-cache": "Caché",
|
||||
"setting-cache-description": "Si has actualizado la biblioteca de Jellyfin pero tu aplicación muestra resultados anteriores, puedes forzar la actualización de la biblioteca pulsando este botón y se volverá a cargar toda información",
|
||||
"reset-cache": "Resetear Caché",
|
||||
"recent-albums": "Álbumes recientes"
|
||||
}
|
||||
28
src/localisation/lang/fr/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Lire ensuite",
|
||||
"play-album": "Jouer l'album",
|
||||
"queue": "File d'attente",
|
||||
"add-to-queue": "Ajouter à la file d'attente",
|
||||
"clear-queue": "Vider la file d'attente",
|
||||
"no-results": "Pas de résultats...",
|
||||
"album": "Album",
|
||||
"albums": "Albums",
|
||||
"all-albums": "Tous les Albums",
|
||||
"search": "Rechercher",
|
||||
"music": "Musique",
|
||||
"now-playing": "En cours",
|
||||
"onboarding-welcome": "Bienvenue !",
|
||||
"onboarding-intro": "Jellyfin Audio Player vous permettra de diffuser votre bibliothèque musicale de n'importe où, avec un support de la lecture en arrière plan et la diffusion à distance.",
|
||||
"onboarding-cta": "Pour utiliser Jellyfin Audio Player, vous avez besoin d'un serveur Jellyfin. Cliquez sur le bouton ci-dessous pour entrer l'adresse de votre serveur Jellyfin et vous y connecter.",
|
||||
"set-jellyfin-server": "Configurer le serveur Jellyfin",
|
||||
"set-jellyfin-server-instruction": "Veuillez entrer l'URL de votre serveur Jellyfin. Assurez-vous d'inclure le protocole et le port",
|
||||
"settings": "Réglages",
|
||||
"jellyfin-library": "Bibliothèque Jellyfin",
|
||||
"jellyfin-server-url": "URL du serveur Jellyfin",
|
||||
"jellyfin-access-token": "Jeton d'accès à la Jellyfin",
|
||||
"jellyfin-user-id": "ID utilisateur Jellyfin",
|
||||
"setting-cache": "Cache",
|
||||
"setting-cache-description": "Si vous avez mis à jour votre bibliothèque Jellyfin mais que l'application conserve toujours des ressources en cache, vous pouvez vider le cache en utilisant ce bouton. Cela forcera l'application à récupérer l’intégralité de bibliothèque.",
|
||||
"reset-cache": "Réinitialiser le cache",
|
||||
"recent-albums": "Albums récents"
|
||||
}
|
||||
28
src/localisation/lang/nl/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Speel volgende",
|
||||
"play-album": "Speel album",
|
||||
"queue": "Wachtrij",
|
||||
"add-to-queue": "Voeg toe aan wachtrij",
|
||||
"clear-queue": "Wis wachtrij",
|
||||
"no-results": "Geen resultaten...",
|
||||
"album": "Album",
|
||||
"albums": "Albums",
|
||||
"all-albums": "Alle Albums",
|
||||
"search": "Zoeken",
|
||||
"music": "Muziek",
|
||||
"now-playing": "Nu spelend",
|
||||
"onboarding-welcome": "Welkom!",
|
||||
"onboarding-intro": "Jellyfin Audio Player maakt het mogelijk om van waar dan ook je muziek te streamen, met volledige support voor achtegroundaudio en casting.",
|
||||
"onboarding-cta": "Om te starten moet je een Jellyfin server hebben. Klik de onderstaande knop om het adres van je Jellyfin server in te vullen en er in te loggen.",
|
||||
"set-jellyfin-server": "Kies Jellyfin Server in",
|
||||
"set-jellyfin-server-instruction": "Vul alsjeblieft je Jellyfin server URL in. Voeg ook het protocol en de poort toe",
|
||||
"settings": "Instellingen",
|
||||
"jellyfin-library": "Jellyfin Bibliotheek",
|
||||
"jellyfin-server-url": "Jellyfin Server URL",
|
||||
"jellyfin-access-token": "Jellyfin Toegangstoken",
|
||||
"jellyfin-user-id": "Jellyfin Gebruiker ID",
|
||||
"setting-cache": "Cache",
|
||||
"setting-cache-description": "Als je Jellyfin bibliotheek geüpdatet hebt, maar de app nog aan elementen uit de cache vasthoudt, kun je de cache geforceerd leegmaken met deze knop. Dit forceert de app om de bibliotheek weer vanaf het begin op te bouwen.",
|
||||
"reset-cache": "Leeg Cache",
|
||||
"recent-albums": "Recente Albums"
|
||||
}
|
||||
26
src/localisation/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export type LocaleKeys = 'play-next'
|
||||
| 'play-album'
|
||||
| 'queue'
|
||||
| 'add-to-queue'
|
||||
| 'clear-queue'
|
||||
| 'no-results'
|
||||
| 'album'
|
||||
| 'albums'
|
||||
| 'all-albums'
|
||||
| 'search'
|
||||
| 'music'
|
||||
| 'now-playing'
|
||||
| 'onboarding-welcome'
|
||||
| 'onboarding-intro'
|
||||
| 'onboarding-cta'
|
||||
| 'set-jellyfin-server'
|
||||
| 'set-jellyfin-server-instruction'
|
||||
| 'settings'
|
||||
| 'jellyfin-library'
|
||||
| 'jellyfin-server-url'
|
||||
| 'jellyfin-access-token'
|
||||
| 'jellyfin-user-id'
|
||||
| 'setting-cache'
|
||||
| 'setting-cache-description'
|
||||
| 'reset-cache'
|
||||
| 'recent-albums'
|
||||
@@ -6,6 +6,7 @@ import Album from './stacks/Album';
|
||||
import RecentAlbums from './stacks/RecentAlbums';
|
||||
import Search from './stacks/Search';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Stack = createStackNavigator<StackParams>();
|
||||
|
||||
@@ -17,10 +18,10 @@ const navigationOptions = {
|
||||
function MusicStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={navigationOptions}>
|
||||
<Stack.Screen name="RecentAlbums" component={RecentAlbums} options={{ headerTitle: 'Recent Albums' }} />
|
||||
<Stack.Screen name="Albums" component={Albums} />
|
||||
<Stack.Screen name="Album" component={Album} />
|
||||
<Stack.Screen name="Search" component={Search} />
|
||||
<Stack.Screen name="RecentAlbums" component={RecentAlbums} options={{ headerTitle: t('recent-albums') }} />
|
||||
<Stack.Screen name="Albums" component={Albums} options={{ headerTitle: t('albums') }} />
|
||||
<Stack.Screen name="Album" component={Album} options={{ headerTitle: t('album') }} />
|
||||
<Stack.Screen name="Search" component={Search} options={{ headerTitle: t('search') }} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { StackParams } from '../types';
|
||||
import { Text, ScrollView, Dimensions, Button, RefreshControl } from 'react-native';
|
||||
import { Text, ScrollView, Dimensions, Button, RefreshControl, StyleSheet } from 'react-native';
|
||||
import { useGetImage } from 'utility/JellyfinApi';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import { useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { useRoute, RouteProp, useNavigation } from '@react-navigation/native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
@@ -11,14 +11,36 @@ import { useTypedSelector } from 'store';
|
||||
import { fetchTracksByAlbum } from 'store/music/actions';
|
||||
import { ALBUM_CACHE_AMOUNT_OF_DAYS, THEME_COLOR } from 'CONSTANTS';
|
||||
import usePlayAlbum from 'utility/usePlayAlbum';
|
||||
import usePlayTrack from 'utility/usePlayTrack';
|
||||
import TouchableHandler from 'components/TouchableHandler';
|
||||
import useCurrentTrack from 'utility/useCurrentTrack';
|
||||
import { colors } from 'components/Colors';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { t } from '@localisation';
|
||||
|
||||
type Route = RouteProp<StackParams, 'Album'>;
|
||||
|
||||
const Screen = Dimensions.get('screen');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
name: {
|
||||
...colors.text,
|
||||
fontSize: 36,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
artist: {
|
||||
...colors.text,
|
||||
fontSize: 24,
|
||||
opacity: 0.5,
|
||||
marginBottom: 24
|
||||
},
|
||||
index: {
|
||||
...colors.text,
|
||||
width: 20,
|
||||
opacity: 0.5,
|
||||
marginRight: 5
|
||||
}
|
||||
});
|
||||
|
||||
const AlbumImage = styled(FastImage)`
|
||||
border-radius: 10px;
|
||||
width: ${Screen.width * 0.6}px;
|
||||
@@ -29,7 +51,6 @@ const AlbumImage = styled(FastImage)`
|
||||
const TrackContainer = styled.View<{isPlaying: boolean}>`
|
||||
padding: 15px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: #eee;
|
||||
flex-direction: row;
|
||||
|
||||
${props => props.isPlaying && css`
|
||||
@@ -51,11 +72,26 @@ const Album: React.FC = () => {
|
||||
const getImage = useGetImage();
|
||||
const playAlbum = usePlayAlbum();
|
||||
const currentTrack = useCurrentTrack();
|
||||
const navigation = useNavigation();
|
||||
|
||||
// Setup callbacks
|
||||
const selectAlbum = useCallback(() => { playAlbum(id); }, [playAlbum, id]);
|
||||
const selectTrack = usePlayTrack();
|
||||
const refresh = useCallback(() => { dispatch(fetchTracksByAlbum(id)); }, [id, dispatch]);
|
||||
const selectTrack = useCallback(async (trackId) => {
|
||||
const tracks = await playAlbum(id, false);
|
||||
|
||||
if (tracks) {
|
||||
const track = tracks.find((t) => t.id.startsWith(trackId));
|
||||
|
||||
if (track) {
|
||||
await TrackPlayer.skip(track.id);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
}
|
||||
}, [playAlbum, id]);
|
||||
const longPressTrack = useCallback((trackId: string) => {
|
||||
navigation.navigate('TrackPopupMenu', { trackId });
|
||||
}, [navigation]);
|
||||
|
||||
// Retrieve album tracks on load
|
||||
useEffect(() => {
|
||||
@@ -71,22 +107,27 @@ const Album: React.FC = () => {
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
style={{ backgroundColor: '#f6f6f6', padding: 20, paddingBottom: 50 }}
|
||||
contentContainerStyle={{ padding: 20, paddingBottom: 50 }}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={isLoading} onRefresh={refresh} />
|
||||
}
|
||||
>
|
||||
<AlbumImage source={{ uri: getImage(album?.Id) }} />
|
||||
<Text style={{ fontSize: 36, fontWeight: 'bold' }} >{album?.Name}</Text>
|
||||
<Text style={{ fontSize: 24, opacity: 0.5, marginBottom: 24 }}>{album?.AlbumArtist}</Text>
|
||||
<Button title="Play Album" onPress={selectAlbum} color={THEME_COLOR} />
|
||||
<AlbumImage source={{ uri: getImage(album?.Id) }} style={colors.imageBackground} />
|
||||
<Text style={styles.name} >{album?.Name}</Text>
|
||||
<Text style={styles.artist}>{album?.AlbumArtist}</Text>
|
||||
<Button title={t('play-album')} onPress={selectAlbum} color={THEME_COLOR} />
|
||||
{album?.Tracks?.length ? album.Tracks.map((trackId) =>
|
||||
<TouchableHandler key={trackId} id={trackId} onPress={selectTrack}>
|
||||
<TrackContainer isPlaying={currentTrack?.id.startsWith(trackId) || false}>
|
||||
<Text style={{ width: 20, opacity: 0.5, marginRight: 5 }}>
|
||||
<TouchableHandler
|
||||
key={trackId}
|
||||
id={trackId}
|
||||
onPress={selectTrack}
|
||||
onLongPress={longPressTrack}
|
||||
>
|
||||
<TrackContainer isPlaying={currentTrack?.id.startsWith(trackId) || false} style={colors.border}>
|
||||
<Text style={styles.index}>
|
||||
{tracks[trackId]?.IndexNumber}
|
||||
</Text>
|
||||
<Text>{tracks[trackId]?.Name}</Text>
|
||||
<Text style={colors.text}>{tracks[trackId]?.Name}</Text>
|
||||
</TrackContainer>
|
||||
</TouchableHandler>
|
||||
) : undefined}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { selectAlbumsByAlphabet, SectionedId } from 'store/music/selectors';
|
||||
import AlphabetScroller from 'components/AlphabetScroller';
|
||||
import { EntityId } from '@reduxjs/toolkit';
|
||||
import styled from 'styled-components/native';
|
||||
import { colors } from 'components/Colors';
|
||||
|
||||
interface VirtualizedItemInfo {
|
||||
section: SectionedId,
|
||||
@@ -39,11 +40,10 @@ function generateSection({ section }: { section: SectionedId }) {
|
||||
}
|
||||
|
||||
const SectionContainer = styled.View`
|
||||
background-color: #f6f6f6;
|
||||
border-bottom-color: #eee;
|
||||
border-bottom-width: 1px;
|
||||
height: 50px;
|
||||
justify-content: center;
|
||||
padding: 0 10px;
|
||||
`;
|
||||
|
||||
const SectionText = styled.Text`
|
||||
@@ -51,13 +51,15 @@ const SectionText = styled.Text`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const sectionStyles = { ...colors.view, ...colors.border };
|
||||
|
||||
class SectionHeading extends PureComponent<{ label: string }> {
|
||||
render() {
|
||||
const { label } = this.props;
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionText>{label}</SectionText>
|
||||
<SectionContainer style={sectionStyles}>
|
||||
<SectionText style={colors.text}>{label}</SectionText>
|
||||
</SectionContainer>
|
||||
);
|
||||
}
|
||||
@@ -82,9 +84,9 @@ class GeneratedAlbumItem extends PureComponent<GeneratedAlbumItemProps> {
|
||||
return (
|
||||
<TouchableHandler id={id as string} onPress={onPress}>
|
||||
<AlbumItem>
|
||||
<AlbumImage source={{ uri: imageUrl }} />
|
||||
<Text numberOfLines={1}>{name}</Text>
|
||||
<HalfOpacity numberOfLines={1}>{artist}</HalfOpacity>
|
||||
<AlbumImage source={{ uri: imageUrl }} style={colors.imageBackground} />
|
||||
<Text numberOfLines={1} style={colors.text}>{name}</Text>
|
||||
<HalfOpacity style={colors.text} numberOfLines={1}>{artist}</HalfOpacity>
|
||||
</AlbumItem>
|
||||
</TouchableHandler>
|
||||
);
|
||||
@@ -153,7 +155,7 @@ const Albums: React.FC = () => {
|
||||
const nextItem = section.data[index + 1];
|
||||
|
||||
return (
|
||||
<View style={{ flexDirection: 'row' }} key={item}>
|
||||
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item}>
|
||||
<GeneratedAlbumItem
|
||||
id={item}
|
||||
imageUrl={getImage(item as string)}
|
||||
@@ -184,19 +186,17 @@ const Albums: React.FC = () => {
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<ListContainer>
|
||||
<AlphabetScroller onSelect={selectLetter} />
|
||||
<SectionList
|
||||
sections={sections}
|
||||
refreshing={isLoading}
|
||||
onRefresh={retrieveData}
|
||||
getItemLayout={getItemLayout}
|
||||
ref={listRef}
|
||||
keyExtractor={(item, index) => `${item}_${index}`}
|
||||
renderSectionHeader={generateSection}
|
||||
renderItem={generateItem}
|
||||
/>
|
||||
</ListContainer>
|
||||
<AlphabetScroller onSelect={selectLetter} />
|
||||
<SectionList
|
||||
sections={sections}
|
||||
refreshing={isLoading}
|
||||
onRefresh={retrieveData}
|
||||
getItemLayout={getItemLayout}
|
||||
ref={listRef}
|
||||
keyExtractor={(item, index) => `${item}_${index}`}
|
||||
renderSectionHeader={generateSection}
|
||||
renderItem={generateItem}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useGetImage } from 'utility/JellyfinApi';
|
||||
import { Album, NavigationProp } from '../types';
|
||||
import { Text, SafeAreaView, FlatList } from 'react-native';
|
||||
import { Text, SafeAreaView, FlatList, StyleSheet } from 'react-native';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useTypedSelector } from 'store';
|
||||
@@ -12,6 +12,15 @@ import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
||||
import { useRecentAlbums } from 'store/music/selectors';
|
||||
import { Header } from 'components/Typography';
|
||||
import ListButton from 'components/ListButton';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
artist: {
|
||||
...colors.text,
|
||||
opacity: 0.5,
|
||||
}
|
||||
});
|
||||
|
||||
const NavigationHeader: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
@@ -20,9 +29,9 @@ const NavigationHeader: React.FC = () => {
|
||||
|
||||
return (
|
||||
<ListContainer>
|
||||
<ListButton onPress={handleAllAlbumsClick}>All Albums</ListButton>
|
||||
<ListButton onPress={handleSearchClick}>Search</ListButton>
|
||||
<Header>Recent Albums</Header>
|
||||
<ListButton onPress={handleAllAlbumsClick}>{t('all-albums')}</ListButton>
|
||||
<ListButton onPress={handleSearchClick}>{t('search')}</ListButton>
|
||||
<Header style={colors.text}>{t('recent-albums')}</Header>
|
||||
</ListContainer>
|
||||
);
|
||||
};
|
||||
@@ -58,9 +67,9 @@ const RecentAlbums: React.FC = () => {
|
||||
renderItem={({ item }) => (
|
||||
<TouchableHandler id={item} onPress={selectAlbum}>
|
||||
<AlbumItem>
|
||||
<AlbumImage source={{ uri: getImage(item) }} />
|
||||
<Text numberOfLines={1}>{albums[item]?.Name}</Text>
|
||||
<Text numberOfLines={1} style={{ opacity: 0.5 }}>{albums[item]?.AlbumArtist}</Text>
|
||||
<AlbumImage source={{ uri: getImage(item) }} style={colors.imageBackground} />
|
||||
<Text style={colors.text} numberOfLines={1}>{albums[item]?.Name}</Text>
|
||||
<Text style={styles.artist} numberOfLines={1}>{albums[item]?.AlbumArtist}</Text>
|
||||
</AlbumItem>
|
||||
</TouchableHandler>
|
||||
)}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { useNavigation } from '@react-navigation/native';
|
||||
import { useGetImage } from 'utility/JellyfinApi';
|
||||
import { NavigationProp } from '../types';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Container = styled.View`
|
||||
padding: 0 20px;
|
||||
@@ -20,7 +22,6 @@ const AlbumImage = styled(FastImage)`
|
||||
border-radius: 4px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-color: #fefefe;
|
||||
margin-right: 10px;
|
||||
`;
|
||||
|
||||
@@ -33,7 +34,6 @@ const HalfOpacity = styled.Text`
|
||||
const SearchResult = styled.View`
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-bottom-color: #ddd;
|
||||
border-bottom-width: 1px;
|
||||
margin-left: 15px;
|
||||
padding-right: 15px;
|
||||
@@ -51,7 +51,7 @@ export default function Search() {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const albums = useTypedSelector(state => state.music.albums.entities);
|
||||
const [results, setResults] = useState<Fuse.FuseResult<Album>[]>([]);
|
||||
let fuse = useRef<Fuse<Album, typeof fuseOptions>>();
|
||||
const fuse = useRef<Fuse<Album, typeof fuseOptions>>();
|
||||
|
||||
// Prepare helpers
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
@@ -89,8 +89,8 @@ export default function Search() {
|
||||
|
||||
const HeaderComponent = React.useMemo(() => (
|
||||
<Container>
|
||||
<Input value={searchTerm} onChangeText={setSearchTerm} placeholder="Search..." />
|
||||
{(searchTerm.length && !results.length) ? <Text>No results...</Text> : null}
|
||||
<Input value={searchTerm} onChangeText={setSearchTerm} style={colors.input} placeholder={t('search') + '...'} />
|
||||
{(searchTerm.length && !results.length) ? <Text>{t('no-results')}</Text> : null}
|
||||
</Container>
|
||||
), [searchTerm, results, setSearchTerm]);
|
||||
|
||||
@@ -105,13 +105,13 @@ export default function Search() {
|
||||
data={results}
|
||||
renderItem={({ item: { item: album } }) =>(
|
||||
<TouchableHandler id={album.Id} onPress={selectAlbum}>
|
||||
<SearchResult>
|
||||
<SearchResult style={colors.border}>
|
||||
<AlbumImage source={{ uri: getImage(album.Id) }} />
|
||||
<View>
|
||||
<Text numberOfLines={1} ellipsizeMode="tail">
|
||||
<Text numberOfLines={1} ellipsizeMode="tail" style={colors.text}>
|
||||
{album.Name} - {album.AlbumArtist}
|
||||
</Text>
|
||||
<HalfOpacity>Album</HalfOpacity>
|
||||
<HalfOpacity style={colors.text}>{t('album')}</HalfOpacity>
|
||||
</View>
|
||||
</SearchResult>
|
||||
</TouchableHandler>
|
||||
|
||||
@@ -14,7 +14,6 @@ const AlbumImage = styled(FastImage)`
|
||||
border-radius: 10px;
|
||||
width: ${Screen.width / 2 - 40}px;
|
||||
height: ${Screen.width / 2 - 40}px;
|
||||
background-color: #fefefe;
|
||||
margin-bottom: 5px;
|
||||
`;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import styled from 'styled-components/native';
|
||||
|
||||
const ListContainer = styled.View`
|
||||
padding: 10px;
|
||||
background-color: #f6f6f6;
|
||||
`;
|
||||
|
||||
export default ListContainer;
|
||||
77
src/screens/Onboarding/index.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import styled from 'styled-components/native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { Button } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from 'screens';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setOnboardingStatus } from 'store/settings/actions';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Container = styled.SafeAreaView`
|
||||
background-color: ${THEME_COLOR};
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const TextContainer = styled.ScrollView`
|
||||
padding: 25px;
|
||||
`;
|
||||
|
||||
const Text = styled.Text`
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
const ButtonContainer = styled.View`
|
||||
margin-top: 50px;
|
||||
`;
|
||||
|
||||
const Logo = styled.Image`
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: 0 auto 50px auto;
|
||||
`;
|
||||
|
||||
function Onboarding() {
|
||||
// Get account from Redux and dispatcher
|
||||
const account = useTypedSelector(state => state.settings.jellyfin);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Also retrieve the navigation handler so that we can open the modal in
|
||||
// which the Jellyfin server is set
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleClick = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
|
||||
// We'll also respond to any change in the account, setting the onboarding
|
||||
// status to true, so that the app becomes available.
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
dispatch(setOnboardingStatus(true));
|
||||
}
|
||||
}, [account, dispatch]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<TextContainer contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
|
||||
<Logo source={require('../../assets/app-icon-white.png')} />
|
||||
<Text >
|
||||
{t('onboarding-welcome')}
|
||||
</Text>
|
||||
<Text>
|
||||
{t('onboarding-intro')}
|
||||
</Text>
|
||||
<Text>
|
||||
{t('onboarding-cta')}
|
||||
</Text>
|
||||
<ButtonContainer>
|
||||
<Button title={t('set-jellyfin-server')} color="#ffffff" onPress={handleClick} />
|
||||
</ButtonContainer>
|
||||
</TextContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default Onboarding;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, STATE_PLAYING, STATE_PAUSED } from 'react-native-track-player';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import styled from 'styled-components/native';
|
||||
@@ -7,8 +7,13 @@ import ForwardIcon from 'assets/forwards.svg';
|
||||
import BackwardIcon from 'assets/backwards.svg';
|
||||
import PlayIcon from 'assets/play.svg';
|
||||
import PauseIcon from 'assets/pause.svg';
|
||||
import RepeatIcon from 'assets/repeat.svg';
|
||||
// import ShuffleIcon from 'assets/shuffle.svg';
|
||||
import { useColorScheme } from 'react-native-appearance';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
|
||||
const BUTTON_SIZE = 40;
|
||||
const BUTTON_SIZE_SMALL = 25;
|
||||
|
||||
const pause = () => TrackPlayer.pause();
|
||||
const play = () => TrackPlayer.play();
|
||||
@@ -32,61 +37,133 @@ const Button = styled.View`
|
||||
`;
|
||||
|
||||
export default function MediaControls() {
|
||||
const scheme = useColorScheme();
|
||||
const fill = scheme === 'dark' ? '#ffffff' : '#000000';
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Buttons>
|
||||
<Button>
|
||||
<PreviousButton />
|
||||
<PreviousButton fill={fill} />
|
||||
</Button>
|
||||
<MainButton />
|
||||
<MainButton fill={fill} />
|
||||
<Button>
|
||||
<NextButton />
|
||||
<NextButton fill={fill} />
|
||||
</Button>
|
||||
</Buttons>
|
||||
<Buttons>
|
||||
<Button>
|
||||
<RepeatButton fill={fill} />
|
||||
</Button>
|
||||
<Button>
|
||||
{/* <ShuffleButton fill={fill} /> */}
|
||||
</Button>
|
||||
</Buttons>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export function PreviousButton() {
|
||||
export function PreviousButton({ fill }: { fill: string }) {
|
||||
const hasQueue = useHasQueue();
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={previous} disabled={!hasQueue} style={{ opacity: hasQueue ? 1 : 0.5 }}>
|
||||
<BackwardIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill="#000" />
|
||||
<BackwardIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
export function NextButton() {
|
||||
export function NextButton({ fill }: { fill: string }) {
|
||||
const hasQueue = useHasQueue();
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={next} disabled={!hasQueue} style={{ opacity: hasQueue ? 1 : 0.5 }}>
|
||||
<ForwardIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill="#000" />
|
||||
<ForwardIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
export function MainButton() {
|
||||
export function RepeatButton({ fill }: { fill: string}) {
|
||||
const [isRepeating, setRepeating] = useState(false);
|
||||
const handlePress = useCallback(() => setRepeating(!isRepeating), [isRepeating, setRepeating]);
|
||||
const listener = useRef<TrackPlayer.EmitterSubscription | null>(null);
|
||||
|
||||
// The callback that should determine whether we need to repeeat or not
|
||||
const handleEndEvent = useCallback(async () => {
|
||||
if (isRepeating) {
|
||||
// Retrieve all current tracks
|
||||
const tracks = await TrackPlayer.getQueue();
|
||||
|
||||
// Then skip to the first track
|
||||
await TrackPlayer.skip(tracks[0].id);
|
||||
|
||||
// Cautiously reset the seek time, as there might only be a single
|
||||
// item in queue.
|
||||
await TrackPlayer.seekTo(0);
|
||||
|
||||
// Then play the item
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
}, [isRepeating]);
|
||||
|
||||
// Subscribe to ended event handler so that we can restart the queue from
|
||||
// the start if looping is enabled
|
||||
useEffect(() => {
|
||||
// Set the event listener
|
||||
listener.current = TrackPlayer.addEventListener('playback-queue-ended', handleEndEvent);
|
||||
|
||||
// Then clean up after
|
||||
return function cleanup() {
|
||||
listener?.current?.remove();
|
||||
};
|
||||
}, [handleEndEvent]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={handlePress} style={{ opacity: isRepeating ? 1 : 0.5 }}>
|
||||
<RepeatIcon
|
||||
width={BUTTON_SIZE_SMALL}
|
||||
height={BUTTON_SIZE_SMALL}
|
||||
fill={isRepeating ? THEME_COLOR : fill}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
// export function ShuffleButton({ fill }: { fill: string}) {
|
||||
// const [isShuffling, setShuffling] = useState(false);
|
||||
// const handlePress = useCallback(() => setShuffling(!isShuffling), [isShuffling, setShuffling]);
|
||||
|
||||
// return (
|
||||
// <TouchableOpacity onPress={handlePress} style={{ opacity: isShuffling ? 1 : 0.5 }}>
|
||||
// <ShuffleIcon
|
||||
// width={BUTTON_SIZE_SMALL}
|
||||
// height={BUTTON_SIZE_SMALL}
|
||||
// fill={isShuffling ? THEME_COLOR : fill}
|
||||
// />
|
||||
// </TouchableOpacity>
|
||||
// );
|
||||
// }
|
||||
|
||||
export function MainButton({ fill }: { fill: string }) {
|
||||
const state = usePlaybackState();
|
||||
|
||||
switch (state) {
|
||||
case STATE_PLAYING:
|
||||
return (
|
||||
<TouchableOpacity onPress={pause}>
|
||||
<PauseIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill="#000" />
|
||||
<PauseIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
case STATE_PAUSED:
|
||||
return (
|
||||
<TouchableOpacity onPress={play}>
|
||||
<PlayIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill="#000" />
|
||||
<PlayIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<TouchableOpacity onPress={pause} disabled>
|
||||
<PauseIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill="#000" />
|
||||
<PauseIcon width={BUTTON_SIZE} height={BUTTON_SIZE} fill={fill} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,51 @@
|
||||
import React from 'react';
|
||||
import { Text, Dimensions, View } from 'react-native';
|
||||
import { Text, Dimensions, View, StyleSheet } from 'react-native';
|
||||
import useCurrentTrack from 'utility/useCurrentTrack';
|
||||
import styled from 'styled-components/native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { colors } from 'components/Colors';
|
||||
|
||||
const Screen = Dimensions.get('screen');
|
||||
|
||||
const Artwork = styled(FastImage)`
|
||||
border-radius: 10px;
|
||||
background-color: #fbfbfb;
|
||||
width: ${Screen.width * 0.8}px;
|
||||
height: ${Screen.width * 0.8}px;
|
||||
margin: 25px auto;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
artist: {
|
||||
...colors.text,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
marginBottom: 12,
|
||||
},
|
||||
title: {
|
||||
...colors.text,
|
||||
fontSize: 18,
|
||||
marginBottom: 12,
|
||||
textAlign: 'center',
|
||||
paddingLeft: 20,
|
||||
paddingRight: 20,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default function NowPlaying() {
|
||||
const track = useCurrentTrack();
|
||||
|
||||
return (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<Artwork style={{ flex: 1 }} source={{ uri: track?.artwork }} />
|
||||
<Text style={{ fontWeight: 'bold', fontSize: 24, marginBottom: 12 }} >{track?.artist}</Text>
|
||||
<Text style={{ fontSize: 18, marginBottom: 12, textAlign: 'center', paddingLeft: 20, paddingRight: 20 }}>{track?.title}</Text>
|
||||
<Artwork
|
||||
style={colors.imageBackground}
|
||||
source={{
|
||||
uri: track?.artwork,
|
||||
priority: FastImage.priority.high,
|
||||
}}
|
||||
/>
|
||||
<Text style={styles.artist} >{track?.artist}</Text>
|
||||
<Text style={styles.title}>{track?.title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import styled from 'styled-components/native';
|
||||
import { Text } from 'react-native';
|
||||
import { Text, Platform } from 'react-native';
|
||||
import Slider from '@react-native-community/slider';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { colors } from 'components/Colors';
|
||||
|
||||
const NumberBar = styled.View`
|
||||
flex-direction: row;
|
||||
@@ -74,11 +75,12 @@ export default class ProgressBar extends Component<{}, State> {
|
||||
onValueChange={this.handleGesture}
|
||||
onSlidingComplete={this.handleEndOfGesture}
|
||||
minimumTrackTintColor={THEME_COLOR}
|
||||
thumbTintColor={Platform.OS === 'android' ? THEME_COLOR : undefined}
|
||||
disabled={!duration}
|
||||
/>
|
||||
<NumberBar>
|
||||
<Text>{getMinutes(gesture || position)}:{getSeconds(gesture || position)}</Text>
|
||||
<Text>{getMinutes(duration)}:{getSeconds(duration)}</Text>
|
||||
<Text style={colors.text}>{getMinutes(gesture || position)}:{getSeconds(gesture || position)}</Text>
|
||||
<Text style={colors.text}>{getMinutes(duration)}:{getSeconds(duration)}</Text>
|
||||
</NumberBar>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,29 +1,44 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import useQueue from 'utility/useQueue';
|
||||
import { View, Text } from 'react-native';
|
||||
import { View, Text, StyleSheet, Button } from 'react-native';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import useCurrentTrack from 'utility/useCurrentTrack';
|
||||
import TouchableHandler from 'components/TouchableHandler';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const QueueItem = styled.View<{ active?: boolean, alreadyPlayed?: boolean }>`
|
||||
const QueueItem = styled.View<{ active?: boolean, alreadyPlayed?: boolean, isDark?: boolean }>`
|
||||
padding: 10px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: #eee;
|
||||
|
||||
${props => props.active && css`
|
||||
font-weight: 900;
|
||||
background-color: ${THEME_COLOR}16;
|
||||
padding: 20px 35px;
|
||||
margin: 0 -25px;
|
||||
`}
|
||||
|
||||
${props => props.alreadyPlayed && css`
|
||||
opacity: 0.25;
|
||||
opacity: 0.5;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ClearQueue = styled.View`
|
||||
margin: 20px 0;
|
||||
`;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
title: {
|
||||
...colors.text,
|
||||
marginBottom: 2,
|
||||
},
|
||||
artist: {
|
||||
...colors.text,
|
||||
opacity: 0.5,
|
||||
}
|
||||
});
|
||||
|
||||
export default function Queue() {
|
||||
const queue = useQueue();
|
||||
const currentTrack = useCurrentTrack();
|
||||
@@ -32,18 +47,32 @@ export default function Queue() {
|
||||
await TrackPlayer.skip(trackId);
|
||||
await TrackPlayer.play();
|
||||
}, []);
|
||||
const clearQueue = useCallback(async () => {
|
||||
await TrackPlayer.reset();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Text style={{ marginTop: 20, marginBottom: 20 }}>Queue</Text>
|
||||
<Text style={{ marginTop: 20, marginBottom: 20 }}>{t('queue')}</Text>
|
||||
{queue.map((track, i) => (
|
||||
<TouchableHandler id={track.id} onPress={playTrack} key={i}>
|
||||
<QueueItem active={currentTrack?.id === track.id} key={i} alreadyPlayed={i < currentIndex}>
|
||||
<Text style={{marginBottom: 2}}>{track.title}</Text>
|
||||
<Text style={{ opacity: 0.5 }}>{track.artist}</Text>
|
||||
<QueueItem
|
||||
active={currentTrack?.id === track.id}
|
||||
key={i}
|
||||
alreadyPlayed={i < currentIndex}
|
||||
style={{
|
||||
...colors.border,
|
||||
...currentTrack?.id === track.id ? colors.activeBackground : {},
|
||||
}}
|
||||
>
|
||||
<Text style={styles.title}>{track.title}</Text>
|
||||
<Text style={styles.artist}>{track.artist}</Text>
|
||||
</QueueItem>
|
||||
</TouchableHandler>
|
||||
))}
|
||||
<ClearQueue>
|
||||
<Button title={t('clear-queue')} color={THEME_COLOR} onPress={clearQueue} />
|
||||
</ClearQueue>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +1,25 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, ScrollView } from 'react-native';
|
||||
import MediaControls from './components/MediaControls';
|
||||
import ProgressBar from './components/ProgressBar';
|
||||
import NowPlaying from './components/NowPlaying';
|
||||
import styled from 'styled-components/native';
|
||||
import Queue from './components/Queue';
|
||||
import { colors } from 'components/Colors';
|
||||
|
||||
const Container = styled.ScrollView`
|
||||
background-color: #fff;
|
||||
padding: 25px;
|
||||
`;
|
||||
const styles = StyleSheet.create({
|
||||
outer: colors.view,
|
||||
inner: {
|
||||
padding: 25,
|
||||
}
|
||||
});
|
||||
|
||||
export default function Player() {
|
||||
return (
|
||||
<Container>
|
||||
<ScrollView contentContainerStyle={styles.inner} style={styles.outer}>
|
||||
<NowPlaying />
|
||||
<MediaControls />
|
||||
<ProgressBar />
|
||||
<Queue />
|
||||
</Container>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
27
src/screens/Settings/components/Cache.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import { Text, Button } from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import music from 'store/music';
|
||||
import { t } from '@localisation';
|
||||
|
||||
export default function CacheSettings() {
|
||||
const dispatch = useDispatch();
|
||||
const handleClearCache = useCallback(() => {
|
||||
// Dispatch an action to reset the music subreducer state
|
||||
dispatch(music.actions.reset());
|
||||
|
||||
// Also clear the TrackPlayer queue
|
||||
TrackPlayer.reset();
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubHeader>{t('setting-cache')}</SubHeader>
|
||||
<Text>{t('setting-cache-description')}</Text>
|
||||
<Button title={t('reset-cache')} onPress={handleClearCache} color={THEME_COLOR} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
45
src/screens/Settings/components/Library.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import styled from 'styled-components/native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Text, Button } from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import { colors } from 'components/Colors';
|
||||
import { NavigationProp } from '../..';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
export default function LibrarySettings() {
|
||||
const { jellyfin } = useTypedSelector(state => state.settings);
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleSetLibrary = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubHeader>{t('jellyfin-library')}</SubHeader>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>{t('jellyfin-server-url')}</Text>
|
||||
<Input placeholder="https://jellyfin.yourserver.com/" value={jellyfin?.uri} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>{t('jellyfin-access-token')}</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.access_token} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>{t('jellyfin-user-id')}</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.user_id} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<Button title={t('set-jellyfin-server')} onPress={handleSetLibrary} color={THEME_COLOR} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +1,19 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { View, Text, SafeAreaView, Button } from 'react-native';
|
||||
import { Picker } from '@react-native-community/picker';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
import styled from 'styled-components/native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '..';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
background-color: #fbfbfb;
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
import React from 'react';
|
||||
import { View, SafeAreaView, ScrollView } from 'react-native';
|
||||
import { Header } from 'components/Typography';
|
||||
import { colors } from 'components/Colors';
|
||||
import Library from './components/Library';
|
||||
import Cache from './components/Cache';
|
||||
import { t } from '@localisation';
|
||||
|
||||
export default function Settings() {
|
||||
const { jellyfin, bitrate } = useSelector((state: AppState) => state.settings);
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleClick = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<SafeAreaView>
|
||||
<View style={{ padding: 20 }}>
|
||||
<Text style={{ fontSize: 36, marginBottom: 24, fontWeight: 'bold' }}>Settings</Text>
|
||||
<InputContainer>
|
||||
<Text>Jellyfin Server URL</Text>
|
||||
<Input placeholder="https://jellyfin.yourserver.com/" value={jellyfin?.uri} editable={false} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text>Jellyfin Access Token</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.access_token} editable={false} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text>Jellyfin User ID</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.user_id} editable={false} />
|
||||
</InputContainer>
|
||||
<Button title="Set Jellyfin server" onPress={handleClick} color={THEME_COLOR} />
|
||||
<InputContainer>
|
||||
<Text>Bitrate</Text>
|
||||
<Picker selectedValue={bitrate}>
|
||||
<Picker.Item label="320kbps" value={140000000} />
|
||||
</Picker>
|
||||
</InputContainer>
|
||||
<Header style={colors.text}>{t('settings')}</Header>
|
||||
<Library />
|
||||
<Cache />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
|
||||
@@ -10,8 +10,13 @@ import PlayPauseIcon from 'assets/play-pause-fill.svg';
|
||||
import NotesIcon from 'assets/notes.svg';
|
||||
import GearIcon from 'assets/gear.svg';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { useTypedSelector } from 'store';
|
||||
import Onboarding from './Onboarding';
|
||||
import TrackPopupMenu from './modals/TrackPopupMenu';
|
||||
import { ModalStackParams } from './types';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
const Stack = createStackNavigator<ModalStackParams>();
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
type Screens = {
|
||||
@@ -34,6 +39,14 @@ function getIcon(route: string): React.FC<any> | null {
|
||||
}
|
||||
|
||||
function Screens() {
|
||||
const isOnboardingComplete = useTypedSelector(state => state.settings.isOnboardingComplete);
|
||||
|
||||
// GUARD: If onboarding has not been completed, we instead render the
|
||||
// onboarding component, so that the user can get setup in the app.
|
||||
if (!isOnboardingComplete) {
|
||||
return <Onboarding />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={({ route }) => ({
|
||||
@@ -52,9 +65,9 @@ function Screens() {
|
||||
inactiveTintColor: 'gray',
|
||||
}}
|
||||
>
|
||||
<Tab.Screen name="NowPlaying" component={Player} options={{ tabBarLabel: 'Now Playing' }} />
|
||||
<Tab.Screen name="Music" component={Music} />
|
||||
<Tab.Screen name="Settings" component={Settings} />
|
||||
<Tab.Screen name="NowPlaying" component={Player} options={{ tabBarLabel: t('now-playing') }} />
|
||||
<Tab.Screen name="Music" component={Music} options={{ tabBarLabel: t('music') }} />
|
||||
<Tab.Screen name="Settings" component={Settings} options={{ tabBarLabel: t('settings') }} />
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
@@ -74,6 +87,7 @@ export default function Routes() {
|
||||
}}>
|
||||
<Stack.Screen name="Screens" component={Screens} />
|
||||
<Stack.Screen name="SetJellyfinServer" component={SetJellyfinServer} />
|
||||
<Stack.Screen name="TrackPopupMenu" component={TrackPopupMenu} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { useDispatch } from 'react-redux';
|
||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
import CredentialGenerator from './components/CredentialGenerator';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
export default function SetJellyfinServer() {
|
||||
// State for first screen
|
||||
@@ -31,8 +33,10 @@ export default function SetJellyfinServer() {
|
||||
onCredentialsRetrieved={saveCredentials}
|
||||
/>
|
||||
) : (
|
||||
<View style={{ padding: 20}}>
|
||||
<Text>Please enter your Jellyfin server URL first. Make sure to include the protocol and port</Text>
|
||||
<View style={{ padding: 20, flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||||
<Text style={colors.text}>
|
||||
{t('set-jellyfin-server-instruction')}
|
||||
</Text>
|
||||
<Input
|
||||
placeholder="https://jellyfin.yourserver.io/"
|
||||
onChangeText={setServerUrl}
|
||||
@@ -40,9 +44,10 @@ export default function SetJellyfinServer() {
|
||||
keyboardType="url"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
style={{ ...colors.input, width: '100%' }}
|
||||
/>
|
||||
<Button
|
||||
title="Set server"
|
||||
title={t('set-jellyfin-server')}
|
||||
onPress={() => setIsLogginIn(true)}
|
||||
disabled={!serverUrl?.length}
|
||||
color={THEME_COLOR}
|
||||
|
||||
60
src/screens/modals/TrackPopupMenu.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import { Text, Button } from 'react-native';
|
||||
import { useNavigation, StackActions, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { ModalStackParams } from 'screens/types';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import styled from 'styled-components/native';
|
||||
import usePlayTrack from 'utility/usePlayTrack';
|
||||
import { t } from '@localisation';
|
||||
|
||||
type Route = RouteProp<ModalStackParams, 'TrackPopupMenu'>;
|
||||
|
||||
const Container = styled.View`
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
const Buttons = styled.View`
|
||||
margin-top: 20px;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
`;
|
||||
|
||||
function TrackPopupMenu() {
|
||||
// Retrieve helpers
|
||||
const { params: { trackId } } = useRoute<Route>();
|
||||
const navigation = useNavigation();
|
||||
const track = useTypedSelector((state) => state.music.tracks.entities[trackId]);
|
||||
const playTrack = usePlayTrack();
|
||||
|
||||
// Set callback to close the modal
|
||||
const closeModal = useCallback(() => {
|
||||
navigation.dispatch(StackActions.popToTop());
|
||||
}, [navigation]);
|
||||
|
||||
const handlePlayNext = useCallback(() => {
|
||||
playTrack(trackId, false, false);
|
||||
closeModal();
|
||||
}, [playTrack, closeModal, trackId]);
|
||||
const handleAddToQueue = useCallback(() => {
|
||||
playTrack(trackId, false, true);
|
||||
closeModal();
|
||||
}, [playTrack, closeModal, trackId]);
|
||||
|
||||
return (
|
||||
<Modal fullSize={false}>
|
||||
<Container>
|
||||
<SubHeader>{track?.Name}</SubHeader>
|
||||
<Text>{track?.Album} - {track?.AlbumArtist}</Text>
|
||||
<Buttons>
|
||||
<Button title={t('play-next')} color={THEME_COLOR} onPress={handlePlayNext} />
|
||||
<Button title={t('add-to-queue')} color={THEME_COLOR} onPress={handleAddToQueue} />
|
||||
</Buttons>
|
||||
</Container>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default TrackPopupMenu;
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ModalStackParams {
|
||||
SetJellyfinServer: undefined;
|
||||
TrackPopupMenu: { trackId: string };
|
||||
}
|
||||
@@ -1,19 +1,23 @@
|
||||
import { configureStore, getDefaultMiddleware, combineReducers } from '@reduxjs/toolkit';
|
||||
import { useSelector, TypedUseSelectorHook } from 'react-redux';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { persistStore, persistReducer } from 'redux-persist';
|
||||
import { persistStore, persistReducer, PersistConfig } from 'redux-persist';
|
||||
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
|
||||
// import logger from 'redux-logger';
|
||||
|
||||
const persistConfig = {
|
||||
const persistConfig: PersistConfig<AppState> = {
|
||||
key: 'root',
|
||||
storage: AsyncStorage,
|
||||
stateReconciler: autoMergeLevel2
|
||||
};
|
||||
|
||||
import settings from './settings';
|
||||
import music from './music';
|
||||
import player from './player';
|
||||
|
||||
const reducers = combineReducers({
|
||||
settings,
|
||||
player: player.reducer,
|
||||
music: music.reducer,
|
||||
});
|
||||
|
||||
@@ -26,7 +30,7 @@ const store = configureStore({
|
||||
),
|
||||
});
|
||||
|
||||
export type AppState = ReturnType<typeof store.getState>;
|
||||
export type AppState = ReturnType<typeof reducers>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export type AsyncThunkAPI = { state: AppState, dispatch: AppDispatch };
|
||||
export const useTypedSelector: TypedUseSelectorHook<AppState> = useSelector;
|
||||
|
||||
@@ -31,7 +31,9 @@ const initialState: State = {
|
||||
const music = createSlice({
|
||||
name: 'music',
|
||||
initialState,
|
||||
reducers: {},
|
||||
reducers: {
|
||||
reset: () => initialState,
|
||||
},
|
||||
extraReducers: builder => {
|
||||
/**
|
||||
* Fetch All albums
|
||||
|
||||
11
src/store/player/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
const player = createSlice({
|
||||
name: 'player',
|
||||
initialState: 0,
|
||||
reducers: {
|
||||
addNewTrackToPlayer: (state) => state + 1,
|
||||
}
|
||||
});
|
||||
|
||||
export default player;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
export const setJellyfinCredentials = createAction<{ access_token: string, user_id: string, uri: string, deviced_id: string; }>('SET_JELLYFIN_CREDENTIALS');
|
||||
export const setBitrate = createAction<number>('SET_BITRATE');
|
||||
export const setBitrate = createAction<number>('SET_BITRATE');
|
||||
export const setOnboardingStatus = createAction<boolean>('SET_ONBOARDING_STATUS');
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { setBitrate, setJellyfinCredentials } from './actions';
|
||||
import { setBitrate, setJellyfinCredentials, setOnboardingStatus } from './actions';
|
||||
|
||||
interface State {
|
||||
jellyfin?: {
|
||||
@@ -9,10 +9,12 @@ interface State {
|
||||
device_id: string;
|
||||
}
|
||||
bitrate: number;
|
||||
isOnboardingComplete: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
bitrate: 140000000
|
||||
bitrate: 140000000,
|
||||
isOnboardingComplete: false,
|
||||
};
|
||||
|
||||
const settings = createReducer(initialState, {
|
||||
@@ -24,6 +26,10 @@ const settings = createReducer(initialState, {
|
||||
...state,
|
||||
bitrate: action.payload,
|
||||
}),
|
||||
[setOnboardingStatus.type]: (state, action) => ({
|
||||
...state,
|
||||
isOnboardingComplete: action.payload,
|
||||
})
|
||||
});
|
||||
|
||||
export default settings;
|
||||
@@ -15,6 +15,8 @@ function generateConfig(credentials: Credentials): RequestInit {
|
||||
const baseTrackOptions: Record<string, string> = {
|
||||
// Not sure where this number refers to, but setting it to 140000000 appears
|
||||
// to do wonders for making stuff work
|
||||
// NOTE: Apparently setting a bitrate is as of yet unsupported in the
|
||||
// Jellyfin core, and hence this value is not used
|
||||
MaxStreamingBitrate: '140000000',
|
||||
MaxSampleRate: '48000',
|
||||
// This must be set to support client seeking
|
||||
@@ -24,7 +26,7 @@ const baseTrackOptions: Record<string, string> = {
|
||||
// react-native-track-player. This is set to be merged and released very
|
||||
// soon: https://github.com/react-native-kit/react-native-track-player/pull/950
|
||||
// Container: 'mp3',
|
||||
Container: 'mp3,aac,m4a,m4b|aac,alac,m4a,m4b|alac',
|
||||
Container: 'mp3,aac,m4a,m4b|aac,alac,m4a,m4b|alac,flac',
|
||||
AudioCodec: 'aac',
|
||||
static: 'true',
|
||||
};
|
||||
@@ -43,7 +45,7 @@ export function generateTrack(track: AlbumTrack, credentials: Credentials): Trac
|
||||
DeviceId: credentials?.device_id || '',
|
||||
};
|
||||
const trackParams = new URLSearchParams(trackOptions).toString();
|
||||
const url = encodeURI(`${credentials?.uri}/Audio/${track.Id}/universal.mp3?${trackParams}`);
|
||||
const url = encodeURI(`${credentials?.uri}/Audio/${track.Id}/universal?${trackParams}`);
|
||||
|
||||
return {
|
||||
id: track.Id,
|
||||
|
||||
@@ -14,15 +14,22 @@ export default function useCurrentTrack(): Track | undefined {
|
||||
|
||||
// GUARD: Only fetch current track if there is a current track
|
||||
if (!currentTrackId) {
|
||||
setTrack(undefined);
|
||||
}
|
||||
|
||||
// GUARD: Only retrieve new track if it is different from the one we
|
||||
// have currently in state.
|
||||
if (currentTrackId === track?.id){
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is different, retrieve the track and save it
|
||||
const currentTrack = await TrackPlayer.getTrack(currentTrackId);
|
||||
setTrack(currentTrack);
|
||||
};
|
||||
|
||||
fetchTrack();
|
||||
}, [state]);
|
||||
}, [state, track, setTrack]);
|
||||
|
||||
return track;
|
||||
}
|
||||
@@ -2,17 +2,22 @@ import { useTypedSelector } from 'store';
|
||||
import { useCallback } from 'react';
|
||||
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||
import { generateTrack } from './JellyfinApi';
|
||||
import useQueue from './useQueue';
|
||||
import player from 'store/player';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
/**
|
||||
* Generate a callback function that starts playing a full album given its
|
||||
* supplied id.
|
||||
*/
|
||||
export default function usePlayAlbum() {
|
||||
const dispatch = useDispatch();
|
||||
const credentials = useTypedSelector(state => state.settings.jellyfin);
|
||||
const albums = useTypedSelector(state => state.music.albums.entities);
|
||||
const tracks = useTypedSelector(state => state.music.tracks.entities);
|
||||
const queue = useQueue();
|
||||
|
||||
return useCallback(async function playAlbum(albumId: string) {
|
||||
return useCallback(async function playAlbum(albumId: string, play = true): Promise<TrackPlayer.Track[] | undefined> {
|
||||
const album = albums[albumId];
|
||||
const trackIds = album?.Tracks;
|
||||
|
||||
@@ -21,6 +26,29 @@ export default function usePlayAlbum() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the queue already contains the consecutive track listing
|
||||
// that is described as part of the album
|
||||
const queuedAlbum = queue.reduce<TrackPlayer.Track[]>((sum, track) => {
|
||||
if (track.id.startsWith(trackIds[sum.length])) {
|
||||
sum.push(track);
|
||||
} else {
|
||||
sum = [];
|
||||
}
|
||||
|
||||
return sum;
|
||||
}, []);
|
||||
|
||||
// If the entire album is already in the queue, we can just return those
|
||||
// tracks, rather than adding it to the queue again.
|
||||
if (queuedAlbum.length === trackIds.length) {
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return queuedAlbum;
|
||||
}
|
||||
|
||||
// Convert all trackIds to the relevant format for react-native-track-player
|
||||
const newTracks = trackIds.map((trackId) => {
|
||||
const track = tracks[trackId];
|
||||
@@ -34,7 +62,15 @@ export default function usePlayAlbum() {
|
||||
// Clear the queue and add all tracks
|
||||
await TrackPlayer.removeUpcomingTracks();
|
||||
await TrackPlayer.add(newTracks);
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
TrackPlayer.play();
|
||||
}, [credentials, albums, tracks]);
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTracks;
|
||||
}, [credentials, albums, tracks, queue, dispatch]);
|
||||
}
|
||||
@@ -3,17 +3,20 @@ import TrackPlayer from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { generateTrack } from './JellyfinApi';
|
||||
import useQueue from './useQueue';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import player from 'store/player';
|
||||
|
||||
/**
|
||||
* A hook that generates a callback that can setup and start playing a
|
||||
* particular trackId in the player.
|
||||
*/
|
||||
export default function usePlayTrack() {
|
||||
const dispatch = useDispatch();
|
||||
const credentials = useTypedSelector(state => state.settings.jellyfin);
|
||||
const tracks = useTypedSelector(state => state.music.tracks.entities);
|
||||
const queue = useQueue();
|
||||
|
||||
return useCallback(async function playTrack(trackId: string) {
|
||||
return useCallback(async function playTrack(trackId: string, play = true, addToEnd = true) {
|
||||
// Get the relevant track
|
||||
const track = tracks[trackId];
|
||||
|
||||
@@ -30,10 +33,35 @@ export default function usePlayTrack() {
|
||||
...(trackInstances.length ? trackInstances[0] : generateTrack(track, credentials)),
|
||||
id: `${trackId}_${trackInstances.length}`
|
||||
};
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
|
||||
// Then, we'll need to check where to add the track
|
||||
if (addToEnd) {
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
} else {
|
||||
// Try and locate the current track
|
||||
const currentTrackId = await TrackPlayer.getCurrentTrack();
|
||||
const currentTrackIndex = queue.findIndex(track => track.id === currentTrackId);
|
||||
|
||||
// Since the argument is the id to insert the track BEFORE, we need
|
||||
// to get the current track + 1
|
||||
const targetTrack = currentTrackIndex >= 0 && queue.length > 1
|
||||
? queue[currentTrackIndex + 1].id
|
||||
: undefined;
|
||||
|
||||
// Depending on whether this track exists, we either add it there,
|
||||
// or at the end of the queue.
|
||||
await TrackPlayer.add([ newTrack ], targetTrack);
|
||||
}
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
// Then we'll skip to it and play it
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
TrackPlayer.play();
|
||||
}, [credentials, tracks, queue]);
|
||||
if (play) {
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTrack;
|
||||
}, [credentials, tracks, queue, dispatch]);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
|
||||
/**
|
||||
* This hook retrieves the current playing track from TrackPlayer
|
||||
@@ -7,10 +8,11 @@ import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player'
|
||||
export default function useQueue(): Track[] {
|
||||
const state = usePlaybackState();
|
||||
const [queue, setQueue] = useState<Track[]>([]);
|
||||
const addedTrackCount = useTypedSelector(state => state.player);
|
||||
|
||||
useEffect(() => {
|
||||
TrackPlayer.getQueue().then(setQueue);
|
||||
}, [state]);
|
||||
}, [state, addedTrackCount]);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"baseUrl": "./src", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
"paths": {
|
||||
"@localisation": ["./localisation"]
|
||||
}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
|
||||