Compare commits
78 Commits
v0.0.1-alp
...
v0.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ba83b8efc | ||
|
|
e4daccfe5e | ||
|
|
76c45623a0 | ||
|
|
e4b93ec8c8 | ||
|
|
4635266273 | ||
|
|
8dc287e56a | ||
|
|
6cfa8f7624 | ||
|
|
6cf5b8167b | ||
|
|
dc76ea27d3 | ||
|
|
7adc96ba12 | ||
|
|
42eb7a169b | ||
|
|
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 |
15
.eslintrc.js
@@ -8,7 +8,7 @@ module.exports = {
|
||||
'plugin:react/recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
// "plugin:@typescript-eslint/recommended"
|
||||
// 'plugin:@typescript-eslint/recommended'
|
||||
],
|
||||
globals: {
|
||||
Atomics: 'readonly',
|
||||
@@ -28,14 +28,15 @@ module.exports = {
|
||||
'react-hooks'
|
||||
],
|
||||
rules: {
|
||||
indent: [
|
||||
indent: 'off',
|
||||
'@typescript-eslint/indent': [
|
||||
'error',
|
||||
4,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
}
|
||||
],
|
||||
"linebreak-style": [
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'unix'
|
||||
],
|
||||
@@ -47,10 +48,10 @@ module.exports = {
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
"no-unused-vars": "off",
|
||||
"react/prop-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error"
|
||||
'no-unused-vars': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error'
|
||||
]
|
||||
}
|
||||
};
|
||||
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.426.0)
|
||||
aws-sdk-core (3.112.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.42.0)
|
||||
aws-sdk-core (~> 3, >= 3.112.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.88.0)
|
||||
aws-sdk-core (~> 3, >= 3.112.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.79.0)
|
||||
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.2)
|
||||
fastlane (2.174.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.2.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.1)
|
||||
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,11 @@
|
||||
// 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 { setupSentry } from 'utility/Sentry';
|
||||
|
||||
setupSentry();
|
||||
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 = 11;
|
||||
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 = 11;
|
||||
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>11</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>11</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
|
||||
605
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,275 @@ 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
|
||||
- react-native-slider (3.0.0):
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsinspector (0.63.4)
|
||||
- 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.3):
|
||||
- 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.10.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 +353,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/`)
|
||||
@@ -386,15 +399,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 +428,7 @@ SPEC REPOS:
|
||||
- OpenSSL-Universal
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
- YogaKit
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -433,6 +448,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:
|
||||
@@ -483,10 +500,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 +517,60 @@ 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-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: 36561eaf7508e67f72d8c959b713bac841f3652e
|
||||
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: 03f587ba19a7e2d9fe48fb9aa6e33ace5fd07dd5
|
||||
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
|
||||
|
||||
10672
package-lock.json
generated
90
package.json
@@ -1,66 +1,74 @@
|
||||
{
|
||||
"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.8",
|
||||
"@react-navigation/native": "^5.9.3",
|
||||
"@react-navigation/stack": "^5.14.3",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@sentry/react-native": "^2.3.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.19.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"i18n-js": "^3.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "16.13.1",
|
||||
"react-native": "^0.63.4",
|
||||
"react-native-collapsible": "^1.5.3",
|
||||
"react-native-dotenv": "^2.5.1",
|
||||
"react-native-fast-image": "^8.3.4",
|
||||
"react-native-gesture-handler": "^1.10.3",
|
||||
"react-native-localize": "^2.0.2",
|
||||
"react-native-reanimated": "^2.0.0",
|
||||
"react-native-safe-area-context": "^3.2.0",
|
||||
"react-native-screens": "^2.18.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.13.10",
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@react-native-community/eslint-config": "^2.0.0",
|
||||
"@sentry/cli": "^1.63.1",
|
||||
"@types/i18n-js": "^3.8.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/react-native": "^0.63.51",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
"@types/react-test-renderer": "^17.0.1",
|
||||
"@types/styled-components-react-native": "^5.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.17.0",
|
||||
"@typescript-eslint/parser": "^4.17.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "^7.21.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^26.6.3",
|
||||
"metro-react-native-babel-preset": "^0.65.2",
|
||||
"react-test-renderer": "^17.0.1",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native",
|
||||
|
||||
BIN
src/assets/app-icon-white.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
1
src/assets/queue-append.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.39 84.08"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Regular-M"><path d="M3.66,0A3.62,3.62,0,0,0,0,3.66,3.67,3.67,0,0,0,3.66,7.37h91a3.75,3.75,0,0,0,3.76-3.71A3.71,3.71,0,0,0,94.63,0Zm0,25.54A3.67,3.67,0,0,0,0,29.25a3.62,3.62,0,0,0,3.66,3.66h91a3.71,3.71,0,0,0,3.76-3.66,3.75,3.75,0,0,0-3.76-3.71ZM.05,50.68v11c0,6.93,4.88,11.18,11.77,11.18h14.4V78c0,4,3.76,5.47,6.84,3l13.23-10.6a3.71,3.71,0,0,0,0-6L33.06,53.86c-3-2.35-6.84-.88-6.84,3v4.88H12.89A1.51,1.51,0,0,1,11.23,60V50.68c0-4-2.05-6.44-5.61-6.44S.05,46.73.05,50.68Zm61.18.44a3.69,3.69,0,0,0,0,7.38h33.4a3.69,3.69,0,1,0,0-7.38Zm0,25.59a3.69,3.69,0,0,0,0,7.37h33.4a3.69,3.69,0,1,0,0-7.37Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 758 B |
1
src/assets/queue-prepend.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.39 84.08"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Regular-M"><path d="M61.23,7.37h33.4a3.72,3.72,0,0,0,3.76-3.71A3.71,3.71,0,0,0,94.63,0H61.23a3.62,3.62,0,0,0-3.66,3.66A3.64,3.64,0,0,0,61.23,7.37ZM.05,33.4c0,4,2,6.44,5.57,6.44s5.61-2.49,5.61-6.44V24.07a1.51,1.51,0,0,1,1.66-1.71H26.22v4.89c0,3.85,3.86,5.32,6.84,3L46.29,19.68a3.71,3.71,0,0,0,0-6L33.06,3.12c-3.08-2.49-6.84-1-6.84,3v5.08H11.82C4.93,11.18.05,15.43.05,22.36ZM61.23,33h33.4a3.69,3.69,0,1,0,0-7.37H61.23a3.69,3.69,0,0,0,0,7.37ZM3.66,58.54h91a3.75,3.75,0,0,0,3.76-3.71,3.71,3.71,0,0,0-3.76-3.66h-91A3.62,3.62,0,0,0,0,54.83,3.67,3.67,0,0,0,3.66,58.54Zm0,25.54h91a3.71,3.71,0,0,0,3.76-3.66,3.75,3.75,0,0,0-3.76-3.71h-91A3.67,3.67,0,0,0,0,80.42,3.62,3.62,0,0,0,3.66,84.08Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 848 B |
1
src/assets/queue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.39 74.76"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Regular-M"><path d="M4,15.17,12.65,10a2.45,2.45,0,0,0,0-4.11L4,.62C2-.6,0,0,0,2.33V13.46C0,15.71,2.05,16.29,4,15.17ZM31.3,11.75H94.43a3.93,3.93,0,1,0,0-7.86H31.3a3.88,3.88,0,0,0-4,4A3.87,3.87,0,0,0,31.3,11.75ZM4,44.61l8.69-5.17a2.44,2.44,0,0,0,0-4.1L4,30.06c-2-1.17-4-.58-4,1.71V42.91C0,45.15,2.05,45.74,4,44.61ZM31.3,41.29H94.43a3.93,3.93,0,1,0,0-7.86H31.3a3.93,3.93,0,1,0,0,7.86ZM4,74.2,12.65,69a2.44,2.44,0,0,0,0-4.1L4,59.65C2,58.48,0,59,0,61.36V72.5C0,74.74,2.05,75.33,4,74.2ZM31.3,70.79H94.43a3.93,3.93,0,1,0,0-7.86H31.3a3.93,3.93,0,1,0,0,7.86Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 717 B |
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 |
@@ -9,10 +9,10 @@ import {
|
||||
TapGestureHandlerGestureEvent
|
||||
} from 'react-native-gesture-handler';
|
||||
|
||||
interface LetterContainerProps {
|
||||
onPress: (letter: string) => void;
|
||||
letter: string;
|
||||
}
|
||||
// interface LetterContainerProps {
|
||||
// onPress: (letter: string) => void;
|
||||
// letter: string;
|
||||
// }
|
||||
|
||||
const Container = styled.View`
|
||||
position: absolute;
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useEffect } 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 Routes from '../screens';
|
||||
import store, { persistedStore } from 'store';
|
||||
import {
|
||||
NavigationContainer,
|
||||
DefaultTheme,
|
||||
DarkTheme,
|
||||
} from '@react-navigation/native';
|
||||
import { useColorScheme } from 'react-native';
|
||||
import { ColorSchemeContext, themes } from './Colors';
|
||||
import ErrorReportingAlert from 'utility/ErrorReportingAlert';
|
||||
|
||||
interface State {
|
||||
isReady: boolean;
|
||||
}
|
||||
export default function App(): JSX.Element {
|
||||
const colorScheme = useColorScheme();
|
||||
// const colorScheme = 'dark';
|
||||
const theme = themes[colorScheme || 'light'];
|
||||
|
||||
export default class App extends Component<State> {
|
||||
state = {
|
||||
isReady: false
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
await TrackPlayer.setupPlayer();
|
||||
await TrackPlayer.updateOptions({
|
||||
capabilities: [
|
||||
TrackPlayer.CAPABILITY_PLAY,
|
||||
TrackPlayer.CAPABILITY_PAUSE,
|
||||
TrackPlayer.CAPABILITY_SKIP_TO_NEXT,
|
||||
TrackPlayer.CAPABILITY_SKIP_TO_PREVIOUS,
|
||||
TrackPlayer.CAPABILITY_STOP,
|
||||
TrackPlayer.CAPABILITY_SEEK_TO,
|
||||
]
|
||||
});
|
||||
this.setState({ isReady: true });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isReady } = this.state;
|
||||
|
||||
if (!isReady) {
|
||||
return null;
|
||||
useEffect(() => {
|
||||
async function setupTrackPlayer() {
|
||||
await TrackPlayer.setupPlayer();
|
||||
await TrackPlayer.updateOptions({
|
||||
capabilities: [
|
||||
TrackPlayer.CAPABILITY_PLAY,
|
||||
TrackPlayer.CAPABILITY_PAUSE,
|
||||
TrackPlayer.CAPABILITY_SKIP_TO_NEXT,
|
||||
TrackPlayer.CAPABILITY_SKIP_TO_PREVIOUS,
|
||||
TrackPlayer.CAPABILITY_STOP,
|
||||
TrackPlayer.CAPABILITY_SEEK_TO,
|
||||
]
|
||||
});
|
||||
}
|
||||
setupTrackPlayer();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistedStore}>
|
||||
<NavigationContainer>
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistedStore}>
|
||||
<ColorSchemeContext.Provider value={theme}>
|
||||
<NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<Routes />
|
||||
</NavigationContainer>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
</ColorSchemeContext.Provider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
70
src/components/Button.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { SvgProps } from 'react-native-svg';
|
||||
import {
|
||||
PressableProps, ViewProps,
|
||||
} from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import useDefaultStyles from './Colors';
|
||||
|
||||
interface ButtonProps extends PressableProps {
|
||||
icon?: React.FC<SvgProps>;
|
||||
title: string;
|
||||
style?: ViewProps['style'];
|
||||
}
|
||||
|
||||
interface PressableStyleProps {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
const BaseButton = styled.Pressable<PressableStyleProps>`
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
|
||||
${props => props.active && css`
|
||||
background-color: ${THEME_COLOR};
|
||||
`}
|
||||
`;
|
||||
|
||||
const ButtonText = styled.Text<{ active?: boolean }>`
|
||||
color: ${THEME_COLOR};
|
||||
font-weight: 600;
|
||||
|
||||
${props => props.active && css`
|
||||
color: white;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default function Button(props: ButtonProps) {
|
||||
const { icon: Icon, title, ...rest } = props;
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const [isPressed, setPressed] = useState(false);
|
||||
const handlePressIn = useCallback(() => setPressed(true), []);
|
||||
const handlePressOut = useCallback(() => setPressed(false), []);
|
||||
|
||||
return (
|
||||
<BaseButton
|
||||
{...rest}
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
active={isPressed}
|
||||
style={[ defaultStyles.button, props.style ]}
|
||||
>
|
||||
{Icon &&
|
||||
<Icon
|
||||
width={12}
|
||||
height={12}
|
||||
fill={isPressed ? '#fff' : THEME_COLOR}
|
||||
style={{
|
||||
marginRight: 8,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
<ButtonText active={isPressed}>{title}</ButtonText>
|
||||
</BaseButton>
|
||||
);
|
||||
}
|
||||
80
src/components/Colors.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { ColorSchemeName, StyleSheet } from 'react-native';
|
||||
|
||||
/**
|
||||
* Function for generating both the dark and light stylesheets, so that they
|
||||
* don't have to be generate on every individual component render
|
||||
*/
|
||||
function generateStyles(scheme: ColorSchemeName) {
|
||||
return StyleSheet.create({
|
||||
text: {
|
||||
color: scheme === 'dark' ? '#fff' : '#000',
|
||||
},
|
||||
textHalfOpacity: {
|
||||
color: scheme === 'dark' ? '#ffffff88' : '#00000088',
|
||||
},
|
||||
view: {
|
||||
backgroundColor: scheme === 'dark' ? '#111' : '#f6f6f6',
|
||||
},
|
||||
border: {
|
||||
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
|
||||
},
|
||||
activeBackground: {
|
||||
backgroundColor: `${THEME_COLOR}${scheme === 'dark' ? '66' : '16'}`,
|
||||
},
|
||||
imageBackground: {
|
||||
backgroundColor: scheme === 'dark' ? '#333' : '#ddd',
|
||||
},
|
||||
modal: {
|
||||
backgroundColor: scheme === 'dark' ? '#222222ee' : '#eeeeeeee',
|
||||
},
|
||||
modalInner: {
|
||||
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
|
||||
},
|
||||
button: {
|
||||
backgroundColor: scheme === 'dark' ? '#161616' : '#e6e6e6',
|
||||
},
|
||||
input: {
|
||||
backgroundColor: scheme === 'dark' ? '#161616' : '#e6e6e6',
|
||||
color: scheme === 'dark' ? '#fff' : '#000',
|
||||
},
|
||||
sectionHeading: {
|
||||
backgroundColor: scheme === 'dark' ? '#111' : '#eee',
|
||||
borderColor: scheme === 'dark' ? '#333' : '#ddd',
|
||||
},
|
||||
stackHeader: {
|
||||
color: scheme === 'dark' ? 'white' : 'black'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Prerender both stylesheets
|
||||
export const themes: Record<'dark' | 'light', ReturnType<typeof generateStyles>> = {
|
||||
'dark': generateStyles('dark'),
|
||||
'light': generateStyles('light'),
|
||||
};
|
||||
|
||||
// Create context for supplying the theming information
|
||||
export const ColorSchemeContext = React.createContext(themes.dark);
|
||||
|
||||
/**
|
||||
* Retrieves the default styles object in hook form
|
||||
*/
|
||||
export default function useDefaultStyles() {
|
||||
return useContext(ColorSchemeContext);
|
||||
}
|
||||
|
||||
interface DefaultStylesProviderProps {
|
||||
children: (defaultStyles: ReturnType<typeof useDefaultStyles>) => JSX.Element;
|
||||
}
|
||||
|
||||
/**
|
||||
* A render props component to supply the defaultStyles object.
|
||||
*/
|
||||
export function DefaultStylesProvider(props: DefaultStylesProviderProps) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
return props.children(defaultStyles);
|
||||
}
|
||||
@@ -2,8 +2,7 @@ import styled from 'styled-components/native';
|
||||
|
||||
const Input = styled.TextInput`
|
||||
margin: 10px 0;
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 5px;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,24 +1,48 @@
|
||||
import React from 'react';
|
||||
import { TouchableOpacityProps, Text } from 'react-native';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { TouchableOpacityProps } from 'react-native';
|
||||
import ChevronRight from 'assets/chevron-right.svg';
|
||||
import styled from 'styled-components/native';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import useDefaultStyles from './Colors';
|
||||
|
||||
const BUTTON_SIZE = 14;
|
||||
|
||||
const Container = styled.TouchableOpacity`
|
||||
padding: 18px 0;
|
||||
const Container = styled.Pressable<{ active?: boolean }>`
|
||||
padding: 18px 20px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: #eee;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
${props => props.active && css`
|
||||
background-color: ${THEME_COLOR};
|
||||
`}
|
||||
`;
|
||||
|
||||
const Label = styled.Text<{ active?: boolean }>`
|
||||
color: ${THEME_COLOR};
|
||||
font-size: 16px;
|
||||
|
||||
${props => props.active && css`
|
||||
color: white;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ListButton: React.FC<TouchableOpacityProps> = ({ children, ...props }) => {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const [isPressed, setPressed] = useState(false);
|
||||
const handlePressIn = useCallback(() => setPressed(true), []);
|
||||
const handlePressOut = useCallback(() => setPressed(false), []);
|
||||
|
||||
return (
|
||||
<Container {...props}>
|
||||
<Text style={{ color: THEME_COLOR, fontSize: 16 }}>{children}</Text>
|
||||
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={THEME_COLOR} />
|
||||
<Container
|
||||
{...props}
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
style={defaultStyles.border}
|
||||
active={isPressed}
|
||||
>
|
||||
<Label active={isPressed}>{children}</Label>
|
||||
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={isPressed ? '#fff' : THEME_COLOR} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
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 { useNavigation, StackActions } from '@react-navigation/native';
|
||||
import useDefaultStyles from './Colors';
|
||||
|
||||
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 defaultStyles = useDefaultStyles();
|
||||
const navigation = useNavigation();
|
||||
const closeModal = useCallback(() => {
|
||||
navigation.dispatch(StackActions.popToTop());
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<Background>
|
||||
<SafeAreaView style={{ flex: 1}}>
|
||||
<Container>
|
||||
<Background style={defaultStyles.modal} onPress={closeModal}>
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Container style={defaultStyles.modalInner} fullSize={fullSize}>
|
||||
{children}
|
||||
</Container>
|
||||
</SafeAreaView>
|
||||
|
||||
11
src/components/Text.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { Text as BaseText, TextProps } from 'react-native';
|
||||
import useDefaultStyles from './Colors';
|
||||
|
||||
export default function Text(props: PropsWithChildren<TextProps>) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
return (
|
||||
<BaseText {...props} style={[defaultStyles.text, props.style]} />
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import styled from 'styled-components/native';
|
||||
import Text from './Text';
|
||||
|
||||
export const Header = styled.Text`
|
||||
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;
|
||||
`;
|
||||
4
src/custom.d.ts
vendored
@@ -2,4 +2,8 @@ declare module '*.svg' {
|
||||
import { SvgProps } from 'react-native-svg';
|
||||
const content: React.FC<SvgProps>;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '@env' {
|
||||
export const SENTRY_DSN: string;
|
||||
}
|
||||
50
src/localisation/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
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
|
||||
let locale = findBestAvailableLanguage(Object.keys(localeGetters));
|
||||
|
||||
// Check if the locale is correctly picked
|
||||
if (!locale || !locale.languageTag) {
|
||||
// Some users might not list English as a fallback language, and hence might not
|
||||
// be assigned a locale. In this case, we'll just default to English.
|
||||
locale = {
|
||||
languageTag: 'en',
|
||||
isRTL: false,
|
||||
};
|
||||
}
|
||||
|
||||
// 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;
|
||||
42
src/localisation/lang/en/locale.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"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",
|
||||
"error-reporting": "Error Reporting",
|
||||
"error-reporting-description": "During use of this app, you may encounter errors. Reporting these errors helps in creating a more secure and stable app experience.",
|
||||
"error-reporting-rationale": "When you enable error reporting, every time an error occurs, a report is automatically created and sent to a server, along with helpful debugging information such as devices, versions and the specific error.",
|
||||
"why-use-tracking": "Why use tracking?",
|
||||
"why-use-tracking-description": "Tracking helps speed up development for this app by reporting weird edge cases and oversights. This helps make the app more stable and robust, thus increasing the app experience for everyone.",
|
||||
"what-data-is-gathered": "What data is gathered?",
|
||||
"what-data-is-gathered-description": "We log the error, device type, OS version, app version and device id. No application state is sent in any error reporting. The device id is a unique hash that can be reset in your device settings, and we cannot deduce any personal information from this identifier.",
|
||||
"where-is-data-stored": "Where is data stored?",
|
||||
"where-is-data-stored-description": "The Sentry backend is self-hosted on our own infrastructure. No-one but us has access to the servers, databases, application and data logs, least of all any Sentry staff. The infrastructure is hosted in the European Union.",
|
||||
"enable-error-reporting": "Do you want to enable error reporting?",
|
||||
"enable-error-reporting-description": "This helps improve the app experience by sending crash and error reports to us.",
|
||||
"enable": "Enable",
|
||||
"disable": "Disable",
|
||||
"more-info": "More Info"
|
||||
}
|
||||
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"
|
||||
}
|
||||
42
src/localisation/lang/nl/locale.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"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",
|
||||
"error-reporting": "Foutmeldingen Rapporteren",
|
||||
"error-reporting-description": "Gedurende het gebruik van deze app kun je tegen fouten aanlopen. Het rapporteren van deze foutmeldingen helpt om een veiligere en stabielere app-ervaring te maken.",
|
||||
"error-reporting-rationale": "Wanneer je foutmeldingen aanzet, wordt er elke keer als je een foutmelding krijgt een automatisch rapport gegenereerd en naar onze server gestuurd. Deze bevat behulpzame informatie, zoals apparaat, versies en de specifieke foutmelding.",
|
||||
"why-use-tracking": "Waarom tracking gebruiken?",
|
||||
"why-use-tracking-description": "Tracking helpt het versnellen van het ontwikkeltempo doordat we beter inzicht hebben op rare gevallen en dingen die we gemist hebben. Dit helpt met het stabieler en veiliger maken van de voor iedereen.",
|
||||
"what-data-is-gathered": "Welke data wordt er verzameld?",
|
||||
"what-data-is-gathered-description": "We loggen de foutmeldingen, je apparaattype, versienummers en een device id. Geen applicatiestaat wordt gedeeld in de rapportage. Het device id is een unieke hash die gereset kan worden in de instellingen van je apparaat. Wij kunnen geen verdere informatie afleiden van deze identifier.",
|
||||
"where-is-data-stored": "Waar wordt de data opgeslagen?",
|
||||
"where-is-data-stored-description": "De Sentry backend wordt door onszelf gehost op onze eigen infrastructuur. Niemand behalve wij heeft toegang tot de servers, databases, applicaties en data logs, en zeker geen Sentry-werknemers. De infrastructuur wordt gehost in de Europese Unie.",
|
||||
"enable-error-reporting": "Wil je foutmeldingen rapporteren?",
|
||||
"enable-error-reporting-description": "Dit helpt de appervaring te verbeteren door ons rapportages te sturen van crashes en andere foutmeldingen.",
|
||||
"enable": "Zet aan",
|
||||
"disable": "Zet uit",
|
||||
"more-info": "Meer informatie"
|
||||
}
|
||||
40
src/localisation/types.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
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'
|
||||
| 'error-reporting'
|
||||
| 'error-reporting-description'
|
||||
| 'error-reporting-rationale'
|
||||
| 'why-use-tracking'
|
||||
| 'why-use-tracking-description'
|
||||
| 'what-data-is-gathered'
|
||||
| 'what-data-is-gathered-description'
|
||||
| 'where-is-data-stored'
|
||||
| 'where-is-data-stored-description'
|
||||
| 'enable-error-reporting'
|
||||
| 'enable-error-reporting-description'
|
||||
| 'enable'
|
||||
| 'disable'
|
||||
| 'more-info'
|
||||
@@ -6,21 +6,23 @@ import Album from './stacks/Album';
|
||||
import RecentAlbums from './stacks/RecentAlbums';
|
||||
import Search from './stacks/Search';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { t } from '@localisation';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
|
||||
const Stack = createStackNavigator<StackParams>();
|
||||
|
||||
const navigationOptions = {
|
||||
headerTintColor: THEME_COLOR,
|
||||
headerTitleStyle: { color: 'black' }
|
||||
};
|
||||
|
||||
function MusicStack() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
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.Navigator initialRouteName="RecentAlbums" screenOptions={{
|
||||
headerTintColor: THEME_COLOR,
|
||||
headerTitleStyle: defaultStyles.stackHeader
|
||||
}}>
|
||||
<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 { StackParams } from '../types';
|
||||
import { Text, ScrollView, Dimensions, Button, RefreshControl } from 'react-native';
|
||||
import { Text, ScrollView, Dimensions, RefreshControl, StyleSheet, View } 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,35 @@ 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 TrackPlayer from 'react-native-track-player';
|
||||
import { t } from '@localisation';
|
||||
import Button from 'components/Button';
|
||||
import Play from 'assets/play.svg';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
|
||||
type Route = RouteProp<StackParams, 'Album'>;
|
||||
|
||||
const Screen = Dimensions.get('screen');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
name: {
|
||||
fontSize: 36,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
artist: {
|
||||
fontSize: 24,
|
||||
opacity: 0.5,
|
||||
marginBottom: 24
|
||||
},
|
||||
index: {
|
||||
width: 20,
|
||||
opacity: 0.5,
|
||||
marginRight: 5
|
||||
}
|
||||
});
|
||||
|
||||
const AlbumImage = styled(FastImage)`
|
||||
border-radius: 10px;
|
||||
width: ${Screen.width * 0.6}px;
|
||||
@@ -29,7 +50,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`
|
||||
@@ -40,6 +60,8 @@ const TrackContainer = styled.View<{isPlaying: boolean}>`
|
||||
`;
|
||||
|
||||
const Album: React.FC = () => {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
// Retrieve state
|
||||
const { params: { id } } = useRoute<Route>();
|
||||
const tracks = useTypedSelector((state) => state.music.tracks.entities);
|
||||
@@ -51,11 +73,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,25 +108,32 @@ 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} />
|
||||
{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 }}>
|
||||
{tracks[trackId]?.IndexNumber}
|
||||
</Text>
|
||||
<Text>{tracks[trackId]?.Name}</Text>
|
||||
</TrackContainer>
|
||||
</TouchableHandler>
|
||||
) : undefined}
|
||||
<AlbumImage source={{ uri: getImage(album?.Id) }} style={defaultStyles.imageBackground} />
|
||||
<Text style={[ defaultStyles.text, styles.name ]} >{album?.Name}</Text>
|
||||
<Text style={[ defaultStyles.text, styles.artist ]}>{album?.AlbumArtist}</Text>
|
||||
<Button title={t('play-album')} icon={Play} onPress={selectAlbum} />
|
||||
<View style={{ marginTop: 15 }}>
|
||||
{album?.Tracks?.length ? album.Tracks.map((trackId) =>
|
||||
<TouchableHandler
|
||||
key={trackId}
|
||||
id={trackId}
|
||||
onPress={selectTrack}
|
||||
onLongPress={longPressTrack}
|
||||
>
|
||||
<TrackContainer isPlaying={currentTrack?.id.startsWith(trackId) || false} style={defaultStyles.border}>
|
||||
<Text style={[ defaultStyles.text, styles.index ]}>
|
||||
{tracks[trackId]?.IndexNumber}
|
||||
</Text>
|
||||
<Text style={defaultStyles.text}>{tracks[trackId]?.Name}</Text>
|
||||
</TrackContainer>
|
||||
</TouchableHandler>
|
||||
) : undefined}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useRef, PureComponent, ReactText } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, ReactText } from 'react';
|
||||
import { useGetImage } from 'utility/JellyfinApi';
|
||||
import { Album, NavigationProp } from '../types';
|
||||
import { Text, SafeAreaView, SectionList, View } from 'react-native';
|
||||
@@ -9,12 +9,12 @@ import { useTypedSelector } from 'store';
|
||||
import { fetchAllAlbums } from 'store/music/actions';
|
||||
import { ALBUM_CACHE_AMOUNT_OF_DAYS } from 'CONSTANTS';
|
||||
import TouchableHandler from 'components/TouchableHandler';
|
||||
import ListContainer from './components/ListContainer';
|
||||
import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
||||
import { selectAlbumsByAlphabet, SectionedId } from 'store/music/selectors';
|
||||
import AlphabetScroller from 'components/AlphabetScroller';
|
||||
import { EntityId } from '@reduxjs/toolkit';
|
||||
import styled from 'styled-components/native';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
|
||||
interface VirtualizedItemInfo {
|
||||
section: SectionedId,
|
||||
@@ -39,11 +39,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,17 +50,16 @@ const SectionText = styled.Text`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
class SectionHeading extends PureComponent<{ label: string }> {
|
||||
render() {
|
||||
const { label } = this.props;
|
||||
const SectionHeading = React.memo(function SectionHeading(props: { label: string }) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const { label } = props;
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionText>{label}</SectionText>
|
||||
</SectionContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<SectionContainer style={defaultStyles.sectionHeading}>
|
||||
<SectionText style={defaultStyles.text}>{label}</SectionText>
|
||||
</SectionContainer>
|
||||
);
|
||||
});
|
||||
|
||||
interface GeneratedAlbumItemProps {
|
||||
id: ReactText;
|
||||
@@ -75,21 +73,20 @@ const HalfOpacity = styled.Text`
|
||||
opacity: 0.5;
|
||||
`;
|
||||
|
||||
class GeneratedAlbumItem extends PureComponent<GeneratedAlbumItemProps> {
|
||||
render() {
|
||||
const { id, imageUrl, name, artist, onPress } = this.props;
|
||||
const GeneratedAlbumItem = React.memo(function GeneratedAlbumItem(props: GeneratedAlbumItemProps) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const { id, imageUrl, name, artist, onPress } = props;
|
||||
|
||||
return (
|
||||
<TouchableHandler id={id as string} onPress={onPress}>
|
||||
<AlbumItem>
|
||||
<AlbumImage source={{ uri: imageUrl }} />
|
||||
<Text numberOfLines={1}>{name}</Text>
|
||||
<HalfOpacity numberOfLines={1}>{artist}</HalfOpacity>
|
||||
</AlbumItem>
|
||||
</TouchableHandler>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<TouchableHandler id={id as string} onPress={onPress}>
|
||||
<AlbumItem>
|
||||
<AlbumImage source={{ uri: imageUrl }} style={defaultStyles.imageBackground} />
|
||||
<Text numberOfLines={1} style={defaultStyles.text}>{name}</Text>
|
||||
<HalfOpacity style={defaultStyles.text} numberOfLines={1}>{artist}</HalfOpacity>
|
||||
</AlbumItem>
|
||||
</TouchableHandler>
|
||||
);
|
||||
});
|
||||
|
||||
const Albums: React.FC = () => {
|
||||
// Retrieve data from store
|
||||
@@ -153,7 +150,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 +181,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,22 +12,36 @@ import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
||||
import { useRecentAlbums } from 'store/music/selectors';
|
||||
import { Header } from 'components/Typography';
|
||||
import ListButton from 'components/ListButton';
|
||||
import { t } from '@localisation';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
columnWrapper: {
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10
|
||||
}
|
||||
});
|
||||
|
||||
const NavigationHeader: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const handleAllAlbumsClick = useCallback(() => { navigation.navigate('Albums'); }, [navigation]);
|
||||
const handleSearchClick = useCallback(() => { navigation.navigate('Search'); }, [navigation]);
|
||||
|
||||
return (
|
||||
<ListContainer>
|
||||
<ListButton onPress={handleAllAlbumsClick}>All Albums</ListButton>
|
||||
<ListButton onPress={handleSearchClick}>Search</ListButton>
|
||||
<Header>Recent Albums</Header>
|
||||
</ListContainer>
|
||||
<>
|
||||
<ListButton onPress={handleAllAlbumsClick}>{t('all-albums')}</ListButton>
|
||||
<ListButton onPress={handleSearchClick}>{t('search')}</ListButton>
|
||||
<ListContainer>
|
||||
<Header style={defaultStyles.text}>{t('recent-albums')}</Header>
|
||||
</ListContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RecentAlbums: React.FC = () => {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
// Retrieve data from store
|
||||
const { entities: albums } = useTypedSelector((state) => state.music.albums);
|
||||
const recentAlbums = useRecentAlbums(24);
|
||||
@@ -47,25 +61,24 @@ const RecentAlbums: React.FC = () => {
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<ListContainer>
|
||||
<FlatList
|
||||
data={recentAlbums as string[]}
|
||||
refreshing={isLoading}
|
||||
onRefresh={retrieveData}
|
||||
numColumns={2}
|
||||
keyExtractor={d => d}
|
||||
ListHeaderComponent={NavigationHeader}
|
||||
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>
|
||||
</AlbumItem>
|
||||
</TouchableHandler>
|
||||
)}
|
||||
/>
|
||||
</ListContainer>
|
||||
<FlatList
|
||||
data={recentAlbums as string[]}
|
||||
refreshing={isLoading}
|
||||
onRefresh={retrieveData}
|
||||
numColumns={2}
|
||||
keyExtractor={d => d}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
ListHeaderComponent={NavigationHeader}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableHandler id={item} onPress={selectAlbum}>
|
||||
<AlbumItem>
|
||||
<AlbumImage source={{ uri: getImage(item) }} style={defaultStyles.imageBackground} />
|
||||
<Text style={defaultStyles.text} numberOfLines={1}>{albums[item]?.Name}</Text>
|
||||
<Text style={defaultStyles.textHalfOpacity} numberOfLines={1}>{albums[item]?.AlbumArtist}</Text>
|
||||
</AlbumItem>
|
||||
</TouchableHandler>
|
||||
)}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 { t } from '@localisation';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
|
||||
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;
|
||||
@@ -47,11 +47,13 @@ const fuseOptions = {
|
||||
};
|
||||
|
||||
export default function Search() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
// Prepare state
|
||||
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,10 +91,10 @@ 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={defaultStyles.input} placeholder={t('search') + '...'} />
|
||||
{(searchTerm.length && !results.length) ? <Text style={{ textAlign: 'center' }}>{t('no-results')}</Text> : null}
|
||||
</Container>
|
||||
), [searchTerm, results, setSearchTerm]);
|
||||
), [searchTerm, results, setSearchTerm, defaultStyles]);
|
||||
|
||||
// GUARD: We cannot search for stuff unless Fuse is loaded with results.
|
||||
// Therefore we delay rendering to when we are certain it's there.
|
||||
@@ -105,13 +107,13 @@ export default function Search() {
|
||||
data={results}
|
||||
renderItem={({ item: { item: album } }) =>(
|
||||
<TouchableHandler id={album.Id} onPress={selectAlbum}>
|
||||
<SearchResult>
|
||||
<SearchResult style={defaultStyles.border}>
|
||||
<AlbumImage source={{ uri: getImage(album.Id) }} />
|
||||
<View>
|
||||
<Text numberOfLines={1} ellipsizeMode="tail">
|
||||
<Text numberOfLines={1} ellipsizeMode="tail" style={defaultStyles.text}>
|
||||
{album.Name} - {album.AlbumArtist}
|
||||
</Text>
|
||||
<HalfOpacity>Album</HalfOpacity>
|
||||
<HalfOpacity style={defaultStyles.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;
|
||||
@@ -70,6 +70,7 @@ export type StackParams = {
|
||||
Albums: undefined;
|
||||
Album: { id: string, album: Album };
|
||||
RecentAlbums: undefined;
|
||||
Search: undefined;
|
||||
};
|
||||
|
||||
export type NavigationProp = StackNavigationProp<StackParams>;
|
||||
|
||||
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,14 +1,18 @@
|
||||
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 { TouchableOpacity, useColorScheme } from 'react-native';
|
||||
import styled from 'styled-components/native';
|
||||
import { useHasQueue } from 'utility/useQueue';
|
||||
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 { THEME_COLOR } from 'CONSTANTS';
|
||||
|
||||
const BUTTON_SIZE = 40;
|
||||
const BUTTON_SIZE_SMALL = 25;
|
||||
|
||||
const pause = () => TrackPlayer.pause();
|
||||
const play = () => TrackPlayer.play();
|
||||
@@ -32,61 +36,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 { 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 useDefaultStyles from 'components/Colors';
|
||||
import Text from 'components/Text';
|
||||
|
||||
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: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
marginBottom: 12,
|
||||
},
|
||||
title: {
|
||||
fontSize: 18,
|
||||
marginBottom: 12,
|
||||
textAlign: 'center',
|
||||
paddingLeft: 20,
|
||||
paddingRight: 20,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default function NowPlaying() {
|
||||
const track = useCurrentTrack();
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
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={defaultStyles.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 { DefaultStylesProvider } from 'components/Colors';
|
||||
|
||||
const NumberBar = styled.View`
|
||||
flex-direction: row;
|
||||
@@ -64,23 +65,29 @@ export default class ProgressBar extends Component<{}, State> {
|
||||
|
||||
render() {
|
||||
const { position, duration, gesture } = this.state;
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Slider
|
||||
value={gesture || position}
|
||||
minimumValue={0}
|
||||
maximumValue={duration || 0}
|
||||
onValueChange={this.handleGesture}
|
||||
onSlidingComplete={this.handleEndOfGesture}
|
||||
minimumTrackTintColor={THEME_COLOR}
|
||||
disabled={!duration}
|
||||
/>
|
||||
<NumberBar>
|
||||
<Text>{getMinutes(gesture || position)}:{getSeconds(gesture || position)}</Text>
|
||||
<Text>{getMinutes(duration)}:{getSeconds(duration)}</Text>
|
||||
</NumberBar>
|
||||
</>
|
||||
<DefaultStylesProvider>
|
||||
{defaultStyle => (
|
||||
<>
|
||||
<Slider
|
||||
value={gesture || position}
|
||||
minimumValue={0}
|
||||
maximumValue={duration || 0}
|
||||
onValueChange={this.handleGesture}
|
||||
onSlidingComplete={this.handleEndOfGesture}
|
||||
minimumTrackTintColor={THEME_COLOR}
|
||||
thumbTintColor={Platform.OS === 'android' ? THEME_COLOR : undefined}
|
||||
disabled={!duration}
|
||||
/>
|
||||
<NumberBar>
|
||||
<Text style={defaultStyle.text}>{getMinutes(gesture || position)}:{getSeconds(gesture || position)}</Text>
|
||||
<Text style={defaultStyle.text}>{getMinutes(duration)}:{getSeconds(duration)}</Text>
|
||||
</NumberBar>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</DefaultStylesProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,42 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import useQueue from 'utility/useQueue';
|
||||
import { View, Text } from 'react-native';
|
||||
import { View, StyleSheet } 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 { t } from '@localisation';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
import Text from 'components/Text';
|
||||
import Button from 'components/Button';
|
||||
|
||||
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({
|
||||
trackTitle: {
|
||||
marginBottom: 2
|
||||
}
|
||||
});
|
||||
|
||||
export default function Queue() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const queue = useQueue();
|
||||
const currentTrack = useCurrentTrack();
|
||||
const currentIndex = queue.findIndex(d => d.id === currentTrack?.id);
|
||||
@@ -32,18 +44,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={[
|
||||
defaultStyles.border,
|
||||
currentTrack?.id === track.id ? defaultStyles.activeBackground : {},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.trackTitle}>{track.title}</Text>
|
||||
<Text style={defaultStyles.textHalfOpacity}>{track.artist}</Text>
|
||||
</QueueItem>
|
||||
</TouchableHandler>
|
||||
))}
|
||||
<ClearQueue>
|
||||
<Button title={t('clear-queue')} onPress={clearQueue} />
|
||||
</ClearQueue>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
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 useDefaultStyles from 'components/Colors';
|
||||
|
||||
const Container = styled.ScrollView`
|
||||
background-color: #fff;
|
||||
padding: 25px;
|
||||
`;
|
||||
const styles = StyleSheet.create({
|
||||
inner: {
|
||||
padding: 25,
|
||||
}
|
||||
});
|
||||
|
||||
export default function Player() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ScrollView contentContainerStyle={styles.inner} style={defaultStyles.view}>
|
||||
<NowPlaying />
|
||||
<MediaControls />
|
||||
<ProgressBar />
|
||||
<Queue />
|
||||
</Container>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
34
src/screens/Settings/components/Cache.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import music from 'store/music';
|
||||
import { t } from '@localisation';
|
||||
import Button from 'components/Button';
|
||||
import styled from 'styled-components/native';
|
||||
import Text from 'components/Text';
|
||||
|
||||
const ClearCache = styled(Button)`
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
const Container = styled.ScrollView`
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
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 (
|
||||
<Container>
|
||||
<Text>{t('setting-cache-description')}</Text>
|
||||
<ClearCache title={t('reset-cache')} onPress={handleClearCache} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
48
src/screens/Settings/components/Library.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import styled from 'styled-components/native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback } from 'react';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
import { NavigationProp } from '../..';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { t } from '@localisation';
|
||||
import Button from 'components/Button';
|
||||
import Text from 'components/Text';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
const Container = styled.ScrollView`
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
export default function LibrarySettings() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const { jellyfin } = useTypedSelector(state => state.settings);
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleSetLibrary = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<InputContainer>
|
||||
<Text style={defaultStyles.text}>{t('jellyfin-server-url')}</Text>
|
||||
<Input placeholder="https://jellyfin.yourserver.com/" value={jellyfin?.uri} editable={false} style={defaultStyles.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={defaultStyles.text}>{t('jellyfin-access-token')}</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.access_token} editable={false} style={defaultStyles.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={defaultStyles.text}>{t('jellyfin-user-id')}</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.user_id} editable={false} style={defaultStyles.input} />
|
||||
</InputContainer>
|
||||
<Button title={t('set-jellyfin-server')} onPress={handleSetLibrary} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
124
src/screens/Settings/components/Sentry.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import Text from 'components/Text';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Switch } from 'react-native-gesture-handler';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import { isSentryEnabled, setSentryStatus } from 'utility/Sentry';
|
||||
import Accordion from 'react-native-collapsible/Accordion';
|
||||
import ChevronIcon from 'assets/chevron-right.svg';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import useDefaultStyles, { DefaultStylesProvider } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Container = styled.ScrollView`
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
const SwitchContainer = styled.View`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16px 0;
|
||||
`;
|
||||
|
||||
const HeaderContainer = styled.View<{ isActive?: boolean }>`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16px 0 4px 0;
|
||||
|
||||
${props => props.isActive && css`
|
||||
background-color: ${THEME_COLOR};
|
||||
`}
|
||||
`;
|
||||
|
||||
const HeaderText = styled(Text)`
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
const ContentContainer = styled.View`
|
||||
margin-top: 8px;
|
||||
`;
|
||||
|
||||
const Label = styled(Text)`
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
const Chevron = styled(ChevronIcon)<{ isActive?: boolean }>`
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
transform: rotate(-90deg);
|
||||
|
||||
${props => props.isActive && css`
|
||||
transform: rotate(90deg);
|
||||
`}
|
||||
`;
|
||||
|
||||
type Question = { title: string, content: string };
|
||||
|
||||
const questions: Question[] = [
|
||||
{
|
||||
title: t('why-use-tracking'),
|
||||
content: t('why-use-tracking-description')
|
||||
},
|
||||
{
|
||||
title: t('what-data-is-gathered'),
|
||||
content: t('what-data-is-gathered-description')
|
||||
},
|
||||
{
|
||||
title: t('where-is-data-stored'),
|
||||
content: t('where-is-data-stored-description')
|
||||
}
|
||||
];
|
||||
|
||||
function renderHeader(question: Question, index: number, isActive: boolean) {
|
||||
return (
|
||||
<HeaderContainer>
|
||||
<HeaderText>{question.title}</HeaderText>
|
||||
<DefaultStylesProvider>
|
||||
{styles =>
|
||||
<Chevron fill={styles.text.color} isActive={isActive} />
|
||||
}
|
||||
</DefaultStylesProvider>
|
||||
</HeaderContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function renderContent(question: Question) {
|
||||
return (
|
||||
<ContentContainer>
|
||||
<Text>{question.content}</Text>
|
||||
</ContentContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Sentry() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const [isReportingEnabled, setReporting] = useState(isSentryEnabled);
|
||||
const [activeSections, setActiveSections] = useState<number[]>([]);
|
||||
|
||||
const toggleSwitch = () => setReporting(previousState => !previousState);
|
||||
|
||||
useEffect(() => {
|
||||
setSentryStatus(isReportingEnabled);
|
||||
});
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Text>{t('error-reporting-description')}</Text>
|
||||
<Text />
|
||||
<Text>{t('error-reporting-rationale')}</Text>
|
||||
<SwitchContainer>
|
||||
<Label>{t('error-reporting')}</Label>
|
||||
<Switch value={isReportingEnabled} onValueChange={toggleSwitch} />
|
||||
</SwitchContainer>
|
||||
<Accordion
|
||||
sections={questions}
|
||||
renderHeader={renderHeader}
|
||||
renderContent={renderContent}
|
||||
activeSections={activeSections}
|
||||
onChange={setActiveSections}
|
||||
underlayColor={defaultStyles.activeBackground.backgroundColor}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,56 +1,46 @@
|
||||
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 { SafeAreaView, ScrollView } from 'react-native';
|
||||
import Library from './components/Library';
|
||||
import Cache from './components/Cache';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '..';
|
||||
import ListButton from 'components/ListButton';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import Sentry from './components/Sentry';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
background-color: #fbfbfb;
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
export default function Settings() {
|
||||
const { jellyfin, bitrate } = useSelector((state: AppState) => state.settings);
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleClick = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
export function SettingsList() {
|
||||
const navigation = useNavigation();
|
||||
const handleLibraryClick = useCallback(() => { navigation.navigate('Library'); }, [navigation]);
|
||||
const handleCacheClick = useCallback(() => { navigation.navigate('Cache'); }, [navigation]);
|
||||
const handleSentryClick = useCallback(() => { navigation.navigate('Sentry'); }, [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>
|
||||
</View>
|
||||
<ListButton onPress={handleLibraryClick}>{t('jellyfin-library')}</ListButton>
|
||||
<ListButton onPress={handleCacheClick}>{t('setting-cache')}</ListButton>
|
||||
<ListButton onPress={handleSentryClick}>{t('error-reporting')}</ListButton>
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
export default function Settings() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="SettingList" screenOptions={{
|
||||
headerTintColor: THEME_COLOR,
|
||||
headerTitleStyle: defaultStyles.stackHeader
|
||||
}}>
|
||||
<Stack.Screen name="SettingList" component={SettingsList} options={{ headerTitle: t('settings') }} />
|
||||
<Stack.Screen name="Library" component={Library} options={{ headerTitle: t('jellyfin-library') }} />
|
||||
<Stack.Screen name="Cache" component={Cache} options={{ headerTitle: t('setting-cache') }} />
|
||||
<Stack.Screen name="Sentry" component={Sentry} options={{ headerTitle: t('error-reporting') }} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
@@ -10,8 +10,15 @@ 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';
|
||||
import ErrorReportingAlert from 'utility/ErrorReportingAlert';
|
||||
import ErrorReportingPopup from './modals/ErrorReportingPopup';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
const Stack = createStackNavigator<ModalStackParams>();
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
type Screens = {
|
||||
@@ -34,28 +41,39 @@ 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 }) => ({
|
||||
tabBarIcon: function TabBarIcon({ color, size }) {
|
||||
const Icon = getIcon(route.name);
|
||||
<>
|
||||
<ErrorReportingAlert />
|
||||
<Tab.Navigator
|
||||
screenOptions={({ route }) => ({
|
||||
tabBarIcon: function TabBarIcon({ color, size }) {
|
||||
const Icon = getIcon(route.name);
|
||||
|
||||
if (!Icon) {
|
||||
return null;
|
||||
if (!Icon) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Icon fill={color} width={size} height={size} />;
|
||||
}
|
||||
|
||||
return <Icon fill={color} width={size} height={size} />;
|
||||
}
|
||||
})}
|
||||
tabBarOptions={{
|
||||
activeTintColor: THEME_COLOR,
|
||||
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.Navigator>
|
||||
})}
|
||||
tabBarOptions={{
|
||||
activeTintColor: THEME_COLOR,
|
||||
inactiveTintColor: 'gray',
|
||||
}}
|
||||
>
|
||||
<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,11 +92,13 @@ export default function Routes() {
|
||||
}}>
|
||||
<Stack.Screen name="Screens" component={Screens} />
|
||||
<Stack.Screen name="SetJellyfinServer" component={SetJellyfinServer} />
|
||||
<Stack.Screen name="TrackPopupMenu" component={TrackPopupMenu} />
|
||||
<Stack.Screen name="ErrorReporting" component={ErrorReportingPopup} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
export type NavigationProp = CompositeNavigationProp<
|
||||
StackNavigationProp<Routes>,
|
||||
BottomTabNavigationProp<Screens>
|
||||
StackNavigationProp<Routes>,
|
||||
BottomTabNavigationProp<Screens>
|
||||
>;
|
||||
11
src/screens/modals/ErrorReportingPopup.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import Sentry from 'screens/Settings/components/Sentry';
|
||||
|
||||
export default function ErrorReportingPopup() {
|
||||
return (
|
||||
<Modal fullSize={false}>
|
||||
<Sentry />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { Text, Button, View } from 'react-native';
|
||||
import { Button, View } from 'react-native';
|
||||
import Modal from 'components/Modal';
|
||||
import Input from 'components/Input';
|
||||
import { setJellyfinCredentials } from 'store/settings/actions';
|
||||
@@ -7,8 +7,12 @@ import { useDispatch } from 'react-redux';
|
||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
import CredentialGenerator from './components/CredentialGenerator';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { t } from '@localisation';
|
||||
import useDefaultStyles from 'components/Colors';
|
||||
import Text from 'components/Text';
|
||||
|
||||
export default function SetJellyfinServer() {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
// State for first screen
|
||||
const [serverUrl, setServerUrl] = useState<string>();
|
||||
const [isLogginIn, setIsLogginIn] = useState<boolean>(false);
|
||||
@@ -31,8 +35,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>
|
||||
{t('set-jellyfin-server-instruction')}
|
||||
</Text>
|
||||
<Input
|
||||
placeholder="https://jellyfin.yourserver.io/"
|
||||
onChangeText={setServerUrl}
|
||||
@@ -40,9 +46,10 @@ export default function SetJellyfinServer() {
|
||||
keyboardType="url"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
style={[ defaultStyles.input, { width: '100%' } ]}
|
||||
/>
|
||||
<Button
|
||||
title="Set server"
|
||||
title={t('set-jellyfin-server')}
|
||||
onPress={() => setIsLogginIn(true)}
|
||||
disabled={!serverUrl?.length}
|
||||
color={THEME_COLOR}
|
||||
|
||||
67
src/screens/modals/TrackPopupMenu.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import { useNavigation, StackActions, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { ModalStackParams } from 'screens/types';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import styled from 'styled-components/native';
|
||||
import usePlayTrack from 'utility/usePlayTrack';
|
||||
import { t } from '@localisation';
|
||||
import Button from 'components/Button';
|
||||
import PlayIcon from 'assets/play.svg';
|
||||
import QueueAppendIcon from 'assets/queue-append.svg';
|
||||
import Text from 'components/Text';
|
||||
|
||||
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; */
|
||||
`;
|
||||
|
||||
const ButtonSpacing = styled.View`
|
||||
width: 8px;
|
||||
`;
|
||||
|
||||
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')} icon={PlayIcon} onPress={handlePlayNext} />
|
||||
<ButtonSpacing />
|
||||
<Button title={t('add-to-queue')} icon={QueueAppendIcon} 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;
|
||||
|
||||