Compare commits

...

120 Commits

Author SHA1 Message Date
Lei Nelissen
f39ab85624 Fix android crashes and add modal exit button 2022-06-09 23:37:07 +02:00
Lei Nelissen
01bd17e8cb Fix borderStyle on image compilation issue 2022-05-19 23:06:03 +02:00
Lei Nelissen
1075e31623 Apply proper padding in Android FlatList 2022-05-19 23:05:46 +02:00
Lei Nelissen
8678e3d881 Render playback text properly on Android 2022-05-19 23:05:28 +02:00
Lei Nelissen
0fc087e832 Update screenshots and README 2022-05-18 23:07:08 +02:00
Lei Nelissen
a8d563f66d Release v1.2.5 2022-05-18 22:29:57 +02:00
Lei Nelissen
1c659d7d90 Merge pull request #83 from leinelissen/feature/redesign
Feature/redesign
2022-05-18 22:13:00 +02:00
Lei Nelissen
f6835d0553 Update all dependencies 2022-05-18 22:10:06 +02:00
Lei Nelissen
21eb1dca3b Remove redundant console.log 2022-05-18 22:00:07 +02:00
Lei Nelissen
a9dcd75f96 Transform Queue component into FlatList 2022-05-18 21:55:59 +02:00
Lei Nelissen
7ef54eb06d Prioritize the pan gesture over the tap gesture 2022-05-18 21:30:10 +02:00
Lei Nelissen
fcb6b1465b Swap string literals for translatable items 2022-05-18 21:23:24 +02:00
Lei Nelissen
06ead4a00e Upgrade react-airplay to version that includes route metadata 2022-05-18 10:18:07 +02:00
Lei Nelissen
f8d5aa68c2 Add default image styles and shadow to nowpalying overlay 2022-05-17 23:55:50 +02:00
Lei Nelissen
f66b541e1b Fix linting issues 2022-05-17 23:45:57 +02:00
Lei Nelissen
b989df43f3 Make nowplaying overlay slightly draggable 2022-05-17 23:37:06 +02:00
Lei Nelissen
de07fc65c4 Restyle the downloads screen 2022-05-17 23:34:25 +02:00
Lei Nelissen
c0fa1160ae Make padding-left optional on textinput 2022-05-17 23:05:45 +02:00
Lei Nelissen
f1925347cb Animate search bar with keyboard 2022-05-17 23:05:17 +02:00
Lei Nelissen
258ac34a2c Slightly adjust light-gray color on dark mode 2022-05-17 23:04:45 +02:00
Lei Nelissen
479f701d32 Remove themed background from welcome screen 2022-05-17 23:04:04 +02:00
Lei Nelissen
0589325055 Adjust dark mode colors 2022-05-16 22:28:13 +02:00
Lei Nelissen
a719e309ad Add stubs for filters in search 2022-05-16 22:17:00 +02:00
Lei Nelissen
ccc0c211fb Add shadows to cover images 2022-05-16 22:16:45 +02:00
Lei Nelissen
d7402bb409 First overhaul of search screen 2022-05-11 23:57:30 +02:00
Lei Nelissen
b7a5c0267c Readjust track popup 2022-05-11 22:13:42 +02:00
Lei Nelissen
b9016f9ba6 Make divider somewhat more visible on light mode 2022-05-10 23:56:20 +02:00
Lei Nelissen
07adb44f19 Add some margin between track name and media button 2022-05-10 23:55:02 +02:00
Lei Nelissen
b21766a352 Revamp the pop-up player modal 2022-05-10 23:52:58 +02:00
Lei Nelissen
37ead0ec98 feat: Apply default text styles to ReText 2022-05-05 22:59:32 +02:00
Lei Nelissen
b0961d3263 feat: Tweak progress bar gestures 2022-05-05 22:54:37 +02:00
Lei Nelissen
e135b23565 Update fastlane.yml 2022-05-05 11:18:28 +02:00
Lei Nelissen
015a1fa196 Update fastlane.yml 2022-05-05 11:07:47 +02:00
Lei Nelissen
25f16a875f Update fastlane.yml 2022-05-05 11:07:09 +02:00
Lei Nelissen
47aabfa8e7 Update fastlane.yml 2022-05-05 11:04:26 +02:00
Lei Nelissen
6efc8e757c feat: Create new progress slider from scratch 2022-05-05 03:30:51 +02:00
Lei Nelissen
f48d248144 feat: Implement colored blur backgrounds 2022-05-04 22:46:19 +02:00
Lei Nelissen
76f2db19e5 Change modal to native stacks 2022-05-04 19:12:01 +02:00
Lei Nelissen
2b24a37218 chore: Upgrade all dependencies 2022-05-04 18:29:11 +02:00
Lei Nelissen
7fb7fc1925 Implement new styles 2022-05-04 18:28:15 +02:00
Lei Nelissen
c4d83d29d8 Add Inter font 2022-05-04 18:28:15 +02:00
Lei Nelissen
d141b80a77 Move search to tabbar and remove now playing 2022-05-04 18:28:15 +02:00
Lei Nelissen
3b0ea4ece7 chore: Release new iOS version 2022-05-04 17:31:47 +02:00
Lei Nelissen
89d29844b9 fix: Only pull Exoplayer from jcenter 2022-05-04 17:16:58 +02:00
Lei Nelissen
f7874410d4 chore: Release new Android build 2022-05-04 17:03:43 +02:00
Lei Nelissen
8914a26822 chore: Bump version 2022-05-04 16:56:39 +02:00
Lei Nelissen
91eaa1d864 fix: No interaction on Android webview (#59) 2022-05-04 16:55:32 +02:00
Lei Nelissen
1402ac06f6 Bump major version 2022-01-16 12:37:09 +01:00
Lei Nelissen
f9334c51a3 Release new versions 2022-01-15 17:51:15 +01:00
Lei Nelissen
5d26a5395b Install JSON plugin for fastlane 2022-01-15 17:35:28 +01:00
Lei Nelissen
714535feeb Bump version 2022-01-15 17:26:07 +01:00
Lei Nelissen
7ea4857997 Queue downloads seperately so we don't overwhelm the app 2022-01-15 17:25:24 +01:00
Lei Nelissen
81ccb6b1f9 Use package.json for app version 2022-01-15 17:24:56 +01:00
Lei Nelissen
98ae0216f7 Replace icons with filled version 2022-01-15 17:24:47 +01:00
Lei Nelissen
55961d5530 Release new version 2022-01-03 09:08:00 +01:00
Lei Nelissen
7c32fb3599 Release new builds 2022-01-03 09:07:38 +01:00
Lei Nelissen
56971a9291 Add migrations for the store 2022-01-03 09:07:30 +01:00
Lei Nelissen
9bf20b1762 Release new version 2022-01-02 23:06:27 +01:00
Lei Nelissen
76598b38cb Add missing Dutch strings 2022-01-02 23:06:10 +01:00
Lei Nelissen
5d6f65b699 Add offline message as i18n string 2022-01-02 23:03:15 +01:00
Lei Nelissen
f78db52e0a Fix Dark Mode colors in new features 2022-01-02 23:02:54 +01:00
Lei Nelissen
611cbc8c69 Add connection notice 2022-01-02 22:50:49 +01:00
Lei Nelissen
cab3935a92 Update XCode so it builds 2022-01-02 22:50:38 +01:00
Lei Nelissen
f8e57827f2 Remove RNFS handler as it breaks the build 2022-01-02 22:50:19 +01:00
Lei Nelissen
55494caf4f Fix Android issues 2022-01-02 21:14:05 +01:00
Lei Nelissen
b7d16a174b Release new version 2022-01-02 19:30:54 +01:00
Lei Nelissen
3c98bf92eb Merge pull request #67 from leinelissen/feature/downloads
Downloads
2022-01-02 19:30:08 +01:00
Lei Nelissen
ebd46970d9 Fix linting errors 2022-01-02 19:29:20 +01:00
Lei Nelissen
cc0dfc2528 Allow for retring individual tracks 2022-01-02 19:16:12 +01:00
Lei Nelissen
74ddc58f83 Allow retries for failed tracks 2022-01-02 18:11:30 +01:00
Lei Nelissen
95a008d8af Add buttons for deleting downloaded tracks from playlist or album 2022-01-02 17:46:50 +01:00
Lei Nelissen
db1c9d8af9 Adjust transport container so we can actually download non-support audio files 2022-01-02 17:21:24 +01:00
Lei Nelissen
c28c1a851d Only render search message if nothing is found 2022-01-02 17:21:02 +01:00
Lei Nelissen
d2fb4a4aea Basic download implementation 2022-01-02 02:28:52 +01:00
Lei Nelissen
284d23d30b Merge pull request #66 from leinelissen/feature/playlists
Add playlists
2022-01-01 22:58:47 +01:00
Lei Nelissen
464747d0c4 Limit tsc output to our files rather than libraries 2022-01-01 22:44:15 +01:00
Lei Nelissen
419ce99d08 Add linting to GitHub actions 2022-01-01 22:37:58 +01:00
Lei Nelissen
4e6a758d83 Fix linting and typescript errors 2022-01-01 22:36:05 +01:00
Lei Nelissen
4ecf978505 Add Dutch translations 2022-01-01 21:58:06 +01:00
Lei Nelissen
5d96a67336 Make playlist view full-height 2022-01-01 21:57:18 +01:00
Lei Nelissen
6a4b5618aa Resolve track pop-up spacing 2022-01-01 21:55:32 +01:00
Lei Nelissen
75e8ece60a Add playlists 2022-01-01 19:09:21 +01:00
Lei Nelissen
4460bdf7f9 (1) Automatically focus on text field in search
(2) Restyle no results message
2022-01-01 14:27:08 +01:00
Lei Nelissen
9b41a0e62f Contextually disable previous and next buttons 2022-01-01 14:10:56 +01:00
Lei Nelissen
93c9ba7498 Adjust modal display with new react-navigation look 2022-01-01 14:09:52 +01:00
Lei Nelissen
9668ba9c7c Upgrade all dependencies
(1) react-native-track-player to v2
(2) react-navigation to v6
(3) react-native to v0.66.4
2021-12-31 15:04:37 +01:00
Lei Nelissen
6440c1ac7b Bump version 2021-10-25 23:50:33 +02:00
Lei Nelissen
6c0a277397 Update dependencies 2021-10-25 23:36:14 +02:00
Lei Nelissen
080a664d4f Merge pull request #58 from Sjord/nl-locale-fixes
Dutch translation fixes
2021-10-25 22:25:22 +02:00
Sjoerd Langkemper
ae6a99ce6d Dutch translation fixes
achtergrond = background
instellen, stel ... in = to configure
2021-10-12 21:26:02 +02:00
Lei Nelissen
b14f546525 Add Japanese as a locale 2021-05-12 22:18:23 +02:00
Lei Nelissen
2b8e78a076 Merge pull request #46 from leinelissen/translations_src-localisation-lang-en-locale-json--master_ja
Translate '/src/localisation/lang/en/locale.json' in 'ja'
2021-05-12 22:14:56 +02:00
Lei Nelissen
4a345699a8 Fix search box (fix #47) 2021-05-12 22:13:44 +02:00
Lei Nelissen
eaef9be7db Update dependencies 2021-05-12 22:03:29 +02:00
transifex-integration[bot]
8049a25cce Apply translations in ja
translation completed for the source file '/src/localisation/lang/en/locale.json'
on the 'ja' language.
2021-05-12 14:42:54 +00:00
Lei Nelissen
7403ab986b Release build 16 2021-04-25 23:45:20 +02:00
Lei Nelissen
d8e9f8f4c6 Allow arbitrary loads (ie HTTP) on iOS
Fixes #42
2021-04-25 23:16:27 +02:00
Lei Nelissen
fa05935017 Release build 15 2021-04-24 23:18:01 +02:00
Lei Nelissen
2de5cc8e6c Polish search engine UX 2021-04-24 15:30:07 +02:00
Lei Nelissen
24d484ca25 Add track results to search queries 2021-04-24 14:50:43 +02:00
Lei Nelissen
a7b24cf4eb Fix XMLDom issue 2021-04-24 12:21:02 +02:00
Lei Nelissen
eaa1402173 Update packages 2021-04-24 12:18:56 +02:00
Lei Nelissen
1edeb00631 Fix active button color on Android 2021-04-03 15:19:38 +02:00
Lei Nelissen
d422c1ff1e Pull current track from Redux store
Fixes #40
2021-04-03 14:49:49 +02:00
Lei Nelissen
28b330ad4c Update all dependencies 2021-04-03 14:49:01 +02:00
Lei Nelissen
a867513212 Update Fastlane Sentry plugin 2021-03-22 09:49:33 +01:00
Lei Nelissen
14a6341fae Release build 13 2021-03-22 09:24:36 +01:00
Lei Nelissen
645f1307f3 Add OGG Support
Fixes #12
2021-03-21 23:01:07 +01:00
Lei Nelissen
3f16bd6465 Show Album art instead of Track art if an AlbumId is available
Fixes #13
2021-03-21 22:55:35 +01:00
Lei Nelissen
5c15d730b3 Add AirPlay casting button 2021-03-21 22:42:04 +01:00
Lei Nelissen
603bf10154 Switch back to regular react-native-track-player version as patch was merged 2021-03-21 22:12:36 +01:00
Lei Nelissen
57f9e0e08e Upgrade Android build for React Native 0.64 2021-03-21 21:50:35 +01:00
Lei Nelissen
597a348eab Release build 12 2021-03-21 21:50:15 +01:00
Lei Nelissen
8860581450 Upgrade to React Native 0.64 2021-03-21 20:16:26 +01:00
Lei Nelissen
a92ff581ae Merge pull request #38 from yisiliang/master
add Chinese language support
2021-03-21 18:51:04 +01:00
YiSiliang
cd2e333caa add Chinese language support 2021-03-14 21:07:50 +08:00
Lei Nelissen
4ba83b8efc Release build 11 2021-03-09 22:42:34 +01:00
Lei Nelissen
e4daccfe5e Update packages 2021-03-09 22:25:43 +01:00
Lei Nelissen
76c45623a0 Fix launch crash on devices without fallback languages 2021-03-09 22:21:41 +01:00
Lei Nelissen
e4b93ec8c8 Release build 10 2021-02-13 22:16:04 +01:00
173 changed files with 25241 additions and 15503 deletions

2
.bundle/config Normal file
View File

@@ -0,0 +1,2 @@
BUNDLE_PATH: "vendor/bundle"
BUNDLE_FORCE_RUBY_PLATFORM: 1

3
.editorconfig Normal file
View File

@@ -0,0 +1,3 @@
# Windows files
[*.bat]
end_of_line = crlf

View File

@@ -53,5 +53,10 @@ module.exports = {
'@typescript-eslint/no-unused-vars': [
'error'
]
},
settings: {
react: {
version: 'detect',
}
}
};

4
.gitattributes vendored
View File

@@ -1 +1,3 @@
*.pbxproj -text
# Windows files should use crlf line endings
# https://help.github.com/articles/dealing-with-line-endings/
*.bat text eol=crlf

View File

@@ -3,26 +3,6 @@ name: Fastlane
on: [push]
jobs:
# build-ios:
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v1
# - name: Install Node dependencies
# run: npm install
# - name: Install CocoaPods dependencies
# run: pod install --project-directory=./ios
# - name: Run fastlane setup
# env:
# APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
# TEAM_ID: ${{ secrets.TEAM_ID }}
# run: |
# cd ios
# fastlane beta --verbose
# - name: Upload artifact
# uses: actions/upload-artifact@v2
# with:
# name: my-artifact
# path: output/*.ipa
build-android:
runs-on: ubuntu-latest
steps:
@@ -30,14 +10,12 @@ jobs:
- name: Set outputs
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
- name: Set up Ruby 2.7
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6.x
ruby-version: 2.7
- name: Install fastlane
run: |
gem install bundler
bundle install -j 6
run: bundle install -j 6
- name: Install Node dependencies
run: npm install
- name: Generate APK
@@ -47,9 +25,9 @@ jobs:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
run: fastlane android beta
run: bundle exec fastlane android beta
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: jellyfin-audio-player-android-${{ steps.vars.outputs.sha_short }}.apk
path: android/app/build/outputs/**/*.apk
path: android/app/build/outputs/**/*.apk

13
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: Lint
on: [push]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install Node dependencies
run: npm install
- name: Run linter
run: npm run lint

1
.gitignore vendored
View File

@@ -61,6 +61,7 @@ buck-out/
# CocoaPods
/ios/Pods/
/vendor/bundle/
build/
fastlane/report.xml

1
.ruby-version Normal file
View File

@@ -0,0 +1 @@
2.7.4

View File

@@ -4,8 +4,7 @@ source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
# gem "rails"
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
gem "fastlane", "~> 2.153"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')

View File

@@ -2,61 +2,74 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.3)
addressable (2.7.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.1.0)
aws-partitions (1.426.0)
aws-sdk-core (3.112.0)
aws-eventstream (1.1.1)
aws-partitions (1.488.0)
aws-sdk-core (3.119.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.42.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-kms (1.46.0)
aws-sdk-core (~> 3, >= 3.119.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.88.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-s3 (1.99.0)
aws-sdk-core (~> 3, >= 3.119.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.2)
aws-sigv4 (1.2.4)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
declarative-option (0.1.0)
digest-crc (0.6.3)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.1)
excon (0.79.0)
faraday (1.3.0)
emoji_regex (3.2.2)
excon (0.85.0)
faraday (1.7.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
faraday_middleware (1.0.0)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday_middleware (1.1.0)
faraday (~> 1.0)
fastimage (2.2.2)
fastlane (2.174.0)
fastimage (2.2.5)
fastlane (2.191.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
@@ -65,18 +78,19 @@ GEM
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.37.0, < 0.39.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
@@ -85,61 +99,56 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-sentry (1.8.0)
fastlane-plugin-sentry (1.8.1)
gh_inspector (1.1.3)
google-api-client (0.38.0)
google-apis-androidpublisher_v3 (0.10.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.12)
google-apis-core (0.2.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.14)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
retriable (>= 2.0, < 4.a)
rexml
signet (~> 0.14)
webrick
google-apis-iamcredentials_v1 (0.1.0)
google-apis-core (~> 0.1)
google-apis-storage_v1 (0.2.0)
google-apis-core (~> 0.1)
google-cloud-core (1.5.0)
google-apis-iamcredentials_v1 (0.6.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.5.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.6.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.4.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.30.0)
google-cloud-errors (1.1.0)
google-cloud-storage (1.34.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (0.15.1)
googleauth (0.17.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
highline (1.7.10)
http-cookie (1.0.3)
highline (2.0.3)
http-cookie (1.0.4)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.4.0)
json (2.5.1)
jwt (2.2.2)
jwt (2.2.3)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.0.2)
mini_mime (1.1.0)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
@@ -147,18 +156,18 @@ GEM
os (1.1.1)
plist (3.6.0)
public_suffix (4.0.6)
rake (13.0.3)
representable (3.0.4)
rake (13.0.6)
representable (3.1.1)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.4)
rexml (3.2.5)
rouge (2.0.7)
ruby2_keywords (0.0.4)
rubyzip (2.3.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.14.1)
signet (0.15.0)
addressable (~> 2.3)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
@@ -166,10 +175,10 @@ GEM
simctl (1.6.8)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.1)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
@@ -181,12 +190,13 @@ GEM
unicode-display_width (1.7.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.19.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)

View File

@@ -7,8 +7,10 @@ This is a [React Native](https://reactnative.dev/)-based audio streaming app for
## ❗Now open for beta testing on iOS
Please follow this link to enroll for the TestFlight beta release of Jellyfin Audio Player: https://testflight.apple.com/join/cf2AMDpx.
|![](./docs/images/now-playing.png)|![](./docs/images/recent-albums.png)|![](./docs/images/album-list.png)|![](./docs/images/search.png)|
|-|-|-|-|
|![](./docs/images/now-playing.png)|![](./docs/images/recent-albums.png)|![](./docs/images/album-list.png)
|-|-|-|
|![](./docs/images/album.png)|![](./docs/images/downloads.png)|![](./docs/images/search.png)
## Features
* Sorting by recent albums
@@ -18,14 +20,12 @@ Please follow this link to enroll for the TestFlight beta release of Jellyfin Au
* AirPlay and Chromecast support
* Background audio
* Native Dark Mode
### Features being considered
* Downloading music for offline playback
* Searching based on track names
* Looping and shuffling queue
## Getting Started
This piece of software is in alpha. I am working on getting this app in ~~TestFlight and~~ Google Play Developer Console, but this is contingent on keys being available. In the meantime, IPAs and APK are intermittenly released on the [Releases page](https://github.com/leinelissen/jellyfin-audio-player/releases). Alternatively, you can build this app from source using the build instructions.
This piece of software is in beta. I am working on getting this app in ~~TestFlight and~~ Google Play Developer Console, but this is contingent on keys being available. In the meantime, IPAs and APK are intermittenly released on the [Releases page](https://github.com/leinelissen/jellyfin-audio-player/releases). Alternatively, you can build this app from source using the build instructions.
### Using the app
You will need to setup your Jellyfin account for the application to be able to pull in all your audio. To do this, go over to the "Settings" tab and click the "Set Jellyfin server"-button. A modal will pop up in which you will enter your Jellyfin server url, after which you enter your credentials in the provided browser view. When the app detects your credentials, they will automatically be remembered for the future.

View File

@@ -1,6 +1,7 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
@@ -20,7 +21,7 @@ import com.android.build.OutputFile
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
@@ -79,11 +80,9 @@ import com.android.build.OutputFile
project.ext.react = [
enableHermes: false, // clean and rebuild if changing
entryFile: 'index.js'
]
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/@sentry/react-native/sentry.gradle"
/**
* Set this to true to create two separate APKs instead of one:
@@ -116,34 +115,112 @@ def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
android {
compileSdkVersion rootProject.ext.compileSdkVersion
/**
* Architectures to build native code for.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.jellyfinaudioplayer"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
multiDexEnabled true
versionCode 9
versionName "1.2.5"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-21",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=$buildDir/generated/source",
"PROJECT_BUILD_DIR=$buildDir",
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
// Make sure this target name is the same you specify inside the
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
targets "rndiffapp_appmodules"
// Fix for windows limit on number of character in file paths and in command lines
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
arguments "NDK_OUT=${rootProject.projectDir.getParent()}\\.cxx",
"NDK_APP_SHORT_COMMANDS=true"
}
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
path "$projectDir/src/main/jni/Android.mk"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureNdkBuild* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureNdkBuildRelease.dependsOn(preReleaseBuild)
configureNdkBuildDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
include (*reactNativeArchitectures())
}
}
signingConfigs {
@@ -160,7 +237,7 @@ android {
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
@@ -172,11 +249,12 @@ android {
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
@@ -185,17 +263,19 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
@@ -211,11 +291,35 @@ dependencies {
}
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source")
}
resolutionStrategy {
force 'com.google.android.exoplayer:exoplayer-core:2.11.4'
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
from configurations.implementation
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

View File

@@ -4,5 +4,12 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
<application
android:usesCleartextTraffic="true"
tools:targetApi="28"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="com.facebook.react.devsupport.DevSettingsActivity"
android:exported="false" />
</application>
</manifest>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
@@ -19,6 +19,7 @@ import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
@@ -51,7 +52,7 @@ public class ReactNativeFlipper {
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
new ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);

View File

@@ -14,15 +14,14 @@
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
</manifest>

View File

@@ -1,6 +1,7 @@
package com.jellyfinaudioplayer;
import com.facebook.react.ReactActivity;
import android.os.Bundle;
public class MainActivity extends ReactActivity {
@@ -12,4 +13,9 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "JellyfinAudioPlayer";
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
}

View File

@@ -11,6 +11,9 @@ import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
@@ -33,6 +36,11 @@ public class MainApplication extends Application implements ReactApplication {
protected String getJSMainModuleName() {
return "index";
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage();
}
};
@Override

View File

@@ -1,19 +1,34 @@
import org.apache.tools.ant.taskdefs.condition.Os
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
buildToolsVersion = "31.0.0"
minSdkVersion = 21
compileSdkVersion = 31
targetSdkVersion = 31
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
// For Android Users, we need to use NDK 23, otherwise the build will
// fail due to paths longer than the OS limit
ndkVersion = "23.1.7779620"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
}
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:3.5.2")
classpath("com.android.tools.build:gradle:7.0.4")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:4.1.2")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -21,7 +36,6 @@ buildscript {
allprojects {
repositories {
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
@@ -30,9 +44,21 @@ allprojects {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
google()
jcenter()
maven { url 'https://www.jitpack.io' }
jcenter() {
content {
includeGroup("com.google.android.exoplayer")
includeGroupByRegex("com.eightbitlab.*")
}
}
}
}
}

View File

@@ -9,8 +9,8 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
@@ -25,4 +25,16 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.54.0
FLIPPER_VERSION=0.125.0
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

272
android/gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,78 +17,113 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -105,84 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
exec "$JAVACMD" "$@"
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

200
android/gradlew.bat vendored
View File

@@ -1,100 +1,100 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,3 +1,9 @@
rootProject.name = 'JellyfinAudioPlayer'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
}

View File

@@ -31,6 +31,7 @@ module.exports = {
],
[
'module:react-native-dotenv'
]
],
'react-native-reanimated/plugin'
]
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 2.9 MiB

BIN
docs/images/album.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
docs/images/downloads.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 261 KiB

View File

@@ -1,18 +1,8 @@
default_platform(:ios)
package = load_json(json_path: "package.json")
platform :ios do
lane :alpha do
get_certificates(
development: true,
output_path: 'certificates/'
)
build_app(
scheme: "Jellyfin Player",
export_method: "development",
output_directory: "build",
workspace: "ios/JellyfinAudioPlayer.xcworkspace"
)
end
lane :beta do
get_certificates(
output_path: 'certificates/'
@@ -26,8 +16,12 @@ platform :ios do
use_automatic_signing: true,
path: "ios/JellyfinAudioPlayer.xcodeproj"
)
increment_version_number(
version_number: package["version"],
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj",
);
increment_build_number(
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj"
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj",
)
build_app(
scheme: "Jellyfin Player",
@@ -37,7 +31,7 @@ platform :ios do
)
upload_to_testflight
build_number = get_build_number(
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj"
xcodeproj: "ios/JellyfinAudioPlayer.xcodeproj",
)
Dir.chdir("..") do
sh(
@@ -78,8 +72,15 @@ end
platform :android do
desc "Generate beta build"
lane :beta do
android_set_version_name(
version_name: package['version'],
gradle_file: "android/app/build.gradle"
)
android_set_version_code(
gradle_file: "android/app/build.gradle"
)
gradle(
task: "clean assembleRelease",
task: "assembleRelease",
project_dir: "android"
)
end

View File

@@ -3,3 +3,5 @@
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-sentry'
gem 'fastlane-plugin-load_json'
gem 'fastlane-plugin-versioning_android'

View File

@@ -1,43 +1,45 @@
fastlane documentation
================
----
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
```sh
xcode-select --install
```
Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew install fastlane`
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
# Available Actions
## iOS
### ios alpha
```
fastlane ios alpha
```
### ios beta
```sh
[bundle exec] fastlane ios beta
```
fastlane ios beta
```
----
## Android
### android beta
```sh
[bundle exec] fastlane android beta
```
fastlane android beta
```
Generate beta build
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).

View File

@@ -5,7 +5,11 @@ import App from './src/components/App';
import { name as appName } from './app.json';
import PlaybackService from './src/utility/PlaybackService';
import { setupSentry } from 'utility/Sentry';
import { enableScreens } from 'react-native-screens';
import { patchTrackPlayer } from 'utility/AddedTrackEvents';
setupSentry();
enableScreens();
patchTrackPlayer();
AppRegistry.registerComponent(appName, () => App);
TrackPlayer.registerPlaybackService(() => PlaybackService);

View File

@@ -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 */; };
4C04FC6E055249ABB204D3BC /* Inter-VariableFont_slnt,wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4B4A0465FF364579B28CF5D7 /* Inter-VariableFont_slnt,wght.ttf */; };
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 */
@@ -40,6 +41,7 @@
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>"; };
4B4A0465FF364579B28CF5D7 /* Inter-VariableFont_slnt,wght.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-VariableFont_slnt,wght.ttf"; path = "../src/assets/fonts/Inter-VariableFont_slnt,wght.ttf"; 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>"; };
@@ -152,6 +154,7 @@
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
46001D7383D71A837AAF6E07 /* Pods */,
CFCEB457E84E4C5195253CD7 /* Resources */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -167,6 +170,14 @@
name = Products;
sourceTree = "<group>";
};
CFCEB457E84E4C5195253CD7 /* Resources */ = {
isa = PBXGroup;
children = (
4B4A0465FF364579B28CF5D7 /* Inter-VariableFont_slnt,wght.ttf */,
);
name = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -179,6 +190,7 @@
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
BDE784ECF29EF861DBFF49D7 /* [CP] Copy Pods Resources */,
A02366876E56A727F566EC3A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -201,7 +213,7 @@
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
B9FB8FC65CEFF9AFAC71127E /* [CP] Copy Pods Resources */,
1DC46C84C90B4D84A18AC142 /* Upload Debug Symbols to Sentry */,
2917566AA57EE087FC9FCCE9 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -266,6 +278,7 @@
files = (
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
4C04FC6E055249ABB204D3BC /* Inter-VariableFont_slnt,wght.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -287,21 +300,51 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
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";
shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
1DC46C84C90B4D84A18AC142 /* Upload Debug Symbols to Sentry */ = {
2917566AA57EE087FC9FCCE9 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer/Pods-JellyfinAudioPlayer-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
);
name = "Upload Debug Symbols to Sentry";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export SENTRY_PROPERTIES=sentry.properties\n../node_modules/@sentry/cli/bin/sentry-cli upload-dsym";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer/Pods-JellyfinAudioPlayer-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A02366876E56A727F566EC3A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests/Pods-JellyfinAudioPlayer-JellyfinAudioPlayerTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
B9FB8FC65CEFF9AFAC71127E /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
@@ -461,6 +504,10 @@
INFOPLIST_FILE = JellyfinAudioPlayerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"$(inherited)",
);
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@@ -484,6 +531,10 @@
INFOPLIST_FILE = JellyfinAudioPlayerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"$(inherited)",
);
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@@ -504,7 +555,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = 238P3C58WC;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -513,6 +564,10 @@
);
INFOPLIST_FILE = JellyfinAudioPlayer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"$(inherited)",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -534,11 +589,16 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = 238P3C58WC;
INFOPLIST_FILE = JellyfinAudioPlayer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"$(inherited)",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -587,7 +647,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -603,11 +663,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = YES;
@@ -645,12 +705,12 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_IDENTITY = "Apple Distribution: Bureau Moeilijke Dingen BV (238P3C58WC)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution: Bureau Moeilijke Dingen BV (238P3C58WC)";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -659,11 +719,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = NO;

View File

@@ -4,42 +4,49 @@
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#if DEBUG
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
#import <React/RCTAppSetupUtils.h>
static void InitializeFlipper(UIApplication *application) {
FlipperClient *client = [FlipperClient sharedClient];
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
[client addPlugin:[FlipperKitReactPlugin new]];
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
[client start];
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if DEBUG
InitializeFlipper(application);
#endif
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"JellyfinAudioPlayer"
initialProperties:nil];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"JellyfinAudioPlayer", nil);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
@@ -53,10 +60,49 @@ static void InitializeFlipper(UIApplication *application) {
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@end
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end

View File

@@ -17,28 +17,20 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.2.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>9</string>
<string>37</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<string/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
@@ -58,5 +50,9 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIAppFonts</key>
<array>
<string>Inter-VariableFont_slnt,wght.ttf</string>
</array>
</dict>
</plist>

View File

@@ -2,7 +2,8 @@
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

View File

@@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.2.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>9</string>
<string>37</string>
</dict>
</plist>

View File

@@ -1,24 +1,36 @@
platform :ios, '10.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/react-native/node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '12.0'
target 'JellyfinAudioPlayer' do
config = use_native_modules!
use_react_native!(:path => config["reactNativePath"])
# Flags change depending on the env values.
flags = get_default_flags()
use_react_native!(
:path => config[:reactNativePath],
# to enable hermes on iOS, change `false` to `true` and then install pods
:hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled],
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
target 'JellyfinAudioPlayerTests' do
inherit! :complete
# Pods for testing
end
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!
# you should disable the next line.
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
react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
end
end

File diff suppressed because it is too large Load Diff

33254
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "JellyfinAudioPlayer",
"version": "0.0.1",
"version": "1.2.5",
"main": "src/index.js",
"private": true,
"scripts": {
@@ -8,66 +8,81 @@
"ios": "react-native run-ios --scheme \"Jellyfin Player\"",
"start": "react-native start",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx && tsc --noEmit",
"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.12.1",
"@react-native-community/masked-view": "^0.1.10",
"@react-native-community/blur": "^3.6.0",
"@react-native-community/masked-view": "^0.1.11",
"@react-native-community/netinfo": "^8.3.0",
"@react-native-community/picker": "^1.8.1",
"@react-native-community/slider": "^3.0.3",
"@react-navigation/bottom-tabs": "^5.11.7",
"@react-navigation/native": "^5.9.2",
"@react-navigation/stack": "^5.14.2",
"@reduxjs/toolkit": "^1.5.0",
"@sentry/react-native": "^2.2.0",
"@types/lodash": "^4.14.168",
"@types/redux-logger": "^3.0.8",
"@types/styled-components": "^5.1.7",
"date-fns": "^2.17.0",
"fuse.js": "^6.4.6",
"i18n-js": "^3.8.0",
"lodash": "^4.17.20",
"react": "16.13.1",
"react-native": "^0.63.4",
"react-native-collapsible": "^1.5.3",
"react-native-dotenv": "^2.5.1",
"react-native-fast-image": "^8.3.4",
"react-native-gesture-handler": "^1.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": "^11.2.1",
"react-redux": "^7.2.2",
"redux": "^4.0.5",
"@react-native-community/slider": "^4.2.2",
"@react-native/normalize-color": "^2.0.0",
"@react-navigation/bottom-tabs": "^6.3.1",
"@react-navigation/native": "^6.0.10",
"@react-navigation/native-stack": "^6.6.2",
"@react-navigation/stack": "^6.2.1",
"@reduxjs/toolkit": "^1.8.1",
"@sentry/react-native": "^3.4.2",
"@shopify/react-native-skia": "^0.1.124",
"@types/lodash": "^4.14.182",
"date-fns": "^2.28.0",
"events": "^3.3.0",
"fuse.js": "^6.6.2",
"hermes-engine": "^0.11.0",
"i18n-js": "^3.9.2",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-airplay": "^1.2.0",
"react-native": "^0.68.2",
"react-native-collapsible": "^1.6.0",
"react-native-dotenv": "^3.3.1",
"react-native-fast-image": "^8.5.11",
"react-native-flipper": "^0.146.0",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.4.2",
"react-native-localize": "^2.2.1",
"react-native-reanimated": "^2.8.0",
"react-native-safe-area-context": "^4.2.5",
"react-native-screens": "^3.13.1",
"react-native-shadow-2": "^6.0.5",
"react-native-svg": "^12.3.0",
"react-native-svg-transformer": "^1.0.0",
"react-native-track-player": "^2.2.0-rc3",
"react-native-webview": "^11.18.2",
"react-redux": "^8.0.1",
"redux": "^4.2.0",
"redux-flipper": "^2.0.1",
"redux-logger": "^3.0.6",
"redux-persist": "^6.0.0",
"styled-components": "^5.2.1"
"styled-components": "^5.3.5"
},
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/runtime": "^7.8.4",
"@react-native-community/eslint-config": "^2.0.0",
"@types/i18n-js": "^3.8.0",
"@types/jest": "^26.0.20",
"@types/react-native": "^0.63.48",
"@types/react-redux": "^7.1.16",
"@types/react-test-renderer": "^17.0.0",
"@types/styled-components-react-native": "^5.1.1",
"@typescript-eslint/eslint-plugin": "^4.14.2",
"@typescript-eslint/parser": "^4.14.2",
"babel-jest": "^26.6.3",
"@babel/core": "^7.17.12",
"@babel/runtime": "^7.17.9",
"@react-native-community/eslint-config": "^3.0.2",
"@sentry/cli": "^2.0.4",
"@types/i18n-js": "^3.8.2",
"@types/jest": "^27.5.1",
"@types/react-native": "^0.67.7",
"@types/react-test-renderer": "^17.0.1",
"@types/redux-logger": "^3.0.9",
"@types/styled-components": "^5.1.25",
"@types/styled-components-react-native": "^5.1.3",
"@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.25.0",
"babel-jest": "^28.1.0",
"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.59.0",
"react-test-renderer": "^17.0.1",
"typescript": "^4.1.3"
"eslint": "^8.15.0",
"eslint-plugin-react-hooks": "^4.5.0",
"jest": "^28.1.0",
"metro-config": "^0.70.3",
"metro-react-native-babel-preset": "^0.70.3",
"metro-react-native-babel-transformer": "^0.70.3",
"react-native-codegen": "^0.69.1",
"react-test-renderer": "^17.0.2",
"typescript": "^4.6.4"
},
"jest": {
"preset": "react-native",
@@ -79,5 +94,8 @@
"json",
"node"
]
},
"overrides": {
"@types/react-native": "^0.67.7"
}
}

7
react-native.config.js Normal file
View File

@@ -0,0 +1,7 @@
module.exports = {
project: {
ios: {},
android: {}
},
assets: ['./src/assets/fonts/'],
};

View File

@@ -1,3 +1,4 @@
export const ALBUM_CACHE_AMOUNT_OF_DAYS = 7;
export const PLAYLIST_CACHE_AMOUNT_OF_DAYS = 7;
export const ALPHABET_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ';
export const THEME_COLOR = '#FF3C00';

Binary file not shown.

93
src/assets/fonts/OFL.txt Normal file
View File

@@ -0,0 +1,93 @@
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56"><defs><clipPath id="clip-path"><path d="M39.31 44.79a.73.73 0 0 1-.54 1.21H17.23a.73.73 0 0 1-.54-1.21l10.68-12.3a.84.84 0 0 1 1.26 0Z"/></clipPath><clipPath id="clip-path-2"><path style="fill:none" d="M-4-3h64v64H-4z"/></clipPath><clipPath id="clip-path-3"><path d="M28 22a6 6 0 0 0-3.65 10.76.16.16 0 0 1 0 .23l-.86 1-.19.21a.18.18 0 0 1-.27 0l-.32-.2a8 8 0 1 1 10.56 0l-.31.25a.18.18 0 0 1-.27 0l-.2-.22-.85-1a.17.17 0 0 1 0-.24A6 6 0 0 0 28 22Zm0-5a11 11 0 0 0-7 19.49l.1.07a.15.15 0 0 1 0 .21L21 37l-.88 1a.2.2 0 0 1-.28 0h-.14a13 13 0 1 1 16.6 0l-.12.09a.14.14 0 0 1-.2 0L35.9 38l-.89-1-.14-.16a.14.14 0 0 1 0-.21l.14-.11A11 11 0 0 0 28 17ZM17.82 40.33a.21.21 0 0 1 0 .29l-.06.07-.92 1.07-.06.07a.2.2 0 0 1-.28 0l-.08-.07a18 18 0 1 1 23 .07.16.16 0 0 1-.21 0l-1-1.2-.05-.05a.16.16 0 0 1 0-.21h.06a16 16 0 1 0-20.49-.07Z"/></clipPath></defs><path d="M11.51 27.21h32.97V51H11.51z" style="clip-path:url(#clip-path)"/><path d="M5 5h46v41.92H5z" style="clip-path:url(#clip-path-3)"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.74512 9.09863C8 9.09863 8.20215 9.01953 8.36035 8.85254L11.9639 5.22266C12.1572 5.0293 12.2451 4.81836 12.2451 4.57227C12.2451 4.33496 12.1484 4.10645 11.9639 3.93066L8.36035 0.265625C8.20215 0.0898438 8 0.00195312 7.74512 0.00195312C7.27051 0.00195312 6.89258 0.397461 6.89258 0.880859C6.89258 1.11816 6.98047 1.31152 7.12988 1.47852L9.23047 3.53516C8.81738 3.47363 8.39551 3.43848 7.97363 3.43848C3.62305 3.43848 0.142578 6.91895 0.142578 11.2783C0.142578 15.6377 3.64941 19.1445 8 19.1445C12.3594 19.1445 15.8574 15.6377 15.8574 11.2783C15.8574 10.751 15.4883 10.373 14.9609 10.373C14.4512 10.373 14.1084 10.751 14.1084 11.2783C14.1084 14.6709 11.3926 17.3955 8 17.3955C4.61621 17.3955 1.8916 14.6709 1.8916 11.2783C1.8916 7.85938 4.58984 5.15234 7.97363 5.15234C8.54492 5.15234 9.07227 5.19629 9.53809 5.27539L7.13867 7.64844C6.98047 7.80664 6.89258 8 6.89258 8.2373C6.89258 8.7207 7.27051 9.09863 7.74512 9.09863Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,3 @@
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.77493 11.4212C0.325905 11.4212 0 11.7543 0 12.2106C0 12.6669 0.325905 13 0.77493 13H10.3203C10.7766 13 11.1097 12.6669 11.1097 12.2106C11.1097 11.7543 10.7766 11.4212 10.3203 11.4212H5.72145C5.88802 11.385 6.03287 11.2981 6.15599 11.1749L10.7694 6.61226C10.9432 6.43844 11.0301 6.23565 11.0301 6.02563C11.0301 5.56936 10.6969 5.23621 10.2552 5.23621C10.0234 5.23621 9.82061 5.33036 9.67577 5.47521L8.19109 6.93816L6.28635 9.02396L6.35877 7.50306V0.818384C6.35877 0.325905 6.03287 0 5.55487 0C5.07688 0 4.74373 0.325905 4.74373 0.818384V7.50306L4.8234 9.0312L2.91866 6.93816L1.44123 5.47521C1.28914 5.33036 1.08635 5.23621 0.861838 5.23621C0.412813 5.23621 0.0869081 5.56936 0.0869081 6.02563C0.0869081 6.23565 0.173816 6.43844 0.347632 6.61226L4.95376 11.1749C5.07688 11.2981 5.22173 11.385 5.38106 11.4212H0.77493Z" />
</svg>

After

Width:  |  Height:  |  Size: 935 B

View File

@@ -0,0 +1,10 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3_22)">
<path d="M0.689175 29.9526H3.63979C4.83259 29.9526 5.44782 29.3373 5.44782 28.1445V20.0084C5.3976 19.8451 5.38505 19.6819 5.38505 19.4936C5.38505 19.3178 5.3976 19.142 5.44782 18.9913V10.8677C5.44782 9.64983 4.83259 9.07226 3.63979 9.05971H0.689175C-0.503626 9.05971 -1.11886 9.67494 -1.11886 10.8677V28.1445C-1.13142 29.3373 -0.516182 29.9526 0.689175 29.9526ZM21.243 29.6136C22.3479 29.6136 23.2771 28.7849 23.2771 27.2531V20.0084C23.4403 20.6236 23.9049 21.1384 24.6456 21.5778L37.5153 29.1364C38.0552 29.4503 38.5324 29.6136 39.0848 29.6136C40.1897 29.6136 41.1189 28.7849 41.1189 27.2531V11.7592C41.1189 10.2274 40.1897 9.39871 39.0848 9.39871C38.5324 9.39871 38.0552 9.56194 37.5153 9.87583L24.6456 17.4344C23.8923 17.8864 23.4403 18.3761 23.2771 18.9913V11.7592C23.2771 10.2274 22.3605 9.39871 21.2556 9.39871C20.7031 9.39871 20.226 9.56194 19.6735 9.87583L6.80385 17.4344C6.06306 17.8864 5.59849 18.3761 5.44782 18.9913V20.0084C5.61105 20.6236 6.06306 21.1384 6.80385 21.5778L19.6735 29.1364C20.226 29.4503 20.6906 29.6136 21.243 29.6136Z"/>
</g>
<defs>
<clipPath id="clip0_3_22">
<rect width="40" height="40" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 531 B

View File

Before

Width:  |  Height:  |  Size: 422 B

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="21" viewBox="0 0 24 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.63672 15.2002H10.9453V9.82129C10.9453 9.25 11.4287 8.78418 12 8.78418C12.5713 8.78418 13.0459 9.25 13.0459 9.82129V15.2002H18.8643C21.4658 15.2002 23.2324 13.6006 23.2324 11.4033C23.2324 9.58398 22.1689 7.98438 20.4199 7.27246C20.4287 3.2998 17.5635 0.43457 13.8809 0.43457C11.543 0.43457 9.78516 1.63867 8.68652 3.22949C6.58594 2.71094 4.10742 4.30176 4.01953 6.78027C2.00684 7.13184 0.767578 8.90723 0.767578 10.9902C0.767578 13.3193 2.70996 15.2002 5.63672 15.2002ZM12 20.5791C12.2109 20.5791 12.4131 20.5088 12.624 20.2979L15.5771 17.4502C15.7266 17.3008 15.8057 17.1426 15.8057 16.9316C15.8057 16.5186 15.4717 16.2197 15.0674 16.2197C14.8652 16.2197 14.6631 16.3076 14.5225 16.4658L13.3008 17.7666L12.7295 18.417L12.8174 17.1162V15.0859C12.8174 14.6465 12.4482 14.2773 12 14.2773C11.543 14.2773 11.1738 14.6465 11.1738 15.0859V17.1162L11.2617 18.417L10.6992 17.7666L9.46875 16.4658C9.32812 16.3076 9.11719 16.2197 8.91504 16.2197C8.51074 16.2197 8.18555 16.5186 8.18555 16.9316C8.18555 17.1426 8.27344 17.3008 8.42285 17.4502L11.376 20.2979C11.5869 20.5088 11.7803 20.5791 12 20.5791Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.3281 15.6592C21.0703 15.6592 23.2324 13.6465 23.2324 11.1328C23.2324 9.26074 22.1689 7.59961 20.4199 6.87012C20.4287 2.89746 17.5635 0.0322266 13.8809 0.0322266C11.543 0.0322266 9.78516 1.23633 8.68652 2.83594C6.4541 2.23828 4.10742 3.89941 4.01953 6.37793C2.00684 6.73828 0.767578 8.54004 0.767578 10.7461C0.767578 13.418 3.10547 15.6504 6.18164 15.6504L18.3281 15.6592ZM18.3281 13.9014H6.19043C4.09863 13.9014 2.54297 12.4424 2.54297 10.7461C2.54297 8.98828 3.62402 7.6875 5.41699 7.6875C5.54883 7.6875 5.60156 7.61719 5.59277 7.49414C5.54004 4.88379 7.41211 3.9873 9.30176 4.58496C9.41602 4.62012 9.48633 4.59375 9.53906 4.49707C10.4092 2.96777 11.6924 1.78125 13.8721 1.78125C16.6318 1.78125 18.6006 3.96973 18.7324 6.52734C18.7588 7.00195 18.7236 7.51172 18.6885 7.93359C18.6709 8.05664 18.7236 8.12695 18.8379 8.14453C20.4287 8.45215 21.457 9.57715 21.457 11.1328C21.457 12.6709 20.0947 13.9014 18.3281 13.9014ZM12.0088 9.4541C12.4658 9.4541 12.7207 9.19922 12.7471 8.70703L12.8701 5.75391C12.8965 5.24414 12.5098 4.88379 12 4.88379C11.4814 4.88379 11.1123 5.23535 11.1387 5.75391L11.2529 8.71582C11.2793 9.19043 11.5342 9.4541 12.0088 9.4541ZM12 12.2402C12.5537 12.2402 12.9932 11.8447 12.9932 11.3174C12.9932 10.7725 12.5625 10.3857 12 10.3857C11.4375 10.3857 11.0068 10.7812 11.0068 11.3174C11.0068 11.8447 11.4463 12.2402 12 12.2402Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.5674 20.4658C23.7207 19.9736 25.2324 18.2334 25.2324 16.1328C25.2324 14.2607 24.1689 12.5996 22.4199 11.8701C22.4287 7.89746 19.5635 5.03223 15.8809 5.03223C13.543 5.03223 11.7852 6.23633 10.6865 7.83594C10.124 7.67773 9.50879 7.67773 8.9375 7.83594L21.5674 20.4658ZM20.3369 22.7773C20.6182 23.0498 21.0576 23.0498 21.3301 22.7773C21.5938 22.5049 21.6025 22.0566 21.3301 21.7842L6.52051 6.9834C6.23926 6.70215 5.78223 6.71973 5.51855 6.9834C5.25488 7.24707 5.26367 7.71289 5.51855 7.96777L20.3369 22.7773ZM8.18164 20.6592H16.6631L6.24805 10.2705C6.11621 10.6045 6.03711 10.9736 6.01953 11.3779C4.00684 11.7383 2.76758 13.54 2.76758 15.7461C2.76758 18.418 5.10547 20.6592 8.18164 20.6592Z" />
</svg>

After

Width:  |  Height:  |  Size: 808 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.3281 15.6504C21.0703 15.6504 23.2324 13.6377 23.2324 11.124C23.2324 9.26074 22.1689 7.59961 20.4199 6.87012C20.4287 2.88867 17.5635 0.0234375 13.8809 0.0234375C11.543 0.0234375 9.78516 1.23633 8.68652 2.82715C6.4541 2.22949 4.10742 3.89062 4.01953 6.36914C2.00684 6.72949 0.767578 8.54004 0.767578 10.7373C0.767578 13.418 3.10547 15.6504 6.18164 15.6504H18.3281Z" />
</svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1,3 @@
<svg width="8" height="10" viewBox="0 0 8 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.37245 10H6.29172C6.67206 10 6.96987 9.88698 7.18515 9.66093C7.40044 9.43488 7.50808 9.09581 7.50808 8.64371V3.84285C7.50808 3.39074 7.39416 3.05257 7.16632 2.82831C6.93847 2.60406 6.59491 2.49193 6.13564 2.49193H1.37245C0.916756 2.49193 0.574092 2.60406 0.344454 2.82831C0.114818 3.05257 0 3.39074 0 3.84285V8.64371C0 9.09581 0.114818 9.43488 0.344454 9.66093C0.574092 9.88698 0.916756 10 1.37245 10ZM0.963407 1.83531H6.54467C6.50521 1.61644 6.42806 1.44959 6.31324 1.33477C6.19842 1.21995 6.02081 1.16254 5.78041 1.16254H1.72767C1.48727 1.16254 1.30966 1.21995 1.19484 1.33477C1.08002 1.44959 1.00288 1.61644 0.963407 1.83531ZM1.72228 0.613563H5.7858C5.77144 0.409042 5.70596 0.255651 5.58934 0.153391C5.47273 0.0511304 5.3032 0 5.08074 0H2.42734C2.20488 0 2.03535 0.0511304 1.91874 0.153391C1.80212 0.255651 1.73663 0.409042 1.72228 0.613563Z" />
</svg>

After

Width:  |  Height:  |  Size: 962 B

View File

@@ -0,0 +1,10 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3_26)">
<path d="M0.915182 29.5759C1.46764 29.5759 1.94476 29.4127 2.48466 29.0988L15.3544 21.5402C16.0951 21.1007 16.5597 20.5859 16.7104 19.9707V27.2154C16.7104 28.7347 17.6395 29.5759 18.7444 29.5759C19.2969 29.5759 19.774 29.4127 20.3139 29.0988L33.1836 21.5402C33.9369 21.1007 34.389 20.5859 34.5522 19.9707V28.1445C34.5522 29.3373 35.1549 29.9526 36.3602 29.9526H39.3108C40.5036 29.9526 41.1189 29.3373 41.1189 28.1445V10.8677C41.1189 9.64983 40.5036 9.05971 39.3108 9.05971H36.3602C35.1674 9.05971 34.5522 9.67494 34.5522 10.8677V18.9537C34.389 18.3384 33.9369 17.8488 33.1836 17.3968L20.3139 9.83817C19.774 9.52427 19.2969 9.36105 18.7444 9.36105C17.6395 9.36105 16.7104 10.1897 16.7104 11.7215V18.9537C16.5597 18.3384 16.1077 17.8488 15.3544 17.3968L2.48466 9.83817C1.9322 9.52427 1.46764 9.36105 0.902626 9.36105C-0.202285 9.36105 -1.11886 10.1897 -1.11886 11.7215V27.2154C-1.11886 28.7347 -0.189729 29.5759 0.915182 29.5759Z"/>
</g>
<defs>
<clipPath id="clip0_3_26">
<rect width="40" height="40" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 558 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.33789 6.74023H17.6621C18.5498 6.74023 19.3496 6.94238 20.0439 7.30273L17.873 2.21387C17.293 0.851562 16.1152 0.0957031 14.542 0.0957031H7.45801C5.87598 0.0957031 4.70703 0.851562 4.12695 2.21387L1.94727 7.29395C2.6416 6.94238 3.4502 6.74023 4.33789 6.74023ZM4.33789 15.2305H17.6621C19.8066 15.2305 21.2832 13.7627 21.2832 11.6357C21.2832 9.5 19.8066 8.03223 17.6621 8.03223H4.33789C2.18457 8.03223 0.708008 9.5 0.708008 11.6357C0.708008 13.7627 2.18457 15.2305 4.33789 15.2305ZM16.4316 11.6357C16.4316 11.0029 16.9678 10.4668 17.6094 10.4668C18.2422 10.4668 18.7783 11.0029 18.7783 11.6357C18.7783 12.2773 18.2422 12.7959 17.6094 12.7959C16.9678 12.8047 16.4316 12.2861 16.4316 11.6357Z" />
</svg>

After

Width:  |  Height:  |  Size: 806 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.50859 13.0172C7.82354 13.0172 9.05579 12.6202 10.0813 11.9421L13.7036 15.5727C13.9434 15.8042 14.2494 15.92 14.5802 15.92C15.2667 15.92 15.7711 15.3824 15.7711 14.7043C15.7711 14.39 15.6636 14.084 15.4321 13.8525L11.8346 10.2384C12.5789 9.17984 13.0172 7.89797 13.0172 6.50859C13.0172 2.92763 10.0896 0 6.50859 0C2.9359 0 0 2.92763 0 6.50859C0 10.0896 2.92763 13.0172 6.50859 13.0172ZM6.50859 11.2805C3.88696 11.2805 1.73673 9.13022 1.73673 6.50859C1.73673 3.88696 3.88696 1.73673 6.50859 1.73673C9.13022 1.73673 11.2805 3.88696 11.2805 6.50859C11.2805 9.13022 9.13022 11.2805 6.50859 11.2805Z" />
</svg>

After

Width:  |  Height:  |  Size: 713 B

View File

@@ -0,0 +1,3 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.03813 10C5.17148 10 5.28504 9.95416 5.3788 9.86248C5.47256 9.77081 5.51944 9.65621 5.51944 9.51869V5.90574L6.96337 4.56808C7.54261 4.63475 8.08226 4.42014 8.58233 3.92424L5.85698 1.19265C5.61112 1.43851 5.43506 1.7 5.32879 1.97712C5.22253 2.25424 5.18606 2.5324 5.2194 2.81161L0.662578 7.70597C0.570901 7.81015 0.518811 7.92891 0.506309 8.06226C0.493814 8.19561 0.543822 8.31854 0.656331 8.43105L0.050007 9.23115C0.0166691 9.27699 0 9.33012 0 9.39055C0 9.45097 0.0250033 9.50827 0.0750098 9.56245L0.218772 9.70621C0.268783 9.75205 0.324001 9.77706 0.384425 9.78122C0.444848 9.78539 0.500061 9.76664 0.550065 9.72497L1.35016 9.11864C1.45852 9.23115 1.58041 9.28116 1.71584 9.26866C1.85127 9.25616 1.969 9.20198 2.06901 9.10614L4.55057 6.80585V9.51869C4.55057 9.65621 4.59745 9.77081 4.69122 9.86248C4.78498 9.95416 4.90061 10 5.03813 10ZM1.25641 8.14351L5.52569 3.6242C5.5632 3.68671 5.60591 3.74713 5.65383 3.80547C5.70175 3.86381 5.7528 3.92007 5.80698 3.97425C5.85698 4.02842 5.91115 4.07946 5.9695 4.12739C6.02784 4.17531 6.0841 4.21802 6.13827 4.25552L1.6377 8.52482L1.25641 8.14351ZM6.39455 0.648827L9.11989 3.38042C9.40326 3.10122 9.59495 2.79909 9.69497 2.47405C9.79498 2.14901 9.80227 1.82397 9.71684 1.49893C9.63142 1.17389 9.4491 0.875935 9.1699 0.60507C8.89487 0.33004 8.59691 0.14877 8.27604 0.0612579C7.95516 -0.0262539 7.6322 -0.0200025 7.30716 0.0800122C6.98212 0.180018 6.67792 0.369624 6.39455 0.648827Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 684 B

View File

@@ -0,0 +1,3 @@
<svg width="7" height="10" viewBox="0 0 7 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.11802 2.2789V0.406546C6.11802 0.268775 6.07001 0.161274 5.974 0.084044C5.87798 0.00681376 5.76317 -0.0171898 5.62958 0.0120333L3.04962 0.575614C2.70312 0.650758 2.52987 0.83236 2.52987 1.12042V6.63102C2.55909 6.8648 2.46725 7.00048 2.25434 7.03805L1.47158 7.20087C0.97062 7.30941 0.600115 7.48996 0.360063 7.74253C0.120021 7.9951 0 8.31133 0 8.69123C0 9.0753 0.13359 9.38945 0.400769 9.63367C0.667949 9.87789 1.01027 10 1.42774 10C1.66988 10 1.93393 9.93425 2.2199 9.80275C2.50586 9.67124 2.75113 9.45938 2.95569 9.16715C3.16025 8.87492 3.26253 8.48667 3.26253 8.00241V3.46868C3.26253 3.34344 3.27819 3.26204 3.3095 3.22446C3.3408 3.18689 3.41282 3.15767 3.52554 3.13679L5.83623 2.6233C5.9239 2.60661 5.99278 2.56695 6.04288 2.50433C6.09298 2.44171 6.11802 2.36657 6.11802 2.2789Z" />
</svg>

After

Width:  |  Height:  |  Size: 898 B

View File

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 714 B

View File

Before

Width:  |  Height:  |  Size: 444 B

After

Width:  |  Height:  |  Size: 444 B

View File

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 666 B

View File

Before

Width:  |  Height:  |  Size: 732 B

After

Width:  |  Height:  |  Size: 732 B

View File

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

View File

Before

Width:  |  Height:  |  Size: 758 B

After

Width:  |  Height:  |  Size: 758 B

View File

Before

Width:  |  Height:  |  Size: 848 B

After

Width:  |  Height:  |  Size: 848 B

View File

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 717 B

View File

Before

Width:  |  Height:  |  Size: 695 B

After

Width:  |  Height:  |  Size: 695 B

View File

Before

Width:  |  Height:  |  Size: 964 B

After

Width:  |  Height:  |  Size: 964 B

View File

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.84277 22.4785H18.166C19.3701 22.4785 20.0732 21.8369 20.126 20.6416L20.6797 7.94141H21.8926C22.3408 7.94141 22.6836 7.58984 22.6836 7.15039C22.6836 6.71094 22.332 6.37695 21.8926 6.37695H18.0781V5.05859C18.0781 3.65234 17.1729 2.81738 15.6611 2.81738H12.3213C10.8096 2.81738 9.9043 3.65234 9.9043 5.05859V6.37695H6.10742C5.66797 6.37695 5.31641 6.71973 5.31641 7.15039C5.31641 7.59863 5.66797 7.94141 6.10742 7.94141H7.3291L7.8916 20.6416C7.93555 21.8369 8.63867 22.4785 9.84277 22.4785ZM11.7324 5.1377C11.7324 4.74219 12.0049 4.4873 12.4443 4.4873H15.5469C15.9863 4.4873 16.2588 4.74219 16.2588 5.1377V6.37695H11.7324V5.1377ZM11.1787 19.7803C10.8271 19.7803 10.5811 19.5518 10.5723 19.2002L10.3086 9.86621C10.2998 9.51465 10.5459 9.27734 10.915 9.27734C11.2666 9.27734 11.5127 9.50586 11.5215 9.85742L11.7852 19.1914C11.8027 19.543 11.5566 19.7803 11.1787 19.7803ZM14 19.7803C13.6309 19.7803 13.3848 19.5518 13.3848 19.2002V9.85742C13.3848 9.51465 13.6309 9.27734 14 9.27734C14.3691 9.27734 14.624 9.51465 14.624 9.85742V19.2002C14.624 19.5518 14.3691 19.7803 14 19.7803ZM16.8213 19.7891C16.4434 19.7891 16.1973 19.543 16.2148 19.2002L16.4785 9.85742C16.4873 9.50586 16.7334 9.27734 17.085 9.27734C17.4541 9.27734 17.7002 9.51465 17.6914 9.86621L17.4277 19.2002C17.4189 19.5518 17.1729 19.7891 16.8213 19.7891Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.18849 19.0509C6.81935 19.4201 6.80177 20.0792 7.19728 20.466C7.58399 20.8527 8.24317 20.8439 8.61231 20.4747L13.9912 15.087L19.3789 20.4747C19.7568 20.8527 20.4072 20.8527 20.794 20.466C21.1719 20.0704 21.1807 19.4288 20.794 19.0509L15.415 13.6632L20.794 8.28431C21.1807 7.90638 21.1807 7.25599 20.794 6.86927C20.3984 6.49134 19.7568 6.48255 19.3789 6.86048L13.9912 12.2482L8.61231 6.86048C8.24317 6.49134 7.5752 6.47376 7.19728 6.86927C6.81056 7.25599 6.81935 7.91517 7.18849 8.28431L12.5762 13.6632L7.18849 19.0509Z" fill="#1C1C1E"/>
</svg>

After

Width:  |  Height:  |  Size: 651 B

View File

@@ -1,17 +1,34 @@
import React, { useEffect } from 'react';
import { Provider } from 'react-redux';
import TrackPlayer from 'react-native-track-player';
import TrackPlayer, { Capability } from 'react-native-track-player';
import { PersistGate } from 'redux-persist/integration/react';
import Routes from '../screens';
import store, { persistedStore } from 'store';
import {
NavigationContainer,
DefaultTheme,
DarkTheme,
DarkTheme as BaseDarkTheme,
} from '@react-navigation/native';
import { useColorScheme } from 'react-native';
import { ColorSchemeContext, themes } from './Colors';
import ErrorReportingAlert from 'utility/ErrorReportingAlert';
import DownloadManager from './DownloadManager';
// import ErrorReportingAlert from 'utility/ErrorReportingAlert';
const LightTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: themes.light.view.backgroundColor,
}
};
const DarkTheme = {
...BaseDarkTheme,
colors: {
...BaseDarkTheme.colors,
background: themes.dark.view.backgroundColor,
}
};
export default function App(): JSX.Element {
const colorScheme = useColorScheme();
@@ -23,13 +40,14 @@ export default function App(): JSX.Element {
await TrackPlayer.setupPlayer();
await TrackPlayer.updateOptions({
capabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_SKIP_TO_NEXT,
TrackPlayer.CAPABILITY_SKIP_TO_PREVIOUS,
TrackPlayer.CAPABILITY_STOP,
TrackPlayer.CAPABILITY_SEEK_TO,
]
Capability.Play,
Capability.Pause,
Capability.SkipToNext,
Capability.SkipToPrevious,
Capability.Stop,
Capability.SeekTo,
],
stopWithApp: true
});
}
setupTrackPlayer();
@@ -39,8 +57,11 @@ export default function App(): JSX.Element {
<Provider store={store}>
<PersistGate loading={null} persistor={persistedStore}>
<ColorSchemeContext.Provider value={theme}>
<NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<NavigationContainer
theme={colorScheme === 'dark' ? DarkTheme : LightTheme}
>
<Routes />
<DownloadManager />
</NavigationContainer>
</ColorSchemeContext.Provider>
</PersistGate>

View File

@@ -1,46 +1,51 @@
import React, { useCallback, useState } from 'react';
import { SvgProps } from 'react-native-svg';
import {
PressableProps, ViewProps,
PressableProps, ViewProps, View,
} from 'react-native';
import { THEME_COLOR } from 'CONSTANTS';
import styled, { css } from 'styled-components/native';
import useDefaultStyles from './Colors';
type ButtonSize = 'default' | 'small';
interface ButtonProps extends PressableProps {
icon?: React.FC<SvgProps>;
title: string;
title?: string;
style?: ViewProps['style'];
size?: ButtonSize;
}
interface PressableStyleProps {
active: boolean;
}
const BaseButton = styled.Pressable<PressableStyleProps>`
padding: 16px;
const BaseButton = styled.Pressable<{ size: ButtonSize }>`
padding: 12px;
border-radius: 8px;
flex-direction: row;
align-items: center;
justify-content: center;
flex-grow: 1;
${props => props.active && css`
background-color: ${THEME_COLOR};
${(props) => props.disabled && css`
opacity: 0.25;
`}
${(props) => props.size === 'small' && css`
flex-grow: 0;
padding: 10px;
`}
`;
const ButtonText = styled.Text<{ active?: boolean }>`
const ButtonText = styled.Text<{ active?: boolean, size: ButtonSize }>`
color: ${THEME_COLOR};
font-weight: 600;
font-weight: 500;
font-size: 14px;
${props => props.active && css`
color: white;
${(props) => props.size === 'small' && css`
font-size: 12px;
`}
`;
export default function Button(props: ButtonProps) {
const { icon: Icon, title, ...rest } = props;
const Button = React.forwardRef<View, ButtonProps>(function Button(props, ref) {
const { icon: Icon, title, disabled, size = 'default', ...rest } = props;
const defaultStyles = useDefaultStyles();
const [isPressed, setPressed] = useState(false);
const handlePressIn = useCallback(() => setPressed(true), []);
@@ -49,22 +54,34 @@ export default function Button(props: ButtonProps) {
return (
<BaseButton
{...rest}
disabled={disabled}
ref={ref}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
active={isPressed}
style={[ defaultStyles.button, props.style ]}
style={[
props.style,
{ backgroundColor: isPressed
? defaultStyles.activeBackground.backgroundColor
: defaultStyles.button.backgroundColor
}
]}
size={size}
>
{Icon &&
<Icon
width={12}
height={12}
fill={isPressed ? '#fff' : THEME_COLOR}
width={14}
height={14}
fill={THEME_COLOR}
style={{
marginRight: 8,
marginRight: title ? 8 : 0,
}}
/>
}
<ButtonText active={isPressed}>{title}</ButtonText>
{title ? (
<ButtonText active={isPressed} size={size}>{title}</ButtonText>
) : undefined}
</BaseButton>
);
}
});
export default Button;

View File

@@ -1,7 +1,10 @@
import { BlurView, BlurViewProperties } from '@react-native-community/blur';
import { THEME_COLOR } from 'CONSTANTS';
import React from 'react';
import React, { PropsWithChildren } from 'react';
import { useContext } from 'react';
import { ColorSchemeName, StyleSheet } from 'react-native';
import { ColorSchemeName, Platform, StyleSheet, useColorScheme, View } from 'react-native';
const majorPlatformVersion = typeof Platform.Version === 'string' ? parseInt(Platform.Version, 10) : Platform.Version;
/**
* Function for generating both the dark and light stylesheets, so that they
@@ -11,42 +14,57 @@ function generateStyles(scheme: ColorSchemeName) {
return StyleSheet.create({
text: {
color: scheme === 'dark' ? '#fff' : '#000',
fontSize: 14,
fontFamily: 'Inter',
},
textHalfOpacity: {
color: scheme === 'dark' ? '#ffffff88' : '#00000088',
fontSize: 14,
// fontFamily: 'Inter',
},
textQuarterOpacity: {
color: scheme === 'dark' ? '#ffffff44' : '#00000044',
fontSize: 14,
},
view: {
backgroundColor: scheme === 'dark' ? '#111' : '#f6f6f6',
backgroundColor: scheme === 'dark' ? '#111' : '#fff',
},
border: {
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
},
activeBackground: {
backgroundColor: `${THEME_COLOR}${scheme === 'dark' ? '66' : '16'}`,
backgroundColor: `${THEME_COLOR}${scheme === 'dark' ? '26' : '16'}`,
},
imageBackground: {
backgroundColor: scheme === 'dark' ? '#333' : '#ddd',
backgroundColor: scheme === 'dark' ? '#191919' : '#eee',
borderWidth: 0.5,
borderColor: scheme === 'dark' ? '#262626' : '#ddd',
},
modal: {
backgroundColor: scheme === 'dark' ? '#222222ee' : '#eeeeeeee',
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
},
modalInner: {
backgroundColor: scheme === 'dark' ? '#000' : '#fff',
},
button: {
backgroundColor: scheme === 'dark' ? '#161616' : '#e6e6e6',
backgroundColor: scheme === 'dark' ? '#ffffff09' : '#00000009',
},
input: {
backgroundColor: scheme === 'dark' ? '#161616' : '#e6e6e6',
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
color: scheme === 'dark' ? '#fff' : '#000',
},
sectionHeading: {
backgroundColor: scheme === 'dark' ? '#111' : '#eee',
borderColor: scheme === 'dark' ? '#333' : '#ddd',
},
stackHeader: {
color: scheme === 'dark' ? 'white' : 'black'
}
},
icon: {
color: scheme === 'dark' ? '#ffffff4d' : '#0000004d',
},
divider: {
backgroundColor: scheme === 'dark' ? '#333' : '#eee',
},
filter: {
backgroundColor: scheme === 'dark' ? '#191919' : '#f3f3f3',
},
});
}
@@ -77,4 +95,21 @@ export function DefaultStylesProvider(props: DefaultStylesProviderProps) {
const defaultStyles = useDefaultStyles();
return props.children(defaultStyles);
}
export function ColoredBlurView(props: PropsWithChildren<BlurViewProperties>) {
const scheme = useColorScheme();
return Platform.OS === 'ios' ? (
<BlurView
{...props}
blurType={Platform.OS === 'ios' && majorPlatformVersion >= 13
? 'material'
: scheme === 'dark' ? 'extraDark' : 'xlight'
} />
) : (
<View {...props} style={[ props.style, {
backgroundColor: scheme === 'light' ? '#f6f6f6f6' : '#333333f6',
} ]} />
);
}

View File

@@ -0,0 +1,85 @@
import React, { useMemo } from 'react';
import { Dimensions, ViewProps } from 'react-native';
import { Canvas, Blur, Image as SkiaImage, useImage, Offset, Mask, RoundedRect, Shadow } from '@shopify/react-native-skia';
import useDefaultStyles from './Colors';
import styled from 'styled-components/native';
const Screen = Dimensions.get('screen');
const Container = styled.View<{ size: number }>`
width: ${(props) => props.size}px;
height: ${(props) => props.size}px;
position: relative;
`;
const BlurContainer = styled(Canvas)<{ size: number, offset: number }>`
position: absolute;
left: -${(props) => props.offset}px;
top: -${(props) => props.offset}px;
width: ${(props) => props.size}px;
height: ${(props) => props.size}px;
z-index: 2;
`;
interface Props {
blurRadius?: number;
opacity?: number;
margin?: number;
radius?: number;
style?: ViewProps['style'];
src: string;
}
/**
* This will take a cover image, and apply shadows and a really nice background
* blur to the image in question. Additionally, we'll add some margin and radius
* to the corners.
*/
function CoverImage({
blurRadius = 256,
opacity = 0.85,
margin = 112,
radius = 12,
style,
src,
}: Props) {
const defaultStyles = useDefaultStyles();
const image = useImage(src || '');
const { canvasSize, imageSize } = useMemo(() => {
const imageSize = Screen.width - margin;
const canvasSize = imageSize + blurRadius * 2;
return { imageSize, canvasSize };
}, [blurRadius, margin]);
return (
<Container size={imageSize} style={style}>
<BlurContainer size={canvasSize} offset={blurRadius}>
<RoundedRect x={blurRadius} y={blurRadius} width={imageSize} height={imageSize} color={defaultStyles.imageBackground.backgroundColor} r={12}>
<Shadow dx={0} dy={1} blur={2} color="#0000000d" />
<Shadow dx={0} dy={2} blur={4} color="#0000000d" />
<Shadow dx={0} dy={4} blur={8} color="#0000000d" />
<Shadow dx={0} dy={8} blur={16} color="#0000000d" />
<Shadow dx={0} dy={16} blur={32} color="#0000000d" />
</RoundedRect>
{image ? (
<>
<SkiaImage image={image} width={imageSize} height={imageSize} opacity={opacity}>
<Offset x={blurRadius} y={blurRadius} />
<Blur blur={blurRadius / 2} />
</SkiaImage>
<Mask mask={<RoundedRect width={imageSize} height={imageSize} x={blurRadius} y={blurRadius} r={radius} />}>
{image ? (
<SkiaImage image={image} width={imageSize} height={imageSize}>
<Offset x={blurRadius} y={blurRadius} />
</SkiaImage>
) : null}
</Mask>
</>
) : null}
</BlurContainer>
</Container>
);
}
export default CoverImage;

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { ViewProps } from 'react-native';
import styled from 'styled-components/native';
import useDefaultStyles from './Colors';
const Container = styled.View`
height: 1px;
flex: 1;
`;
function Divider({ style }: ViewProps) {
const defaultStyles = useDefaultStyles();
return (
<Container style={[defaultStyles.divider, style]} />
);
}
export default Divider;

View File

@@ -0,0 +1,115 @@
import React, { useEffect, useMemo, useRef } from 'react';
import { useTypedSelector } from 'store';
import CloudIcon from 'assets/icons/cloud.svg';
import CloudDownArrow from 'assets/icons/cloud-down-arrow.svg';
import CloudExclamationMarkIcon from 'assets/icons/cloud-exclamation-mark.svg';
import InternalDriveIcon from 'assets/icons/internal-drive.svg';
import useDefaultStyles from './Colors';
import { EntityId } from '@reduxjs/toolkit';
import Svg, { Circle, CircleProps } from 'react-native-svg';
import { Animated, Easing } from 'react-native';
import styled from 'styled-components/native';
interface DownloadIconProps {
trackId: EntityId;
size?: number;
fill?: string;
}
const DownloadContainer = styled.View`
position: relative;
`;
const IconOverlay = styled.View`
position: absolute;
top: 0;
left: 0;
transform: scale(0.5);
`;
function DownloadIcon({ trackId, size = 16, fill }: DownloadIconProps) {
// determine styles
const defaultStyles = useDefaultStyles();
const iconFill = fill || defaultStyles.textQuarterOpacity.color;
// Get download icon from state
const entity = useTypedSelector((state) => state.downloads.entities[trackId]);
const isQueued = useTypedSelector((state) => state.downloads.queued.includes(trackId));
// Memoize calculations for radius and circumference of the circle
const radius = useMemo(() => size / 2, [size]);
const circumference = useMemo(() => radius * 2 * Math.PI, [radius]);
// Initialize refs for the circle and the animated value
const circleRef = useRef<Circle>(null);
const offsetAnimation = useRef(new Animated.Value(entity?.progress || 0)).current;
// Whenever the progress changes, trigger the animation
useEffect(() => {
Animated.timing(offsetAnimation, {
toValue: (circumference * (1 - (entity?.progress || 0))),
duration: 250,
useNativeDriver: false,
easing: Easing.ease,
}).start();
}, [entity?.progress, offsetAnimation, circumference]);
// On mount, subscribe to changes in the animation value and then
// apply them to the circle using native props
useEffect(() => {
const subscription = offsetAnimation.addListener((offset) => {
// @ts-expect-error undocumented functionality
const setNativeProps = circleRef.current?.setNativeProps as (props: CircleProps) => void | undefined;
setNativeProps?.({ strokeDashoffset: offset.value });
});
return () => offsetAnimation.removeListener(subscription);
}, [offsetAnimation]);
if (!entity && !isQueued) {
return (
<CloudIcon width={size} height={size} fill={iconFill} />
);
}
if (entity?.isComplete) {
return (
<InternalDriveIcon width={size} height={size} fill={iconFill} />
);
}
if (entity?.isFailed) {
return (
<CloudExclamationMarkIcon width={size} height={size} fill={iconFill} />
);
}
if (isQueued || (!entity?.isFailed && !entity?.isComplete)) {
return (
<DownloadContainer>
<Svg width={size} height={size} transform={[{ rotate: '-90deg' }]}>
<Circle
cx={radius}
cy={radius}
r={radius - 1}
stroke={iconFill}
// @ts-expect-error react-native-svg has outdated react-native typings
ref={circleRef}
strokeWidth={1.5}
strokeDasharray={[ circumference, circumference ]}
strokeDashoffset={circumference}
strokeLinecap='round'
fill='transparent'
/>
</Svg>
<IconOverlay>
<CloudDownArrow width={size} height={size} fill={iconFill} />
</IconOverlay>
</DownloadContainer>
);
}
return null;
}
export default DownloadIcon;

View File

@@ -0,0 +1,106 @@
import { EntityId } from '@reduxjs/toolkit';
import { xor } from 'lodash';
import { useEffect, useRef, useState } from 'react';
import { DocumentDirectoryPath, readDir } from 'react-native-fs';
import { useAppDispatch, useTypedSelector } from 'store';
import { completeDownload, downloadTrack } from 'store/downloads/actions';
/**
* The maximum number of concurrent downloads we allow to take place at once.
* This is hardcoded at 5 for now, but might be extracted to a setting later.
*/
const MAX_CONCURRENT_DOWNLOADS = 5;
/**
* This is a component that tracks queued downloads, and starts them one-by-one,
* so that we don't overload react-native-fs, as well as the render performance.
*/
function DownloadManager () {
// Retrieve store helpers
const { queued, ids } = useTypedSelector((state) => state.downloads);
const rehydrated = useTypedSelector((state) => state._persist.rehydrated);
const dispatch = useAppDispatch();
// Keep state for the currently active downloads (i.e. the downloads that
// have actually been pushed out to react-native-fs).
const [hasRehydratedOrphans, setHasRehydratedOrphans] = useState(false);
const activeDownloads = useRef(new Set<EntityId>());
useEffect(() => {
// GUARD: Check if the queue is empty
if (!queued.length) {
// If so, clear any current downloads
activeDownloads.current.clear();
return;
}
// Apparently, the queue has changed, and we need to manage
// First, we pick the first n downloads
const queue = queued.slice(0, MAX_CONCURRENT_DOWNLOADS);
// We then filter for new downloads
queue.filter((id) => !activeDownloads.current.has(id))
.forEach((id) => {
// We dispatch the actual call to start downloading
dispatch(downloadTrack(id));
// And add it to the active downloads
activeDownloads.current.add(id);
});
// Lastly, if something isn't part of the queue, but is of active
// downloads, we can assume the download completed.
xor(Array.from(activeDownloads.current), queue)
.forEach((id) => activeDownloads.current.delete(id));
}, [queued, dispatch, activeDownloads]);
useEffect(() => {
// GUARD: We only run this functino once
if (hasRehydratedOrphans) {
return;
}
// GUARD: If the state has not been rehydrated, we cannot check against
// the store ids.
if (!rehydrated) {
return;
}
/**
* Whenever the store is cleared, existing downloads get "lost" because
* the only reference we have is the store. This function checks for
* those lost downloads and adds them to the store
*/
async function hydrateOrphanedDownloads() {
// Retrieve all files for this app
const files = await readDir(DocumentDirectoryPath);
// Loop through the mp3 files
files.filter((file) => file.isFile() && file.name.endsWith('.mp3'))
.forEach((file) => {
const id = file.name.replace('.mp3', '');
// GUARD: If the id is already in the store, there's nothing
// left for us to do.
if (ids.includes(id)) {
return;
}
// Add the download to the store
dispatch(completeDownload({
id,
location: file.path,
size: file.size,
}));
});
}
hydrateOrphanedDownloads();
setHasRehydratedOrphans(true);
}, [rehydrated, ids, hasRehydratedOrphans, dispatch]);
return null;
}
export default DownloadManager;

View File

@@ -1,9 +1,13 @@
import styled from 'styled-components/native';
import styled, { css } from 'styled-components/native';
const Input = styled.TextInput`
const Input = styled.TextInput<{ icon?: boolean }>`
margin: 10px 0;
border-radius: 8px;
padding: 15px;
${(props) => props.icon && css`
padding-left: 40px;
`}
`;
export default Input;

View File

@@ -1,30 +1,24 @@
import React, { useCallback, useState } from 'react';
import { TouchableOpacityProps } from 'react-native';
import ChevronRight from 'assets/chevron-right.svg';
import styled, { css } from 'styled-components/native';
import ChevronRight from 'assets/icons/chevron-right.svg';
import styled from 'styled-components/native';
import { THEME_COLOR } from 'CONSTANTS';
import useDefaultStyles from './Colors';
const BUTTON_SIZE = 14;
const Container = styled.Pressable<{ active?: boolean }>`
padding: 18px 20px;
border-bottom-width: 1px;
padding: 14px 16px;
border-radius: 8px;
margin: 4px 8px;
flex-direction: row;
justify-content: space-between;
${props => props.active && css`
background-color: ${THEME_COLOR};
`}
align-items: center;
`;
const Label = styled.Text<{ active?: boolean }>`
color: ${THEME_COLOR};
font-size: 16px;
${props => props.active && css`
color: white;
`}
`;
const ListButton: React.FC<TouchableOpacityProps> = ({ children, ...props }) => {
@@ -38,11 +32,13 @@ const ListButton: React.FC<TouchableOpacityProps> = ({ children, ...props }) =>
{...props}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
style={defaultStyles.border}
active={isPressed}
style={[
defaultStyles.border,
isPressed ? defaultStyles.activeBackground : undefined
]}
>
<Label active={isPressed}>{children}</Label>
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={isPressed ? '#fff' : THEME_COLOR} />
<Label>{children}</Label>
<ChevronRight width={BUTTON_SIZE} height={BUTTON_SIZE} fill={THEME_COLOR} />
</Container>
);
};

View File

@@ -1,6 +1,5 @@
import React, { useCallback } from 'react';
import styled, { css } from 'styled-components/native';
import { SafeAreaView, Pressable } from 'react-native';
import { useNavigation, StackActions } from '@react-navigation/native';
import useDefaultStyles from './Colors';
@@ -8,21 +7,29 @@ interface Props {
fullSize?: boolean;
}
const Background = styled(Pressable)`
padding: 100px 25px;
const Background = styled.View`
flex: 1;
justify-content: center;
`;
const Container = styled(Pressable)<Pick<Props, 'fullSize'>>`
border-radius: 20px;
margin: auto 0;
const Container = styled.View<Pick<Props, 'fullSize'>>`
margin: auto 20px;
padding: 4px;
border-radius: 12px;
flex: 0 0 auto;
background: salmon;
${props => props.fullSize && css`
flex: 1;
margin: auto 0;
border-radius: 0px;
`}
`;
const Spacer = styled.Pressable`
flex: 1;
`;
const Modal: React.FC<Props> = ({ children, fullSize = true }) => {
const defaultStyles = useDefaultStyles();
const navigation = useNavigation();
@@ -31,12 +38,12 @@ const Modal: React.FC<Props> = ({ children, fullSize = true }) => {
}, [navigation]);
return (
<Background style={defaultStyles.modal} onPress={closeModal}>
<SafeAreaView style={{ flex: 1 }}>
<Container style={defaultStyles.modalInner} fullSize={fullSize}>
{children}
</Container>
</SafeAreaView>
<Background style={defaultStyles.modal}>
{!fullSize && <Spacer onPress={closeModal} />}
<Container style={defaultStyles.modalInner} fullSize={fullSize}>
{children}
</Container>
{!fullSize && <Spacer onPress={closeModal} />}
</Background>
);
};

View File

@@ -0,0 +1,52 @@
import { THEME_COLOR } from 'CONSTANTS';
import styled from 'styled-components/native';
import Animated from 'react-native-reanimated';
export function getSeconds(seconds: number): string {
'worklet';
return Math.floor(seconds % 60).toString().padStart(2, '0');
}
export function getMinutes(seconds: number): number {
'worklet';
return Math.floor(seconds / 60);
}
export function calculateProgressTranslation(
position: number,
reference: number,
width: number,
) {
'worklet';
const completion = position / reference;
const output = (1 - (completion || 0)) * -1 * width;
return output;
}
export const ProgressTrackContainer = styled.View`
overflow: hidden;
height: 5px;
flex: 1;
flex-direction: row;
align-items: center;
position: relative;
border-radius: 6px;
`;
export interface ProgressTrackProps {
opacity?: number;
stroke?: number;
}
const ProgressTrack = styled(Animated.View)<ProgressTrackProps>`
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: ${(props) => props.stroke ? props.stroke + 'px' : '100%'};
background-color: ${THEME_COLOR};
opacity: ${(props) => props.opacity || 1};
border-radius: 99px;
`;
export default ProgressTrack;

44
src/components/ReText.tsx Normal file
View File

@@ -0,0 +1,44 @@
import React from 'react';
import type { TextProps as RNTextProps } from 'react-native';
import { StyleSheet, TextInput } from 'react-native';
import Animated, { useAnimatedProps } from 'react-native-reanimated';
import useDefaultStyles from './Colors';
const styles = StyleSheet.create({
baseStyle: {
color: 'black',
},
});
Animated.addWhitelistedNativeProps({ text: true });
interface TextProps {
text: Animated.SharedValue<string>;
style?: Animated.AnimateProps<RNTextProps>['style'];
}
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
const ReText = (props: TextProps) => {
const { text, style } = { style: {}, ...props };
const defaultStyles = useDefaultStyles();
const animatedProps = useAnimatedProps(() => {
return {
text: text.value,
// Here we use any because the text prop is not available in the type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
});
return (
<AnimatedTextInput
underlineColorAndroid="transparent"
editable={false}
value={text.value}
style={[styles.baseStyle, defaultStyles.text, style]}
{...{ animatedProps }}
/>
);
};
export default ReText;

36
src/components/Shadow.tsx Normal file
View File

@@ -0,0 +1,36 @@
import React, { PropsWithChildren } from 'react';
import { StyleSheet, View } from 'react-native';
export const shadow = StyleSheet.create({
small: {
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 2.62,
elevation: 4,
},
medium: {
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 3,
},
shadowOpacity: 0.1,
shadowRadius: 4.65,
elevation: 6,
}
});
type SizeProp = 'small' | 'medium';
const shadowMap: Record<SizeProp, StyleSheet.NamedStyles<unknown>> = {
'small': shadow.small,
'medium': shadow.medium,
};
export const ShadowWrapper = ({ children, size = 'small' }: PropsWithChildren<{ size?: SizeProp }>) => (
<View style={shadowMap[size]}>{children}</View>
);

View File

@@ -1,11 +0,0 @@
import React, { PropsWithChildren } from 'react';
import { Text as BaseText, TextProps } from 'react-native';
import useDefaultStyles from './Colors';
export default function Text(props: PropsWithChildren<TextProps>) {
const defaultStyles = useDefaultStyles();
return (
<BaseText {...props} style={[defaultStyles.text, props.style]} />
);
}

View File

@@ -1,10 +1,10 @@
import React, { useCallback } from 'react';
import React, { PropsWithChildren, useCallback } from 'react';
import { Pressable, ViewStyle } from 'react-native';
interface TouchableHandlerProps {
id: string;
onPress: (id: string) => void;
onLongPress?: (id: string) => void;
interface TouchableHandlerProps<T = number> {
id: T;
onPress: (id: T) => void;
onLongPress?: (id: T) => void;
}
function TouchableStyles({ pressed }: { pressed: boolean }): ViewStyle {
@@ -19,7 +19,12 @@ function TouchableStyles({ pressed }: { pressed: boolean }): ViewStyle {
* 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, onLongPress, children }) => {
function TouchableHandler<T>({
id,
onPress,
onLongPress,
children
}: PropsWithChildren<TouchableHandlerProps<T>>): JSX.Element {
const handlePress = useCallback(() => {
return onPress(id);
}, [id, onPress]);
@@ -37,6 +42,6 @@ const TouchableHandler: React.FC<TouchableHandlerProps> = ({ id, onPress, onLon
{children}
</Pressable>
);
};
}
export default TouchableHandler;

View File

@@ -1,13 +0,0 @@
import styled from 'styled-components/native';
import Text from './Text';
export const Header = styled(Text)`
margin: 24px 0 12px 0;
font-size: 36px;
font-weight: bold;
`;
export const SubHeader = styled(Text)`
font-size: 24px;
margin: 12px 0;
`;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import styled from 'styled-components/native';
import { Text as BaseText, TextProps } from 'react-native';
import { PropsWithChildren } from 'react';
import useDefaultStyles from './Colors';
export function Text(props: PropsWithChildren<TextProps>) {
const defaultStyles = useDefaultStyles();
return (
<BaseText {...props} style={[defaultStyles.text, props.style]} />
);
}
export const Header = styled(Text)`
margin: 0 0 6px 0;
font-size: 28px;
font-weight: 400;
`;
export const SubHeader = styled(Text)`
font-size: 16px;
margin: 0 0 6px 0;
font-weight: 400;
opacity: 0.5;
`;

View File

@@ -0,0 +1,13 @@
import styled from 'styled-components/native';
import Button from './Button';
export const WrappableButtonRow = styled.View`
flex: 0 0 auto;
flex-direction: row;
flex-wrap: wrap;
margin: 24px -2px;
`;
export const WrappableButton = styled(Button)`
margin: 2px;
`;

View File

@@ -8,14 +8,21 @@ const localeGetters: Record<string, () => object> = {
fr: () => require('./lang/fr/locale.json'),
nl: () => require('./lang/nl/locale.json'),
es: () => require('./lang/es/locale.json'),
zh: () => require('./lang/zh/locale.json'),
ja: () => require('./lang/ja/locale.json'),
};
// Have RNLocalize pick the best locale from the languages on offer
const locale = findBestAvailableLanguage(Object.keys(localeGetters));
let locale = findBestAvailableLanguage(Object.keys(localeGetters));
// Check if the locale is correctly picked
if (!locale || !locale.languageTag) {
throw new Error('Invalid locale selected');
// Some users might not list English as a fallback language, and hence might not
// be assigned a locale. In this case, we'll just default to English.
locale = {
languageTag: 'en',
isRTL: false,
};
}
// Set the key-value pairs for the different languages you want to support.

Some files were not shown because too many files have changed in this diff Show More