Compare commits
36 Commits
v1.0.0-bet
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eab7793f5 | ||
|
|
66eb4bd61d | ||
|
|
3312e99911 | ||
|
|
3ae967e137 | ||
|
|
d18daf027e | ||
|
|
0ae7e3ba0c | ||
|
|
e918f089e2 | ||
|
|
491f019fb3 | ||
|
|
0af730dabe | ||
|
|
fadfabc868 | ||
|
|
de73fe6358 | ||
|
|
6f3ed9c988 | ||
|
|
ff484cebed | ||
|
|
1e14ed3271 | ||
|
|
954fe38531 | ||
|
|
83cf0e4d56 | ||
|
|
3b98eb4bf1 | ||
|
|
49bb4dfaab | ||
|
|
9f9e61017e | ||
|
|
65c7db9911 | ||
|
|
481a411e4c | ||
|
|
8eeddebad7 | ||
|
|
974c73e70a | ||
|
|
0988e10a09 | ||
|
|
9ea0753912 | ||
|
|
6cf421f24f | ||
|
|
0df9d4a621 | ||
|
|
6a1d75c27c | ||
|
|
c88158cf16 | ||
|
|
3026fdf4da | ||
|
|
12b53eca4a | ||
|
|
3ff971a773 | ||
|
|
8fac21f4d8 | ||
|
|
6e85013b5a | ||
|
|
edce0329cf | ||
|
|
be40d8a404 |
6
.github/workflows/fastlane.yml
vendored
6
.github/workflows/fastlane.yml
vendored
@@ -41,6 +41,12 @@ jobs:
|
||||
- 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
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -65,4 +65,7 @@ buck-out/
|
||||
build/
|
||||
fastlane/report.xml
|
||||
fastlane/Appfile
|
||||
certificates/
|
||||
certificates/
|
||||
|
||||
.env
|
||||
sentry.properties
|
||||
3
Gemfile
3
Gemfile
@@ -7,3 +7,6 @@ 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)
|
||||
|
||||
87
Gemfile.lock
87
Gemfile.lock
@@ -1,27 +1,28 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.2)
|
||||
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.355.0)
|
||||
aws-sdk-core (3.104.3)
|
||||
aws-partitions (1.421.0)
|
||||
aws-sdk-core (3.111.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.36.0)
|
||||
aws-sdk-core (~> 3, >= 3.99.0)
|
||||
aws-sdk-kms (1.41.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.78.0)
|
||||
aws-sdk-core (~> 3, >= 3.104.3)
|
||||
aws-sdk-s3 (1.87.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.3)
|
||||
babosa (1.0.4)
|
||||
claide (1.0.3)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
@@ -29,24 +30,28 @@ GEM
|
||||
highline (~> 1.7.2)
|
||||
declarative (0.0.20)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.6.1)
|
||||
rake (~> 13.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.0.0)
|
||||
excon (0.76.0)
|
||||
faraday (1.0.1)
|
||||
emoji_regex (3.2.1)
|
||||
excon (0.78.1)
|
||||
faraday (1.3.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
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.0)
|
||||
fastlane (2.156.1)
|
||||
fastimage (2.2.1)
|
||||
fastlane (2.172.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
@@ -80,6 +85,7 @@ GEM
|
||||
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)
|
||||
@@ -89,20 +95,35 @@ GEM
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.12)
|
||||
google-apis-core (0.2.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.14)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
rexml
|
||||
signet (~> 0.14)
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.1.0)
|
||||
google-apis-core (~> 0.1)
|
||||
google-apis-storage_v1 (0.1.0)
|
||||
google-apis-core (~> 0.1)
|
||||
google-cloud-core (1.5.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.3.3)
|
||||
google-cloud-env (1.4.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-errors (1.0.1)
|
||||
google-cloud-storage (1.27.0)
|
||||
google-cloud-storage (1.30.0)
|
||||
addressable (~> 2.5)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.33)
|
||||
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.13.1)
|
||||
googleauth (0.15.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
@@ -114,28 +135,30 @@ GEM
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
json (2.3.1)
|
||||
jwt (2.2.1)
|
||||
json (2.5.1)
|
||||
jwt (2.2.2)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.10.1)
|
||||
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.0)
|
||||
naturally (2.2.1)
|
||||
os (1.1.1)
|
||||
plist (3.5.0)
|
||||
public_suffix (4.0.5)
|
||||
rake (13.0.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.0)
|
||||
signet (0.14.1)
|
||||
addressable (~> 2.3)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
@@ -156,8 +179,9 @@ GEM
|
||||
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.18.0)
|
||||
xcodeproj (1.19.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
@@ -165,7 +189,7 @@ GEM
|
||||
nanaimo (~> 0.3.0)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
@@ -173,6 +197,7 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane (~> 2.153)
|
||||
fastlane-plugin-sentry
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
|
||||
@@ -60,4 +60,10 @@ fastlane android beta
|
||||
```
|
||||
|
||||
## Licensing and Credits
|
||||
This work is licensed under the MIT license and was built by Lei Nelissen.
|
||||
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.
|
||||
@@ -83,6 +83,7 @@ project.ext.react = [
|
||||
]
|
||||
|
||||
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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
2
app.json
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
BIN
docs/images/bmd-logo-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
@@ -7,7 +7,7 @@ platform :ios do
|
||||
output_path: 'certificates/'
|
||||
)
|
||||
build_app(
|
||||
scheme: "JellyfinAudioPlayer",
|
||||
scheme: "Jellyfin Player",
|
||||
export_method: "development",
|
||||
output_directory: "build",
|
||||
workspace: "ios/JellyfinAudioPlayer.xcworkspace"
|
||||
@@ -30,12 +30,48 @@ platform :ios do
|
||||
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj"
|
||||
)
|
||||
build_app(
|
||||
scheme: "JellyfinAudioPlayer",
|
||||
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
|
||||
|
||||
|
||||
5
fastlane/Pluginfile
Normal file
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'
|
||||
17
index.js
17
index.js
@@ -1,18 +1,17 @@
|
||||
// import React from 'react';
|
||||
|
||||
// // if (process.env.NODE_ENV === 'development') {
|
||||
// // const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
||||
// // whyDidYouRender(React, {
|
||||
// // trackAllPureComponents: true,
|
||||
// // });
|
||||
// // }
|
||||
|
||||
import 'react-native-gesture-handler';
|
||||
import { AppRegistry } from 'react-native';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import App from './src/components/App';
|
||||
import { name as appName } from './app.json';
|
||||
import PlaybackService from './src/utility/PlaybackService';
|
||||
import * as Sentry from '@sentry/react-native';
|
||||
import { SENTRY_DSN } from '@env';
|
||||
|
||||
if (SENTRY_DSN) {
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN
|
||||
});
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
TrackPlayer.registerPlaybackService(() => PlaybackService);
|
||||
8
ios/File.swift
Normal file
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
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>
|
||||
@@ -13,6 +13,7 @@
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
463612208457EBB4B723000A /* libPods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 842AB597D56E84A4ACDC4735 /* libPods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.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 */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -39,6 +40,8 @@
|
||||
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>"; };
|
||||
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; };
|
||||
@@ -99,6 +102,8 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
4FA1B23C2550A94C007A035E /* File.swift */,
|
||||
4FA1B23B2550A94B007A035E /* JellyfinAudioPlayer-Bridging-Header.h */,
|
||||
);
|
||||
name = JellyfinAudioPlayer;
|
||||
sourceTree = "<group>";
|
||||
@@ -196,6 +201,7 @@
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
B9FB8FC65CEFF9AFAC71127E /* [CP] Copy Pods Resources */,
|
||||
1DC46C84C90B4D84A18AC142 /* Upload Debug Symbols to Sentry */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -222,7 +228,7 @@
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
DevelopmentTeam = 238P3C58WC;
|
||||
LastSwiftMigration = 1120;
|
||||
LastSwiftMigration = 1210;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
@@ -281,7 +287,21 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export NODE_BINARY=$(which 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";
|
||||
};
|
||||
1DC46C84C90B4D84A18AC142 /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Upload Debug Symbols to Sentry";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export SENTRY_PROPERTIES=sentry.properties\n../node_modules/@sentry/cli/bin/sentry-cli upload-dsym";
|
||||
};
|
||||
B9FB8FC65CEFF9AFAC71127E /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -398,6 +418,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
|
||||
4FA1B23D2550A94C007A035E /* File.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -429,6 +450,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8DAD3DCD6450C4255A20940E /* Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
@@ -454,6 +476,7 @@
|
||||
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;
|
||||
@@ -481,7 +504,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -498,6 +521,7 @@
|
||||
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";
|
||||
@@ -511,7 +535,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
INFOPLIST_FILE = JellyfinAudioPlayer/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -524,6 +548,7 @@
|
||||
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";
|
||||
};
|
||||
@@ -562,6 +587,7 @@
|
||||
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;
|
||||
@@ -577,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)\"",
|
||||
@@ -586,6 +612,7 @@
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "Jellyfin Player";
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -623,6 +650,7 @@
|
||||
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;
|
||||
@@ -631,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)\"",
|
||||
@@ -639,6 +667,7 @@
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_NAME = "Jellyfin Player";
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3</string>
|
||||
<string>7</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3</string>
|
||||
<string>7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -17,5 +17,8 @@ target 'JellyfinAudioPlayer' do
|
||||
use_flipper!
|
||||
post_install do |installer|
|
||||
flipper_post_install(installer)
|
||||
installer.pods_project.build_configurations.each do |config|
|
||||
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
|
||||
end
|
||||
end
|
||||
end
|
||||
514
ios/Podfile.lock
514
ios/Podfile.lock
@@ -3,58 +3,58 @@ PODS:
|
||||
- CocoaAsyncSocket (7.6.4)
|
||||
- CocoaLibEvent (1.0.0)
|
||||
- DoubleConversion (1.1.6)
|
||||
- FBLazyVector (0.63.2)
|
||||
- FBReactNativeSpec (0.63.2):
|
||||
- FBLazyVector (0.63.4)
|
||||
- FBReactNativeSpec (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.63.2)
|
||||
- RCTTypeSafety (= 0.63.2)
|
||||
- React-Core (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- Flipper (0.41.5):
|
||||
- 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.41.5):
|
||||
- FlipperKit/Core (= 0.41.5)
|
||||
- FlipperKit/Core (0.41.5):
|
||||
- Flipper (~> 0.41.5)
|
||||
- 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.41.5):
|
||||
- Flipper (~> 0.41.5)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.41.5):
|
||||
- FlipperKit/CppBridge (0.54.0):
|
||||
- Flipper (~> 0.54.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
|
||||
- Flipper-Folly (~> 2.2)
|
||||
- FlipperKit/FBDefines (0.41.5)
|
||||
- FlipperKit/FKPortForwarding (0.41.5):
|
||||
- FlipperKit/FBDefines (0.54.0)
|
||||
- FlipperKit/FKPortForwarding (0.54.0):
|
||||
- CocoaAsyncSocket (~> 7.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.41.5)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.41.5):
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.54.0)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||
- YogaKit (~> 1.18)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.41.5)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.41.5):
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitReactPlugin (0.41.5):
|
||||
- FlipperKit/FlipperKitReactPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.41.5):
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.41.5):
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.54.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitNetworkPlugin
|
||||
- Folly (2020.01.13.00):
|
||||
@@ -76,269 +76,277 @@ PODS:
|
||||
- libwebp/mux (1.1.0):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.1.0)
|
||||
- OpenSSL-Universal (1.0.2.19):
|
||||
- OpenSSL-Universal/Static (= 1.0.2.19)
|
||||
- OpenSSL-Universal/Static (1.0.2.19)
|
||||
- RCTRequired (0.63.2)
|
||||
- RCTTypeSafety (0.63.2):
|
||||
- FBLazyVector (= 0.63.2)
|
||||
- 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.2)
|
||||
- React-Core (= 0.63.2)
|
||||
- React (0.63.2):
|
||||
- React-Core (= 0.63.2)
|
||||
- React-Core/DevSupport (= 0.63.2)
|
||||
- React-Core/RCTWebSocket (= 0.63.2)
|
||||
- React-RCTActionSheet (= 0.63.2)
|
||||
- React-RCTAnimation (= 0.63.2)
|
||||
- React-RCTBlob (= 0.63.2)
|
||||
- React-RCTImage (= 0.63.2)
|
||||
- React-RCTLinking (= 0.63.2)
|
||||
- React-RCTNetwork (= 0.63.2)
|
||||
- React-RCTSettings (= 0.63.2)
|
||||
- React-RCTText (= 0.63.2)
|
||||
- React-RCTVibration (= 0.63.2)
|
||||
- React-callinvoker (0.63.2)
|
||||
- React-Core (0.63.2):
|
||||
- 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.63.2)
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.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.63.2):
|
||||
- React-Core/CoreModulesHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/Default (0.63.2):
|
||||
- React-Core/Default (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/DevSupport (0.63.2):
|
||||
- React-Core/DevSupport (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default (= 0.63.2)
|
||||
- React-Core/RCTWebSocket (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-jsinspector (= 0.63.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.63.2):
|
||||
- React-Core/RCTActionSheetHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTAnimationHeaders (0.63.2):
|
||||
- React-Core/RCTAnimationHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTBlobHeaders (0.63.2):
|
||||
- React-Core/RCTBlobHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTImageHeaders (0.63.2):
|
||||
- React-Core/RCTImageHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTLinkingHeaders (0.63.2):
|
||||
- React-Core/RCTLinkingHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTNetworkHeaders (0.63.2):
|
||||
- React-Core/RCTNetworkHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTSettingsHeaders (0.63.2):
|
||||
- React-Core/RCTSettingsHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTTextHeaders (0.63.2):
|
||||
- React-Core/RCTTextHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTVibrationHeaders (0.63.2):
|
||||
- React-Core/RCTVibrationHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- Yoga
|
||||
- React-Core/RCTWebSocket (0.63.2):
|
||||
- React-Core/RCTWebSocket (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-Core/Default (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsiexecutor (= 0.63.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.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- React-CoreModules (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.2)
|
||||
- React-Core/CoreModulesHeaders (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-RCTImage (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-cxxreact (0.63.2):
|
||||
- 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 (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-callinvoker (= 0.63.2)
|
||||
- React-jsinspector (= 0.63.2)
|
||||
- React-jsi (0.63.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 (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsi/Default (= 0.63.2)
|
||||
- React-jsi/Default (0.63.2):
|
||||
- React-jsi/Default (= 0.63.4)
|
||||
- React-jsi/Default (0.63.4):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsiexecutor (0.63.2):
|
||||
- React-jsiexecutor (0.63.4):
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-jsinspector (0.63.2)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsinspector (0.63.4)
|
||||
- react-native-appearance (0.3.4):
|
||||
- React
|
||||
- react-native-safe-area-context (3.1.1):
|
||||
- React
|
||||
- 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.63.2):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.63.2)
|
||||
- React-RCTAnimation (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- react-native-webview (11.2.1):
|
||||
- React-Core
|
||||
- React-RCTActionSheet (0.63.4):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.63.4)
|
||||
- React-RCTAnimation (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.2)
|
||||
- React-Core/RCTAnimationHeaders (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-RCTBlob (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- 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.2)
|
||||
- React-Core/RCTWebSocket (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-RCTNetwork (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-RCTImage (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- 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.2)
|
||||
- React-Core/RCTImageHeaders (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- React-RCTNetwork (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-RCTLinking (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- React-Core/RCTLinkingHeaders (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-RCTNetwork (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- 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.2)
|
||||
- React-Core/RCTNetworkHeaders (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-RCTSettings (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- 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.2)
|
||||
- React-Core/RCTSettingsHeaders (= 0.63.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- React-RCTText (0.63.2):
|
||||
- React-Core/RCTTextHeaders (= 0.63.2)
|
||||
- React-RCTVibration (0.63.2):
|
||||
- FBReactNativeSpec (= 0.63.2)
|
||||
- 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.2)
|
||||
- React-jsi (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (= 0.63.2)
|
||||
- ReactCommon/turbomodule/core (0.63.2):
|
||||
- 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 (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-callinvoker (= 0.63.2)
|
||||
- React-Core (= 0.63.2)
|
||||
- React-cxxreact (= 0.63.2)
|
||||
- React-jsi (= 0.63.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.3.2):
|
||||
- React
|
||||
- RNCPicker (1.8.1):
|
||||
- React-Core
|
||||
- RNFastImage (8.3.4):
|
||||
- React-Core
|
||||
- SDWebImage (~> 5.8)
|
||||
- SDWebImageWebPCoder (~> 0.6.1)
|
||||
- RNGestureHandler (1.7.0):
|
||||
- React
|
||||
- RNReanimated (1.10.1):
|
||||
- React
|
||||
- RNScreens (2.9.0):
|
||||
- React
|
||||
- RNGestureHandler (1.9.0):
|
||||
- React-Core
|
||||
- RNLocalize (2.0.1):
|
||||
- React-Core
|
||||
- RNReanimated (1.13.2):
|
||||
- React-Core
|
||||
- RNScreens (2.17.1):
|
||||
- React-Core
|
||||
- RNSentry (2.2.0):
|
||||
- React-Core
|
||||
- Sentry (= 6.1.4)
|
||||
- RNSVG (12.1.0):
|
||||
- React
|
||||
- SDWebImage (5.8.1):
|
||||
- SDWebImage/Core (= 5.8.1)
|
||||
- SDWebImage/Core (5.8.1)
|
||||
- SDWebImage (5.10.4):
|
||||
- SDWebImage/Core (= 5.10.4)
|
||||
- SDWebImage/Core (5.10.4)
|
||||
- SDWebImageWebPCoder (0.6.1):
|
||||
- libwebp (~> 1.0)
|
||||
- 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)
|
||||
@@ -347,25 +355,25 @@ 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.41.1)
|
||||
- Flipper (~> 0.54.0)
|
||||
- Flipper-DoubleConversion (= 1.1.7)
|
||||
- Flipper-Folly (~> 2.2)
|
||||
- Flipper-Glog (= 0.3.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- Flipper-RSocket (~> 1.1)
|
||||
- FlipperKit (~> 0.41.1)
|
||||
- FlipperKit/Core (~> 0.41.1)
|
||||
- FlipperKit/CppBridge (~> 0.41.1)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.41.1)
|
||||
- FlipperKit/FBDefines (~> 0.41.1)
|
||||
- FlipperKit/FKPortForwarding (~> 0.41.1)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (~> 0.41.1)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (~> 0.41.1)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.41.1)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (~> 0.41.1)
|
||||
- FlipperKit/FlipperKitReactPlugin (~> 0.41.1)
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.41.1)
|
||||
- FlipperKit/SKIOSNetworkPlugin (~> 0.41.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`)
|
||||
@@ -400,8 +408,10 @@ DEPENDENCIES:
|
||||
- "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`)
|
||||
|
||||
@@ -421,6 +431,7 @@ SPEC REPOS:
|
||||
- OpenSSL-Universal
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
- YogaKit
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -494,10 +505,14 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-fast-image"
|
||||
RNGestureHandler:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNLocalize:
|
||||
:path: "../node_modules/react-native-localize"
|
||||
RNReanimated:
|
||||
:path: "../node_modules/react-native-reanimated"
|
||||
RNScreens:
|
||||
:path: "../node_modules/react-native-screens"
|
||||
RNSentry:
|
||||
:path: "../node_modules/@sentry/react-native"
|
||||
RNSVG:
|
||||
:path: "../node_modules/react-native-svg"
|
||||
Yoga:
|
||||
@@ -508,57 +523,60 @@ SPEC CHECKSUMS:
|
||||
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
|
||||
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
|
||||
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||
FBLazyVector: 3ef4a7f62e7db01092f9d517d2ebc0d0677c4a37
|
||||
FBReactNativeSpec: dc7fa9088f0f2a998503a352b0554d69a4391c5a
|
||||
Flipper: 33585e2d9810fe5528346be33bcf71b37bb7ae13
|
||||
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: bc68102cd4952a258a23c9c1b316c7bec1fecf83
|
||||
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
|
||||
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
|
||||
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
|
||||
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
|
||||
RCTRequired: f13f25e7b12f925f1f6a6a8c69d929a03c0129fe
|
||||
RCTTypeSafety: 44982c5c8e43ff4141eb519a8ddc88059acd1f3a
|
||||
React: e1c65dd41cb9db13b99f24608e47dd595f28ca9a
|
||||
React-callinvoker: 552a6a6bc8b3bb794cf108ad59e5a9e2e3b4fc98
|
||||
React-Core: 9d341e725dc9cd2f49e4c49ad1fc4e8776aa2639
|
||||
React-CoreModules: 5335e168165da7f7083ce7147768d36d3e292318
|
||||
React-cxxreact: d3261ec5f7d11743fbf21e263a34ea51d1f13ebc
|
||||
React-jsi: 54245e1d5f4b690dec614a73a3795964eeef13a8
|
||||
React-jsiexecutor: 8ca588cc921e70590820ce72b8789b02c67cce38
|
||||
React-jsinspector: b14e62ebe7a66e9231e9581279909f2fc3db6606
|
||||
OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
|
||||
RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e
|
||||
RCTTypeSafety: 8c9c544ecbf20337d069e4ae7fd9a377aadf504b
|
||||
React: b0a957a2c44da4113b0c4c9853d8387f8e64e615
|
||||
React-callinvoker: c3f44dd3cb195b6aa46621fff95ded79d59043fe
|
||||
React-Core: d3b2a1ac9a2c13c3bcde712d9281fc1c8a5b315b
|
||||
React-CoreModules: 0581ff36cb797da0943d424f69e7098e43e9be60
|
||||
React-cxxreact: c1480d4fda5720086c90df537ee7d285d4c57ac3
|
||||
React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31
|
||||
React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949
|
||||
React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a
|
||||
react-native-appearance: fc2014516054585d531e07aa0b40ab0de1d2be85
|
||||
react-native-safe-area-context: 4c3249e4840225c61fcd215b136af0a737bccb79
|
||||
react-native-safe-area-context: 86612d2c9a9e94e288319262d10b5f93f0b395f5
|
||||
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
||||
react-native-track-player: ba2416753b58f3cdf9db5a07daa65876d659f925
|
||||
react-native-webview: e2c0bce9a1a7c7edd4eb30f0c3016fce216245ce
|
||||
React-RCTActionSheet: 910163b6b09685a35c4ebbc52b66d1bfbbe39fc5
|
||||
React-RCTAnimation: 9a883bbe1e9d2e158d4fb53765ed64c8dc2200c6
|
||||
React-RCTBlob: 39cf0ece1927996c4466510e25d2105f67010e13
|
||||
React-RCTImage: de355d738727b09ad3692f2a979affbd54b5f378
|
||||
React-RCTLinking: 8122f221d395a63364b2c0078ce284214bd04575
|
||||
React-RCTNetwork: 8f96c7b49ea6a0f28f98258f347b6ad218bc0830
|
||||
React-RCTSettings: 8a49622aff9c1925f5455fa340b6fe4853d64ab6
|
||||
React-RCTText: 1b6773e776e4b33f90468c20fe3b16ca3e224bb8
|
||||
React-RCTVibration: 4d2e726957f4087449739b595f107c0d4b6c2d2d
|
||||
ReactCommon: a0a1edbebcac5e91338371b72ffc66aa822792ce
|
||||
RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1
|
||||
react-native-webview: dbe6c1ad149740f0e2d84a963f1d3c3e77f2d99c
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
||||
React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0
|
||||
React-RCTImage: c1b1f2d3f43a4a528c8946d6092384b5c880d2f0
|
||||
React-RCTLinking: 35ae4ab9dc0410d1fcbdce4d7623194a27214fb2
|
||||
React-RCTNetwork: 29ec2696f8d8cfff7331fac83d3e893c95ef43ae
|
||||
React-RCTSettings: 60f0691bba2074ef394f95d4c2265ec284e0a46a
|
||||
React-RCTText: 5c51df3f08cb9dedc6e790161195d12bac06101c
|
||||
React-RCTVibration: ae4f914cfe8de7d4de95ae1ea6cc8f6315d73d9d
|
||||
ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b
|
||||
RNCAsyncStorage: cb9a623793918c6699586281f0b51cbc38f046f9
|
||||
RNCMaskedView: f5c7d14d6847b7b44853f7acb6284c1da30a3459
|
||||
RNCPicker: 55b9b4240d0a9eba8733d02616775d4040de2e7d
|
||||
RNFastImage: e19ba191922e7dab9d932a4d59d62d76660aa222
|
||||
RNGestureHandler: b6b359bb800ae399a9c8b27032bdbf7c18f08a08
|
||||
RNReanimated: c2bb7438b57a3d987bb2e4e6e4bca94787e30b02
|
||||
RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
|
||||
RNCPicker: 914b557e20b3b8317b084aca9ff4b4edb95f61e4
|
||||
RNFastImage: d4870d58f5936111c56218dbd7fcfc18e65b58ff
|
||||
RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b
|
||||
RNLocalize: dcf0fdb332b37b0d24178e876a7ce4dbbc9c838d
|
||||
RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad
|
||||
RNScreens: b6c9607e6fe47c1b6e2f1910d2acd46dd7ecea3a
|
||||
RNSentry: 6aeba1adc242fd22a6826acae92f430697b47a9c
|
||||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||
SDWebImage: e3eae2eda88578db0685a0c88597fdadd9433f05
|
||||
SDWebImage: c666b97e1fa9c64b4909816a903322018f0a9c84
|
||||
SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
|
||||
Yoga: 7740b94929bbacbddda59bf115b5317e9a161598
|
||||
Sentry: 9d055e2de30a77685e86b219acf02e59b82091fc
|
||||
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 2d28e638928312f9630682b819df0581e381449e
|
||||
PODFILE CHECKSUM: d99c1202f98b3f7477b6a86c9226010b36143469
|
||||
|
||||
COCOAPODS: 1.9.1
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
9688
package-lock.json
generated
9688
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
81
package.json
81
package.json
@@ -5,63 +5,68 @@
|
||||
"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/picker": "^1.8.1",
|
||||
"@react-native-community/slider": "^3.0.3",
|
||||
"@react-navigation/bottom-tabs": "^5.7.2",
|
||||
"@react-navigation/native": "^5.7.1",
|
||||
"@react-navigation/stack": "^5.7.1",
|
||||
"@reduxjs/toolkit": "^1.4.0",
|
||||
"@types/lodash": "^4.14.158",
|
||||
"@react-navigation/bottom-tabs": "^5.11.7",
|
||||
"@react-navigation/native": "^5.9.2",
|
||||
"@react-navigation/stack": "^5.14.2",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@sentry/react-native": "^2.2.0",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/redux-logger": "^3.0.8",
|
||||
"@types/styled-components": "^5.1.1",
|
||||
"date-fns": "^2.15.0",
|
||||
"fuse.js": "^6.4.0",
|
||||
"lodash": "^4.17.19",
|
||||
"react": "^16.13.1",
|
||||
"react-native": "^0.63.2",
|
||||
"@types/styled-components": "^5.1.7",
|
||||
"date-fns": "^2.17.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"i18n-js": "^3.8.0",
|
||||
"lodash": "^4.17.20",
|
||||
"react": "^17.0.1",
|
||||
"react-native": "^0.63.4",
|
||||
"react-native-appearance": "^0.3.4",
|
||||
"react-native-fast-image": "^8.3.2",
|
||||
"react-native-gesture-handler": "^1.7.0",
|
||||
"react-native-reanimated": "^1.10.1",
|
||||
"react-native-safe-area-context": "^3.1.1",
|
||||
"react-native-screens": "^2.9.0",
|
||||
"react-native-dotenv": "^2.5.1",
|
||||
"react-native-fast-image": "^8.3.4",
|
||||
"react-native-gesture-handler": "^1.9.0",
|
||||
"react-native-localize": "^2.0.1",
|
||||
"react-native-reanimated": "^1.13.2",
|
||||
"react-native-safe-area-context": "^3.1.9",
|
||||
"react-native-screens": "^2.17.1",
|
||||
"react-native-svg": "^12.1.0",
|
||||
"react-native-svg-transformer": "^0.14.3",
|
||||
"react-native-track-player": "github:leinelissen/react-native-track-player",
|
||||
"react-native-webview": "^10.3.2",
|
||||
"react-redux": "^7.2.1",
|
||||
"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.5",
|
||||
"@babel/runtime": "^7.10.5",
|
||||
"@babel/core": "^7.12.13",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@react-native-community/eslint-config": "^2.0.0",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/react-native": "^0.63.2",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/react-test-renderer": "16.9.2",
|
||||
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
||||
"@typescript-eslint/parser": "^3.7.0",
|
||||
"babel-jest": "^26.1.0",
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.8",
|
||||
"jest": "^26.1.0",
|
||||
"metro-react-native-babel-preset": "^0.61.0",
|
||||
"react-test-renderer": "^16.13.1",
|
||||
"typescript": "^3.9.7"
|
||||
"@types/i18n-js": "^3.8.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/react-native": "^0.63.48",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
"@types/react-test-renderer": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.14.2",
|
||||
"@typescript-eslint/parser": "^4.14.2",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^26.6.3",
|
||||
"metro-react-native-babel-preset": "^0.65.0",
|
||||
"react-test-renderer": "^17.0.1",
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native",
|
||||
|
||||
1
src/assets/repeat.svg
Normal file
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
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 |
@@ -1,23 +1,38 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components/native';
|
||||
import { SafeAreaView } from 'react-native';
|
||||
import React, { useCallback } from 'react';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import { SafeAreaView, Pressable } from 'react-native';
|
||||
import { colors } from './Colors';
|
||||
import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
|
||||
const Background = styled.View`
|
||||
interface Props {
|
||||
fullSize?: boolean;
|
||||
}
|
||||
|
||||
const Background = styled(Pressable)`
|
||||
padding: 100px 25px;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const Container = styled.View`
|
||||
const Container = styled(Pressable)<Pick<Props, 'fullSize'>>`
|
||||
border-radius: 20px;
|
||||
flex: 1;
|
||||
margin: auto 0;
|
||||
|
||||
${props => props.fullSize && css`
|
||||
flex: 1;
|
||||
`}
|
||||
`;
|
||||
|
||||
const Modal: React.FC = ({ children }) => {
|
||||
const Modal: React.FC<Props> = ({ children, fullSize = true }) => {
|
||||
const navigation = useNavigation();
|
||||
const closeModal = useCallback(() => {
|
||||
navigation.dispatch(StackActions.popToTop());
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<Background style={colors.modal}>
|
||||
<Background style={colors.modal} onPress={closeModal}>
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Container style={colors.view}>
|
||||
<Container style={colors.view} fullSize={fullSize}>
|
||||
{children}
|
||||
</Container>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { Pressable, ViewStyle } from 'react-native';
|
||||
|
||||
interface TouchableHandlerProps {
|
||||
id: string;
|
||||
onPress: (id: string) => void;
|
||||
onLongPress?: (id: string) => void;
|
||||
}
|
||||
|
||||
function TouchableStyles({ pressed }: { pressed: boolean }): ViewStyle {
|
||||
if (pressed) {
|
||||
return { opacity: 0.5 };
|
||||
} else {
|
||||
return { opacity: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a generic handler that accepts id as a prop, and return it when it is
|
||||
* pressed. This comes in handy with lists in which albums / tracks need to be selected.
|
||||
*/
|
||||
const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, children }) => {
|
||||
const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, onLongPress, children }) => {
|
||||
const handlePress = useCallback(() => {
|
||||
return onPress(id);
|
||||
}, [id, onPress]);
|
||||
|
||||
const handleLongPress = useCallback(() => {
|
||||
return onLongPress ? onLongPress(id) : undefined;
|
||||
}, [id, onLongPress]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<Pressable
|
||||
onPress={handlePress}
|
||||
onLongPress={handleLongPress}
|
||||
style={TouchableStyles}
|
||||
>
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,4 +4,9 @@ export const Header = styled.Text`
|
||||
margin: 24px 0 12px 0;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
export const SubHeader = styled.Text`
|
||||
font-size: 24px;
|
||||
margin: 12px 0;
|
||||
`;
|
||||
45
src/localisation/index.ts
Normal file
45
src/localisation/index.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import i18n from 'i18n-js';
|
||||
import { findBestAvailableLanguage } from 'react-native-localize';
|
||||
import { LocaleKeys } from './types';
|
||||
|
||||
// Lazy loaders for locale
|
||||
const localeGetters: Record<string, () => object> = {
|
||||
en: () => require('./lang/en/locale.json'),
|
||||
fr: () => require('./lang/fr/locale.json'),
|
||||
nl: () => require('./lang/nl/locale.json'),
|
||||
es: () => require('./lang/es/locale.json'),
|
||||
};
|
||||
|
||||
// Have RNLocalize pick the best locale from the languages on offer
|
||||
const locale = findBestAvailableLanguage(Object.keys(localeGetters));
|
||||
|
||||
// Check if the locale is correctly picked
|
||||
if (!locale || !locale.languageTag) {
|
||||
throw new Error('Invalid locale selected');
|
||||
}
|
||||
|
||||
// Set the key-value pairs for the different languages you want to support.
|
||||
i18n.translations = {
|
||||
en: localeGetters.en(),
|
||||
};
|
||||
|
||||
// If the locale is not english, we add it to the translations key s well
|
||||
if (locale.languageTag !== 'en') {
|
||||
i18n.translations[locale.languageTag] = localeGetters[locale.languageTag as string]();
|
||||
}
|
||||
|
||||
// Set the locale once at the beginning of your app.
|
||||
i18n.locale = locale.languageTag;
|
||||
|
||||
// Fallback to the default language for missing translation strings
|
||||
i18n.fallbacks = true;
|
||||
|
||||
/**
|
||||
* An i18n Typescript helper with autocomplete for the key argument
|
||||
* @param key string
|
||||
*/
|
||||
export function t(key: LocaleKeys) {
|
||||
return i18n.t(key);
|
||||
}
|
||||
|
||||
export default i18n;
|
||||
28
src/localisation/lang/en/locale.json
Normal file
28
src/localisation/lang/en/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Play Next",
|
||||
"play-album": "Play Album",
|
||||
"queue": "Queue",
|
||||
"add-to-queue": "Add to Queue",
|
||||
"clear-queue": "Clear Queue",
|
||||
"no-results": "No results...",
|
||||
"album": "Album",
|
||||
"albums": "Albums",
|
||||
"all-albums": "All Albums",
|
||||
"search": "Search",
|
||||
"music": "Music",
|
||||
"now-playing": "Now Playing",
|
||||
"onboarding-welcome": "Welcome!",
|
||||
"onboarding-intro": "Jellyfin Audio Player will allow you to stream your music library from anywhere, with full support for background audio and casting.",
|
||||
"onboarding-cta": "In order to get started, you need a Jellyfin server. Click the button below to enter your Jellyfin server address and login to it.",
|
||||
"set-jellyfin-server": "Set Jellyfin Server",
|
||||
"set-jellyfin-server-instruction": "Please enter your Jellyfin server URL. Make sure to include the protocol and port",
|
||||
"settings": "Settings",
|
||||
"jellyfin-library": "Jellyfin Library",
|
||||
"jellyfin-server-url": "Jellyfin Server URL",
|
||||
"jellyfin-access-token": "Jellyfin Access Token",
|
||||
"jellyfin-user-id": "Jellyfin User ID",
|
||||
"setting-cache": "Cache",
|
||||
"setting-cache-description": "If you have updated your Jellyfin library, but the app is holding on to cached assets, you can forcefully clear the cache using this button. This will force the app to fetch the library from scratch.",
|
||||
"reset-cache": "Reset Cache",
|
||||
"recent-albums": "Recent Albums"
|
||||
}
|
||||
28
src/localisation/lang/es/locale.json
Normal file
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
28
src/localisation/lang/fr/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Lire ensuite",
|
||||
"play-album": "Jouer l'album",
|
||||
"queue": "File d'attente",
|
||||
"add-to-queue": "Ajouter à la file d'attente",
|
||||
"clear-queue": "Vider la file d'attente",
|
||||
"no-results": "Pas de résultats...",
|
||||
"album": "Album",
|
||||
"albums": "Albums",
|
||||
"all-albums": "Tous les Albums",
|
||||
"search": "Rechercher",
|
||||
"music": "Musique",
|
||||
"now-playing": "En cours",
|
||||
"onboarding-welcome": "Bienvenue !",
|
||||
"onboarding-intro": "Jellyfin Audio Player vous permettra de diffuser votre bibliothèque musicale de n'importe où, avec un support de la lecture en arrière plan et la diffusion à distance.",
|
||||
"onboarding-cta": "Pour utiliser Jellyfin Audio Player, vous avez besoin d'un serveur Jellyfin. Cliquez sur le bouton ci-dessous pour entrer l'adresse de votre serveur Jellyfin et vous y connecter.",
|
||||
"set-jellyfin-server": "Configurer le serveur Jellyfin",
|
||||
"set-jellyfin-server-instruction": "Veuillez entrer l'URL de votre serveur Jellyfin. Assurez-vous d'inclure le protocole et le port",
|
||||
"settings": "Réglages",
|
||||
"jellyfin-library": "Bibliothèque Jellyfin",
|
||||
"jellyfin-server-url": "URL du serveur Jellyfin",
|
||||
"jellyfin-access-token": "Jeton d'accès à la Jellyfin",
|
||||
"jellyfin-user-id": "ID utilisateur Jellyfin",
|
||||
"setting-cache": "Cache",
|
||||
"setting-cache-description": "Si vous avez mis à jour votre bibliothèque Jellyfin mais que l'application conserve toujours des ressources en cache, vous pouvez vider le cache en utilisant ce bouton. Cela forcera l'application à récupérer l’intégralité de bibliothèque.",
|
||||
"reset-cache": "Réinitialiser le cache",
|
||||
"recent-albums": "Albums récents"
|
||||
}
|
||||
28
src/localisation/lang/nl/locale.json
Normal file
28
src/localisation/lang/nl/locale.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"play-next": "Speel volgende",
|
||||
"play-album": "Speel album",
|
||||
"queue": "Wachtrij",
|
||||
"add-to-queue": "Voeg toe aan wachtrij",
|
||||
"clear-queue": "Wis wachtrij",
|
||||
"no-results": "Geen resultaten...",
|
||||
"album": "Album",
|
||||
"albums": "Albums",
|
||||
"all-albums": "Alle Albums",
|
||||
"search": "Zoeken",
|
||||
"music": "Muziek",
|
||||
"now-playing": "Nu spelend",
|
||||
"onboarding-welcome": "Welkom!",
|
||||
"onboarding-intro": "Jellyfin Audio Player maakt het mogelijk om van waar dan ook je muziek te streamen, met volledige support voor achtegroundaudio en casting.",
|
||||
"onboarding-cta": "Om te starten moet je een Jellyfin server hebben. Klik de onderstaande knop om het adres van je Jellyfin server in te vullen en er in te loggen.",
|
||||
"set-jellyfin-server": "Kies Jellyfin Server in",
|
||||
"set-jellyfin-server-instruction": "Vul alsjeblieft je Jellyfin server URL in. Voeg ook het protocol en de poort toe",
|
||||
"settings": "Instellingen",
|
||||
"jellyfin-library": "Jellyfin Bibliotheek",
|
||||
"jellyfin-server-url": "Jellyfin Server URL",
|
||||
"jellyfin-access-token": "Jellyfin Toegangstoken",
|
||||
"jellyfin-user-id": "Jellyfin Gebruiker ID",
|
||||
"setting-cache": "Cache",
|
||||
"setting-cache-description": "Als je Jellyfin bibliotheek geüpdatet hebt, maar de app nog aan elementen uit de cache vasthoudt, kun je de cache geforceerd leegmaken met deze knop. Dit forceert de app om de bibliotheek weer vanaf het begin op te bouwen.",
|
||||
"reset-cache": "Leeg Cache",
|
||||
"recent-albums": "Recente Albums"
|
||||
}
|
||||
26
src/localisation/types.ts
Normal file
26
src/localisation/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export type LocaleKeys = 'play-next'
|
||||
| 'play-album'
|
||||
| 'queue'
|
||||
| 'add-to-queue'
|
||||
| 'clear-queue'
|
||||
| 'no-results'
|
||||
| 'album'
|
||||
| 'albums'
|
||||
| 'all-albums'
|
||||
| 'search'
|
||||
| 'music'
|
||||
| 'now-playing'
|
||||
| 'onboarding-welcome'
|
||||
| 'onboarding-intro'
|
||||
| 'onboarding-cta'
|
||||
| 'set-jellyfin-server'
|
||||
| 'set-jellyfin-server-instruction'
|
||||
| 'settings'
|
||||
| 'jellyfin-library'
|
||||
| 'jellyfin-server-url'
|
||||
| 'jellyfin-access-token'
|
||||
| 'jellyfin-user-id'
|
||||
| 'setting-cache'
|
||||
| 'setting-cache-description'
|
||||
| 'reset-cache'
|
||||
| 'recent-albums'
|
||||
@@ -6,6 +6,7 @@ import Album from './stacks/Album';
|
||||
import RecentAlbums from './stacks/RecentAlbums';
|
||||
import Search from './stacks/Search';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Stack = createStackNavigator<StackParams>();
|
||||
|
||||
@@ -17,10 +18,10 @@ const navigationOptions = {
|
||||
function MusicStack() {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="RecentAlbums" screenOptions={navigationOptions}>
|
||||
<Stack.Screen name="RecentAlbums" component={RecentAlbums} options={{ headerTitle: 'Recent Albums' }} />
|
||||
<Stack.Screen name="Albums" component={Albums} />
|
||||
<Stack.Screen name="Album" component={Album} />
|
||||
<Stack.Screen name="Search" component={Search} />
|
||||
<Stack.Screen name="RecentAlbums" component={RecentAlbums} options={{ headerTitle: t('recent-albums') }} />
|
||||
<Stack.Screen name="Albums" component={Albums} options={{ headerTitle: t('albums') }} />
|
||||
<Stack.Screen name="Album" component={Album} options={{ headerTitle: t('album') }} />
|
||||
<Stack.Screen name="Search" component={Search} options={{ headerTitle: t('search') }} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { StackParams } from '../types';
|
||||
import { Text, ScrollView, Dimensions, Button, RefreshControl, StyleSheet } from 'react-native';
|
||||
import { useGetImage } from 'utility/JellyfinApi';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import { useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { useRoute, RouteProp, useNavigation } from '@react-navigation/native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
@@ -11,10 +11,11 @@ import { useTypedSelector } from 'store';
|
||||
import { fetchTracksByAlbum } from 'store/music/actions';
|
||||
import { ALBUM_CACHE_AMOUNT_OF_DAYS, THEME_COLOR } from 'CONSTANTS';
|
||||
import usePlayAlbum from 'utility/usePlayAlbum';
|
||||
import usePlayTrack from 'utility/usePlayTrack';
|
||||
import TouchableHandler from 'components/TouchableHandler';
|
||||
import useCurrentTrack from 'utility/useCurrentTrack';
|
||||
import { colors } from 'components/Colors';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { t } from '@localisation';
|
||||
|
||||
type Route = RouteProp<StackParams, 'Album'>;
|
||||
|
||||
@@ -71,11 +72,26 @@ const Album: React.FC = () => {
|
||||
const getImage = useGetImage();
|
||||
const playAlbum = usePlayAlbum();
|
||||
const currentTrack = useCurrentTrack();
|
||||
const navigation = useNavigation();
|
||||
|
||||
// Setup callbacks
|
||||
const selectAlbum = useCallback(() => { playAlbum(id); }, [playAlbum, id]);
|
||||
const selectTrack = usePlayTrack();
|
||||
const refresh = useCallback(() => { dispatch(fetchTracksByAlbum(id)); }, [id, dispatch]);
|
||||
const selectTrack = useCallback(async (trackId) => {
|
||||
const tracks = await playAlbum(id, false);
|
||||
|
||||
if (tracks) {
|
||||
const track = tracks.find((t) => t.id.startsWith(trackId));
|
||||
|
||||
if (track) {
|
||||
await TrackPlayer.skip(track.id);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
}
|
||||
}, [playAlbum, id]);
|
||||
const longPressTrack = useCallback((trackId: string) => {
|
||||
navigation.navigate('TrackPopupMenu', { trackId });
|
||||
}, [navigation]);
|
||||
|
||||
// Retrieve album tracks on load
|
||||
useEffect(() => {
|
||||
@@ -99,9 +115,14 @@ const Album: React.FC = () => {
|
||||
<AlbumImage source={{ uri: getImage(album?.Id) }} style={colors.imageBackground} />
|
||||
<Text style={styles.name} >{album?.Name}</Text>
|
||||
<Text style={styles.artist}>{album?.AlbumArtist}</Text>
|
||||
<Button title="Play Album" onPress={selectAlbum} color={THEME_COLOR} />
|
||||
<Button title={t('play-album')} onPress={selectAlbum} color={THEME_COLOR} />
|
||||
{album?.Tracks?.length ? album.Tracks.map((trackId) =>
|
||||
<TouchableHandler key={trackId} id={trackId} onPress={selectTrack}>
|
||||
<TouchableHandler
|
||||
key={trackId}
|
||||
id={trackId}
|
||||
onPress={selectTrack}
|
||||
onLongPress={longPressTrack}
|
||||
>
|
||||
<TrackContainer isPlaying={currentTrack?.id.startsWith(trackId) || false} style={colors.border}>
|
||||
<Text style={styles.index}>
|
||||
{tracks[trackId]?.IndexNumber}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useRecentAlbums } from 'store/music/selectors';
|
||||
import { Header } from 'components/Typography';
|
||||
import ListButton from 'components/ListButton';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
artist: {
|
||||
@@ -28,9 +29,9 @@ const NavigationHeader: React.FC = () => {
|
||||
|
||||
return (
|
||||
<ListContainer>
|
||||
<ListButton onPress={handleAllAlbumsClick}>All Albums</ListButton>
|
||||
<ListButton onPress={handleSearchClick}>Search</ListButton>
|
||||
<Header style={colors.text}>Recent Albums</Header>
|
||||
<ListButton onPress={handleAllAlbumsClick}>{t('all-albums')}</ListButton>
|
||||
<ListButton onPress={handleSearchClick}>{t('search')}</ListButton>
|
||||
<Header style={colors.text}>{t('recent-albums')}</Header>
|
||||
</ListContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useGetImage } from 'utility/JellyfinApi';
|
||||
import { NavigationProp } from '../types';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Container = styled.View`
|
||||
padding: 0 20px;
|
||||
@@ -88,8 +89,8 @@ export default function Search() {
|
||||
|
||||
const HeaderComponent = React.useMemo(() => (
|
||||
<Container>
|
||||
<Input value={searchTerm} onChangeText={setSearchTerm} style={colors.input} placeholder="Search..." />
|
||||
{(searchTerm.length && !results.length) ? <Text>No results...</Text> : null}
|
||||
<Input value={searchTerm} onChangeText={setSearchTerm} style={colors.input} placeholder={t('search') + '...'} />
|
||||
{(searchTerm.length && !results.length) ? <Text>{t('no-results')}</Text> : null}
|
||||
</Container>
|
||||
), [searchTerm, results, setSearchTerm]);
|
||||
|
||||
@@ -110,7 +111,7 @@ export default function Search() {
|
||||
<Text numberOfLines={1} ellipsizeMode="tail" style={colors.text}>
|
||||
{album.Name} - {album.AlbumArtist}
|
||||
</Text>
|
||||
<HalfOpacity style={colors.text}>Album</HalfOpacity>
|
||||
<HalfOpacity style={colors.text}>{t('album')}</HalfOpacity>
|
||||
</View>
|
||||
</SearchResult>
|
||||
</TouchableHandler>
|
||||
|
||||
@@ -7,6 +7,7 @@ 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};
|
||||
@@ -57,16 +58,16 @@ function Onboarding() {
|
||||
<TextContainer contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
|
||||
<Logo source={require('../../assets/app-icon-white.png')} />
|
||||
<Text >
|
||||
Welcome!
|
||||
{t('onboarding-welcome')}
|
||||
</Text>
|
||||
<Text>
|
||||
Jellyfin Audio Player will allow you to stream your music library from anywhere, with full support for background audio and casting.
|
||||
{t('onboarding-intro')}
|
||||
</Text>
|
||||
<Text>
|
||||
In order to get started, you need a Jellyfin server. Click the button below to enter your Jellyfin server address and login to it.
|
||||
{t('onboarding-cta')}
|
||||
</Text>
|
||||
<ButtonContainer>
|
||||
<Button title="Set Jellyfin Server" color="#ffffff" onPress={handleClick} />
|
||||
<Button title={t('set-jellyfin-server')} color="#ffffff" onPress={handleClick} />
|
||||
</ButtonContainer>
|
||||
</TextContainer>
|
||||
</Container>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, STATE_PLAYING, STATE_PAUSED } from 'react-native-track-player';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import styled from 'styled-components/native';
|
||||
@@ -7,9 +7,13 @@ import ForwardIcon from 'assets/forwards.svg';
|
||||
import BackwardIcon from 'assets/backwards.svg';
|
||||
import PlayIcon from 'assets/play.svg';
|
||||
import PauseIcon from 'assets/pause.svg';
|
||||
import RepeatIcon from 'assets/repeat.svg';
|
||||
// import ShuffleIcon from 'assets/shuffle.svg';
|
||||
import { useColorScheme } from 'react-native-appearance';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
|
||||
const BUTTON_SIZE = 40;
|
||||
const BUTTON_SIZE_SMALL = 25;
|
||||
|
||||
const pause = () => TrackPlayer.pause();
|
||||
const play = () => TrackPlayer.play();
|
||||
@@ -47,6 +51,14 @@ export default function MediaControls() {
|
||||
<NextButton fill={fill} />
|
||||
</Button>
|
||||
</Buttons>
|
||||
<Buttons>
|
||||
<Button>
|
||||
<RepeatButton fill={fill} />
|
||||
</Button>
|
||||
<Button>
|
||||
{/* <ShuffleButton fill={fill} /> */}
|
||||
</Button>
|
||||
</Buttons>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -71,6 +83,67 @@ export function NextButton({ fill }: { fill: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ const Artwork = styled(FastImage)`
|
||||
width: ${Screen.width * 0.8}px;
|
||||
height: ${Screen.width * 0.8}px;
|
||||
margin: 25px auto;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -39,7 +37,13 @@ export default function NowPlaying() {
|
||||
|
||||
return (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<Artwork style={colors.imageBackground} source={{ uri: track?.artwork }} />
|
||||
<Artwork
|
||||
style={colors.imageBackground}
|
||||
source={{
|
||||
uri: track?.artwork,
|
||||
priority: FastImage.priority.high,
|
||||
}}
|
||||
/>
|
||||
<Text style={styles.artist} >{track?.artist}</Text>
|
||||
<Text style={styles.title}>{track?.title}</Text>
|
||||
</View>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import useQueue from 'utility/useQueue';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { View, Text, StyleSheet, Button } from 'react-native';
|
||||
import styled, { css } from 'styled-components/native';
|
||||
import useCurrentTrack from 'utility/useCurrentTrack';
|
||||
import TouchableHandler from 'components/TouchableHandler';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const QueueItem = styled.View<{ active?: boolean, alreadyPlayed?: boolean, isDark?: boolean }>`
|
||||
padding: 10px;
|
||||
@@ -23,6 +24,10 @@ const QueueItem = styled.View<{ active?: boolean, alreadyPlayed?: boolean, isDar
|
||||
`}
|
||||
`;
|
||||
|
||||
const ClearQueue = styled.View`
|
||||
margin: 20px 0;
|
||||
`;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
title: {
|
||||
...colors.text,
|
||||
@@ -42,10 +47,13 @@ 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
|
||||
@@ -62,6 +70,9 @@ export default function Queue() {
|
||||
</QueueItem>
|
||||
</TouchableHandler>
|
||||
))}
|
||||
<ClearQueue>
|
||||
<Button title={t('clear-queue')} color={THEME_COLOR} onPress={clearQueue} />
|
||||
</ClearQueue>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
27
src/screens/Settings/components/Cache.tsx
Normal file
27
src/screens/Settings/components/Cache.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import { Text, Button } from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import music from 'store/music';
|
||||
import { t } from '@localisation';
|
||||
|
||||
export default function CacheSettings() {
|
||||
const dispatch = useDispatch();
|
||||
const handleClearCache = useCallback(() => {
|
||||
// Dispatch an action to reset the music subreducer state
|
||||
dispatch(music.actions.reset());
|
||||
|
||||
// Also clear the TrackPlayer queue
|
||||
TrackPlayer.reset();
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubHeader>{t('setting-cache')}</SubHeader>
|
||||
<Text>{t('setting-cache-description')}</Text>
|
||||
<Button title={t('reset-cache')} onPress={handleClearCache} color={THEME_COLOR} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
45
src/screens/Settings/components/Library.tsx
Normal file
45
src/screens/Settings/components/Library.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import styled from 'styled-components/native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Text, Button } from 'react-native';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import { colors } from 'components/Colors';
|
||||
import { NavigationProp } from '../..';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
export default function LibrarySettings() {
|
||||
const { jellyfin } = useTypedSelector(state => state.settings);
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleSetLibrary = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubHeader>{t('jellyfin-library')}</SubHeader>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>{t('jellyfin-server-url')}</Text>
|
||||
<Input placeholder="https://jellyfin.yourserver.com/" value={jellyfin?.uri} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>{t('jellyfin-access-token')}</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.access_token} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>{t('jellyfin-user-id')}</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.user_id} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<Button title={t('set-jellyfin-server')} onPress={handleSetLibrary} color={THEME_COLOR} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,55 +1,19 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { View, Text, SafeAreaView, Button, StyleSheet } from 'react-native';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
import styled from 'styled-components/native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '..';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import React from 'react';
|
||||
import { View, SafeAreaView, ScrollView } from 'react-native';
|
||||
import { Header } from 'components/Typography';
|
||||
import { colors } from 'components/Colors';
|
||||
|
||||
const InputContainer = styled.View`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const Input = styled.TextInput`
|
||||
padding: 15px;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
import Library from './components/Library';
|
||||
import Cache from './components/Cache';
|
||||
import { t } from '@localisation';
|
||||
|
||||
export default function Settings() {
|
||||
const { jellyfin, bitrate } = useSelector((state: AppState) => state.settings);
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const handleClick = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<SafeAreaView>
|
||||
<View style={{ padding: 20 }}>
|
||||
<Header style={colors.text}>Settings</Header>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>Jellyfin Server URL</Text>
|
||||
<Input placeholder="https://jellyfin.yourserver.com/" value={jellyfin?.uri} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>Jellyfin Access Token</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.access_token} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<InputContainer>
|
||||
<Text style={colors.text}>Jellyfin User ID</Text>
|
||||
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.user_id} editable={false} style={colors.input} />
|
||||
</InputContainer>
|
||||
<Button title="Set Jellyfin server" onPress={handleClick} color={THEME_COLOR} />
|
||||
{/* The bitrate setting is hidden for now, since Jellyfin does not appear to support custom bitrates */}
|
||||
{/* <InputContainer>
|
||||
<Text style={colors.text}>Bitrate</Text>
|
||||
<Picker selectedValue={bitrate}>
|
||||
<Picker.Item label="320kbps" value={140000000} />
|
||||
</Picker>
|
||||
</InputContainer> */}
|
||||
<Header style={colors.text}>{t('settings')}</Header>
|
||||
<Library />
|
||||
<Cache />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
|
||||
@@ -12,8 +12,11 @@ import GearIcon from 'assets/gear.svg';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { useTypedSelector } from 'store';
|
||||
import Onboarding from './Onboarding';
|
||||
import TrackPopupMenu from './modals/TrackPopupMenu';
|
||||
import { ModalStackParams } from './types';
|
||||
import { t } from '@localisation';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
const Stack = createStackNavigator<ModalStackParams>();
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
type Screens = {
|
||||
@@ -62,9 +65,9 @@ function Screens() {
|
||||
inactiveTintColor: 'gray',
|
||||
}}
|
||||
>
|
||||
<Tab.Screen name="NowPlaying" component={Player} options={{ tabBarLabel: 'Now Playing' }} />
|
||||
<Tab.Screen name="Music" component={Music} />
|
||||
<Tab.Screen name="Settings" component={Settings} />
|
||||
<Tab.Screen name="NowPlaying" component={Player} options={{ tabBarLabel: t('now-playing') }} />
|
||||
<Tab.Screen name="Music" component={Music} options={{ tabBarLabel: t('music') }} />
|
||||
<Tab.Screen name="Settings" component={Settings} options={{ tabBarLabel: t('settings') }} />
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
@@ -84,6 +87,7 @@ export default function Routes() {
|
||||
}}>
|
||||
<Stack.Screen name="Screens" component={Screens} />
|
||||
<Stack.Screen name="SetJellyfinServer" component={SetJellyfinServer} />
|
||||
<Stack.Screen name="TrackPopupMenu" component={TrackPopupMenu} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useNavigation, StackActions } from '@react-navigation/native';
|
||||
import CredentialGenerator from './components/CredentialGenerator';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { colors } from 'components/Colors';
|
||||
import { t } from '@localisation';
|
||||
|
||||
export default function SetJellyfinServer() {
|
||||
// State for first screen
|
||||
@@ -34,7 +35,7 @@ export default function SetJellyfinServer() {
|
||||
) : (
|
||||
<View style={{ padding: 20, flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||||
<Text style={colors.text}>
|
||||
Please enter your Jellyfin server URL. Make sure to include the protocol and port
|
||||
{t('set-jellyfin-server-instruction')}
|
||||
</Text>
|
||||
<Input
|
||||
placeholder="https://jellyfin.yourserver.io/"
|
||||
@@ -46,7 +47,7 @@ export default function SetJellyfinServer() {
|
||||
style={{ ...colors.input, width: '100%' }}
|
||||
/>
|
||||
<Button
|
||||
title="Set server"
|
||||
title={t('set-jellyfin-server')}
|
||||
onPress={() => setIsLogginIn(true)}
|
||||
disabled={!serverUrl?.length}
|
||||
color={THEME_COLOR}
|
||||
|
||||
60
src/screens/modals/TrackPopupMenu.tsx
Normal file
60
src/screens/modals/TrackPopupMenu.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import { Text, Button } from 'react-native';
|
||||
import { useNavigation, StackActions, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { ModalStackParams } from 'screens/types';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { THEME_COLOR } from 'CONSTANTS';
|
||||
import { SubHeader } from 'components/Typography';
|
||||
import styled from 'styled-components/native';
|
||||
import usePlayTrack from 'utility/usePlayTrack';
|
||||
import { t } from '@localisation';
|
||||
|
||||
type Route = RouteProp<ModalStackParams, 'TrackPopupMenu'>;
|
||||
|
||||
const Container = styled.View`
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
const Buttons = styled.View`
|
||||
margin-top: 20px;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
`;
|
||||
|
||||
function TrackPopupMenu() {
|
||||
// Retrieve helpers
|
||||
const { params: { trackId } } = useRoute<Route>();
|
||||
const navigation = useNavigation();
|
||||
const track = useTypedSelector((state) => state.music.tracks.entities[trackId]);
|
||||
const playTrack = usePlayTrack();
|
||||
|
||||
// Set callback to close the modal
|
||||
const closeModal = useCallback(() => {
|
||||
navigation.dispatch(StackActions.popToTop());
|
||||
}, [navigation]);
|
||||
|
||||
const handlePlayNext = useCallback(() => {
|
||||
playTrack(trackId, false, false);
|
||||
closeModal();
|
||||
}, [playTrack, closeModal, trackId]);
|
||||
const handleAddToQueue = useCallback(() => {
|
||||
playTrack(trackId, false, true);
|
||||
closeModal();
|
||||
}, [playTrack, closeModal, trackId]);
|
||||
|
||||
return (
|
||||
<Modal fullSize={false}>
|
||||
<Container>
|
||||
<SubHeader>{track?.Name}</SubHeader>
|
||||
<Text>{track?.Album} - {track?.AlbumArtist}</Text>
|
||||
<Buttons>
|
||||
<Button title={t('play-next')} color={THEME_COLOR} onPress={handlePlayNext} />
|
||||
<Button title={t('add-to-queue')} color={THEME_COLOR} onPress={handleAddToQueue} />
|
||||
</Buttons>
|
||||
</Container>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default TrackPopupMenu;
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ModalStackParams {
|
||||
SetJellyfinServer: undefined;
|
||||
TrackPopupMenu: { trackId: string };
|
||||
}
|
||||
@@ -13,9 +13,11 @@ const persistConfig: PersistConfig<AppState> = {
|
||||
|
||||
import settings from './settings';
|
||||
import music from './music';
|
||||
import player from './player';
|
||||
|
||||
const reducers = combineReducers({
|
||||
settings,
|
||||
player: player.reducer,
|
||||
music: music.reducer,
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ const initialState: State = {
|
||||
const music = createSlice({
|
||||
name: 'music',
|
||||
initialState,
|
||||
reducers: {},
|
||||
reducers: {
|
||||
reset: () => initialState,
|
||||
},
|
||||
extraReducers: builder => {
|
||||
/**
|
||||
* Fetch All albums
|
||||
|
||||
11
src/store/player/index.ts
Normal file
11
src/store/player/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
const player = createSlice({
|
||||
name: 'player',
|
||||
initialState: 0,
|
||||
reducers: {
|
||||
addNewTrackToPlayer: (state) => state + 1,
|
||||
}
|
||||
});
|
||||
|
||||
export default player;
|
||||
@@ -26,7 +26,7 @@ const baseTrackOptions: Record<string, string> = {
|
||||
// react-native-track-player. This is set to be merged and released very
|
||||
// soon: https://github.com/react-native-kit/react-native-track-player/pull/950
|
||||
// Container: 'mp3',
|
||||
Container: 'mp3,aac,m4a,m4b|aac,alac,m4a,m4b|alac',
|
||||
Container: 'mp3,aac,m4a,m4b|aac,alac,m4a,m4b|alac,flac',
|
||||
AudioCodec: 'aac',
|
||||
static: 'true',
|
||||
};
|
||||
@@ -45,7 +45,7 @@ export function generateTrack(track: AlbumTrack, credentials: Credentials): Trac
|
||||
DeviceId: credentials?.device_id || '',
|
||||
};
|
||||
const trackParams = new URLSearchParams(trackOptions).toString();
|
||||
const url = encodeURI(`${credentials?.uri}/Audio/${track.Id}/universal.mp3?${trackParams}`);
|
||||
const url = encodeURI(`${credentials?.uri}/Audio/${track.Id}/universal?${trackParams}`);
|
||||
|
||||
return {
|
||||
id: track.Id,
|
||||
|
||||
@@ -14,15 +14,22 @@ export default function useCurrentTrack(): Track | undefined {
|
||||
|
||||
// GUARD: Only fetch current track if there is a current track
|
||||
if (!currentTrackId) {
|
||||
setTrack(undefined);
|
||||
}
|
||||
|
||||
// GUARD: Only retrieve new track if it is different from the one we
|
||||
// have currently in state.
|
||||
if (currentTrackId === track?.id){
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is different, retrieve the track and save it
|
||||
const currentTrack = await TrackPlayer.getTrack(currentTrackId);
|
||||
setTrack(currentTrack);
|
||||
};
|
||||
|
||||
fetchTrack();
|
||||
}, [state]);
|
||||
}, [state, track, setTrack]);
|
||||
|
||||
return track;
|
||||
}
|
||||
@@ -2,17 +2,22 @@ import { useTypedSelector } from 'store';
|
||||
import { useCallback } from 'react';
|
||||
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||
import { generateTrack } from './JellyfinApi';
|
||||
import useQueue from './useQueue';
|
||||
import player from 'store/player';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
/**
|
||||
* Generate a callback function that starts playing a full album given its
|
||||
* supplied id.
|
||||
*/
|
||||
export default function usePlayAlbum() {
|
||||
const dispatch = useDispatch();
|
||||
const credentials = useTypedSelector(state => state.settings.jellyfin);
|
||||
const albums = useTypedSelector(state => state.music.albums.entities);
|
||||
const tracks = useTypedSelector(state => state.music.tracks.entities);
|
||||
const queue = useQueue();
|
||||
|
||||
return useCallback(async function playAlbum(albumId: string) {
|
||||
return useCallback(async function playAlbum(albumId: string, play = true): Promise<TrackPlayer.Track[] | undefined> {
|
||||
const album = albums[albumId];
|
||||
const trackIds = album?.Tracks;
|
||||
|
||||
@@ -21,6 +26,29 @@ export default function usePlayAlbum() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the queue already contains the consecutive track listing
|
||||
// that is described as part of the album
|
||||
const queuedAlbum = queue.reduce<TrackPlayer.Track[]>((sum, track) => {
|
||||
if (track.id.startsWith(trackIds[sum.length])) {
|
||||
sum.push(track);
|
||||
} else {
|
||||
sum = [];
|
||||
}
|
||||
|
||||
return sum;
|
||||
}, []);
|
||||
|
||||
// If the entire album is already in the queue, we can just return those
|
||||
// tracks, rather than adding it to the queue again.
|
||||
if (queuedAlbum.length === trackIds.length) {
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return queuedAlbum;
|
||||
}
|
||||
|
||||
// Convert all trackIds to the relevant format for react-native-track-player
|
||||
const newTracks = trackIds.map((trackId) => {
|
||||
const track = tracks[trackId];
|
||||
@@ -34,7 +62,15 @@ export default function usePlayAlbum() {
|
||||
// Clear the queue and add all tracks
|
||||
await TrackPlayer.removeUpcomingTracks();
|
||||
await TrackPlayer.add(newTracks);
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
TrackPlayer.play();
|
||||
}, [credentials, albums, tracks]);
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTracks;
|
||||
}, [credentials, albums, tracks, queue, dispatch]);
|
||||
}
|
||||
@@ -3,17 +3,20 @@ import TrackPlayer from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { generateTrack } from './JellyfinApi';
|
||||
import useQueue from './useQueue';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import player from 'store/player';
|
||||
|
||||
/**
|
||||
* A hook that generates a callback that can setup and start playing a
|
||||
* particular trackId in the player.
|
||||
*/
|
||||
export default function usePlayTrack() {
|
||||
const dispatch = useDispatch();
|
||||
const credentials = useTypedSelector(state => state.settings.jellyfin);
|
||||
const tracks = useTypedSelector(state => state.music.tracks.entities);
|
||||
const queue = useQueue();
|
||||
|
||||
return useCallback(async function playTrack(trackId: string) {
|
||||
return useCallback(async function playTrack(trackId: string, play = true, addToEnd = true) {
|
||||
// Get the relevant track
|
||||
const track = tracks[trackId];
|
||||
|
||||
@@ -30,10 +33,35 @@ export default function usePlayTrack() {
|
||||
...(trackInstances.length ? trackInstances[0] : generateTrack(track, credentials)),
|
||||
id: `${trackId}_${trackInstances.length}`
|
||||
};
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
|
||||
// Then, we'll need to check where to add the track
|
||||
if (addToEnd) {
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
} else {
|
||||
// Try and locate the current track
|
||||
const currentTrackId = await TrackPlayer.getCurrentTrack();
|
||||
const currentTrackIndex = queue.findIndex(track => track.id === currentTrackId);
|
||||
|
||||
// Since the argument is the id to insert the track BEFORE, we need
|
||||
// to get the current track + 1
|
||||
const targetTrack = currentTrackIndex >= 0 && queue.length > 1
|
||||
? queue[currentTrackIndex + 1].id
|
||||
: undefined;
|
||||
|
||||
// Depending on whether this track exists, we either add it there,
|
||||
// or at the end of the queue.
|
||||
await TrackPlayer.add([ newTrack ], targetTrack);
|
||||
}
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
// Then we'll skip to it and play it
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
TrackPlayer.play();
|
||||
}, [credentials, tracks, queue]);
|
||||
if (play) {
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTrack;
|
||||
}, [credentials, tracks, queue, dispatch]);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
|
||||
/**
|
||||
* This hook retrieves the current playing track from TrackPlayer
|
||||
@@ -7,10 +8,11 @@ import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player'
|
||||
export default function useQueue(): Track[] {
|
||||
const state = usePlaybackState();
|
||||
const [queue, setQueue] = useState<Track[]>([]);
|
||||
const addedTrackCount = useTypedSelector(state => state.player);
|
||||
|
||||
useEffect(() => {
|
||||
TrackPlayer.getQueue().then(setQueue);
|
||||
}, [state]);
|
||||
}, [state, addedTrackCount]);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"baseUrl": "./src", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
"paths": {
|
||||
"@localisation": ["./localisation"]
|
||||
}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
|
||||
Reference in New Issue
Block a user