feat: Create new progress slider from scratch

This commit is contained in:
Lei Nelissen
2022-05-05 03:30:51 +02:00
parent f48d248144
commit 6efc8e757c
14 changed files with 362 additions and 141 deletions

View File

@@ -135,7 +135,7 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig { defaultConfig {
applicationId "com.rndiffapp" applicationId "com.jellyfinaudioplayer"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 8 versionCode 8
@@ -300,6 +300,10 @@ if (isNewArchitectureEnabled()) {
substitute(module("com.facebook.react:react-native")) substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source") .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'
}
} }
} }

View File

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

View File

@@ -57,6 +57,7 @@ allprojects {
jcenter() { jcenter() {
content { content {
includeGroup("com.google.android.exoplayer") includeGroup("com.google.android.exoplayer")
includeGroupByRegex("com.eightbitlab.*")
} }
} }
} }

View File

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

View File

@@ -422,6 +422,33 @@ PODS:
- React-Core - React-Core
- RNLocalize (2.2.1): - RNLocalize (2.2.1):
- React-Core - React-Core
- RNReanimated (2.8.0):
- DoubleConversion
- FBLazyVector
- FBReactNativeSpec
- glog
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-callinvoker
- React-Core
- React-Core/DevSupport
- React-Core/RCTWebSocket
- React-CoreModules
- React-cxxreact
- React-jsi
- React-jsiexecutor
- React-jsinspector
- React-RCTActionSheet
- React-RCTAnimation
- React-RCTBlob
- React-RCTImage
- React-RCTLinking
- React-RCTNetwork
- React-RCTSettings
- React-RCTText
- ReactCommon/turbomodule/core
- Yoga
- RNScreens (3.13.1): - RNScreens (3.13.1):
- React-Core - React-Core
- React-RCTImage - React-RCTImage
@@ -516,6 +543,7 @@ DEPENDENCIES:
- RNFS (from `../node_modules/react-native-fs`) - RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNLocalize (from `../node_modules/react-native-localize`) - RNLocalize (from `../node_modules/react-native-localize`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`) - RNScreens (from `../node_modules/react-native-screens`)
- "RNSentry (from `../node_modules/@sentry/react-native`)" - "RNSentry (from `../node_modules/@sentry/react-native`)"
- RNSVG (from `../node_modules/react-native-svg`) - RNSVG (from `../node_modules/react-native-svg`)
@@ -637,6 +665,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-gesture-handler" :path: "../node_modules/react-native-gesture-handler"
RNLocalize: RNLocalize:
:path: "../node_modules/react-native-localize" :path: "../node_modules/react-native-localize"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens: RNScreens:
:path: "../node_modules/react-native-screens" :path: "../node_modules/react-native-screens"
RNSentry: RNSentry:
@@ -707,6 +737,7 @@ SPEC CHECKSUMS:
RNFS: fc610f78fdf8bfc89a9e5cc2f898519f4dba1002 RNFS: fc610f78fdf8bfc89a9e5cc2f898519f4dba1002
RNGestureHandler: 4f4986408310a43f1606c391f38f76e0d6e790d5 RNGestureHandler: 4f4986408310a43f1606c391f38f76e0d6e790d5
RNLocalize: cbcb55d0e19c78086ea4eea20e03fe8000bbbced RNLocalize: cbcb55d0e19c78086ea4eea20e03fe8000bbbced
RNReanimated: 64573e25e078ae6bec03b891586d50b9ec284393
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19 RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
RNSentry: 2cd1daa124b0d9fd0dfc2cb6094fdd168cb579bc RNSentry: 2cd1daa124b0d9fd0dfc2cb6094fdd168cb579bc
RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8 RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8

62
package-lock.json generated
View File

@@ -26,6 +26,7 @@
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"events": "^3.3.0", "events": "^3.3.0",
"fuse.js": "^6.6.0", "fuse.js": "^6.6.0",
"hermes-engine": "^0.11.0",
"i18n-js": "^3.9.2", "i18n-js": "^3.9.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
@@ -38,12 +39,13 @@
"react-native-fs": "^2.19.0", "react-native-fs": "^2.19.0",
"react-native-gesture-handler": "^2.4.1", "react-native-gesture-handler": "^2.4.1",
"react-native-localize": "^2.2.1", "react-native-localize": "^2.2.1",
"react-native-reanimated": "^2.8.0",
"react-native-safe-area-context": "^4.2.5", "react-native-safe-area-context": "^4.2.5",
"react-native-screens": "^3.13.1", "react-native-screens": "^3.13.1",
"react-native-shadow-2": "^6.0.5", "react-native-shadow-2": "^6.0.5",
"react-native-svg": "^12.3.0", "react-native-svg": "^12.3.0",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"react-native-track-player": "^2.1.3", "react-native-track-player": "^2.2.0-rc3",
"react-native-webview": "^11.18.2", "react-native-webview": "^11.18.2",
"react-redux": "^7.2.6", "react-redux": "^7.2.6",
"redux": "^4.2.0", "redux": "^4.2.0",
@@ -4911,8 +4913,7 @@
"node_modules/@types/invariant": { "node_modules/@types/invariant": {
"version": "2.2.35", "version": "2.2.35",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
"integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==", "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
"peer": true
}, },
"node_modules/@types/istanbul-lib-coverage": { "node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.3", "version": "2.0.3",
@@ -8772,6 +8773,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/hermes-engine": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
"integrity": "sha512-7aMUlZja2IyLYAcZ69NBnwJAR5ZOYlSllj0oMpx08a8HzxHOys0eKCzfphrf6D0vX1JGO1QQvVsQKe6TkYherw=="
},
"node_modules/hermes-estree": { "node_modules/hermes-estree": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.5.0.tgz", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.5.0.tgz",
@@ -12456,8 +12462,7 @@
"node_modules/lodash.isequal": { "node_modules/lodash.isequal": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
"peer": true
}, },
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
@@ -14427,7 +14432,6 @@
"version": "2.8.0", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.8.0.tgz", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.8.0.tgz",
"integrity": "sha512-kJvf/UWLBMaGCs9X66MKq5zdFMgwx8D0nHnolbHR7s8ZnbLdb7TlQ/yuzIXqn/9wABfnwtNRI3CyaP1aHWMmZg==", "integrity": "sha512-kJvf/UWLBMaGCs9X66MKq5zdFMgwx8D0nHnolbHR7s8ZnbLdb7TlQ/yuzIXqn/9wABfnwtNRI3CyaP1aHWMmZg==",
"peer": true,
"dependencies": { "dependencies": {
"@babel/plugin-transform-object-assign": "^7.16.7", "@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7", "@babel/preset-typescript": "^7.16.7",
@@ -14505,9 +14509,9 @@
} }
}, },
"node_modules/react-native-track-player": { "node_modules/react-native-track-player": {
"version": "2.1.3", "version": "2.2.0-rc3",
"resolved": "https://registry.npmjs.org/react-native-track-player/-/react-native-track-player-2.1.3.tgz", "resolved": "https://registry.npmjs.org/react-native-track-player/-/react-native-track-player-2.2.0-rc3.tgz",
"integrity": "sha512-JWKFRu+hr1ECN339RH+c+XDM7HfwvdvS4H1p4cJbhg/9b1CQGPJSrYXEhYkngN0msoxBxAjFyFIhjT2fWDCltA==", "integrity": "sha512-Pvmum3MQ5PE8/yOIIPsk00zZk3EzdocUuVUwuBKSCmdKK/3O9YhnZRC3EuT59XCDm23pZZJZDSR44VeXN6Gamg==",
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.6", "react": ">=16.8.6",
"react-native": ">=0.60.0-rc.2", "react-native": ">=0.60.0-rc.2",
@@ -14806,11 +14810,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-native/node_modules/hermes-engine": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
"integrity": "sha512-7aMUlZja2IyLYAcZ69NBnwJAR5ZOYlSllj0oMpx08a8HzxHOys0eKCzfphrf6D0vX1JGO1QQvVsQKe6TkYherw=="
},
"node_modules/react-native/node_modules/hermes-parser": { "node_modules/react-native/node_modules/hermes-parser": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.5.0.tgz", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.5.0.tgz",
@@ -15879,8 +15878,7 @@
"node_modules/setimmediate": { "node_modules/setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
"peer": true
}, },
"node_modules/setprototypeof": { "node_modules/setprototypeof": {
"version": "1.1.1", "version": "1.1.1",
@@ -16385,8 +16383,7 @@
"node_modules/string-hash-64": { "node_modules/string-hash-64": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz",
"integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==", "integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw=="
"peer": true
}, },
"node_modules/string-length": { "node_modules/string-length": {
"version": "4.0.2", "version": "4.0.2",
@@ -20895,8 +20892,7 @@
"@types/invariant": { "@types/invariant": {
"version": "2.2.35", "version": "2.2.35",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
"integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==", "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
"peer": true
}, },
"@types/istanbul-lib-coverage": { "@types/istanbul-lib-coverage": {
"version": "2.0.3", "version": "2.0.3",
@@ -23834,6 +23830,11 @@
} }
} }
}, },
"hermes-engine": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
"integrity": "sha512-7aMUlZja2IyLYAcZ69NBnwJAR5ZOYlSllj0oMpx08a8HzxHOys0eKCzfphrf6D0vX1JGO1QQvVsQKe6TkYherw=="
},
"hermes-estree": { "hermes-estree": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.5.0.tgz", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.5.0.tgz",
@@ -26684,8 +26685,7 @@
"lodash.isequal": { "lodash.isequal": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
"peer": true
}, },
"lodash.merge": { "lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
@@ -28377,11 +28377,6 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
"integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==" "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="
}, },
"hermes-engine": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
"integrity": "sha512-7aMUlZja2IyLYAcZ69NBnwJAR5ZOYlSllj0oMpx08a8HzxHOys0eKCzfphrf6D0vX1JGO1QQvVsQKe6TkYherw=="
},
"hermes-parser": { "hermes-parser": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.5.0.tgz", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.5.0.tgz",
@@ -28817,7 +28812,6 @@
"version": "2.8.0", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.8.0.tgz", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.8.0.tgz",
"integrity": "sha512-kJvf/UWLBMaGCs9X66MKq5zdFMgwx8D0nHnolbHR7s8ZnbLdb7TlQ/yuzIXqn/9wABfnwtNRI3CyaP1aHWMmZg==", "integrity": "sha512-kJvf/UWLBMaGCs9X66MKq5zdFMgwx8D0nHnolbHR7s8ZnbLdb7TlQ/yuzIXqn/9wABfnwtNRI3CyaP1aHWMmZg==",
"peer": true,
"requires": { "requires": {
"@babel/plugin-transform-object-assign": "^7.16.7", "@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7", "@babel/preset-typescript": "^7.16.7",
@@ -28871,9 +28865,9 @@
} }
}, },
"react-native-track-player": { "react-native-track-player": {
"version": "2.1.3", "version": "2.2.0-rc3",
"resolved": "https://registry.npmjs.org/react-native-track-player/-/react-native-track-player-2.1.3.tgz", "resolved": "https://registry.npmjs.org/react-native-track-player/-/react-native-track-player-2.2.0-rc3.tgz",
"integrity": "sha512-JWKFRu+hr1ECN339RH+c+XDM7HfwvdvS4H1p4cJbhg/9b1CQGPJSrYXEhYkngN0msoxBxAjFyFIhjT2fWDCltA==", "integrity": "sha512-Pvmum3MQ5PE8/yOIIPsk00zZk3EzdocUuVUwuBKSCmdKK/3O9YhnZRC3EuT59XCDm23pZZJZDSR44VeXN6Gamg==",
"requires": {} "requires": {}
}, },
"react-native-webview": { "react-native-webview": {
@@ -29401,8 +29395,7 @@
"setimmediate": { "setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
"peer": true
}, },
"setprototypeof": { "setprototypeof": {
"version": "1.1.1", "version": "1.1.1",
@@ -29816,8 +29809,7 @@
"string-hash-64": { "string-hash-64": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz",
"integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==", "integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw=="
"peer": true
}, },
"string-length": { "string-length": {
"version": "4.0.2", "version": "4.0.2",

View File

@@ -30,6 +30,7 @@
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"events": "^3.3.0", "events": "^3.3.0",
"fuse.js": "^6.6.0", "fuse.js": "^6.6.0",
"hermes-engine": "^0.11.0",
"i18n-js": "^3.9.2", "i18n-js": "^3.9.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
@@ -42,12 +43,13 @@
"react-native-fs": "^2.19.0", "react-native-fs": "^2.19.0",
"react-native-gesture-handler": "^2.4.1", "react-native-gesture-handler": "^2.4.1",
"react-native-localize": "^2.2.1", "react-native-localize": "^2.2.1",
"react-native-reanimated": "^2.8.0",
"react-native-safe-area-context": "^4.2.5", "react-native-safe-area-context": "^4.2.5",
"react-native-screens": "^3.13.1", "react-native-screens": "^3.13.1",
"react-native-shadow-2": "^6.0.5", "react-native-shadow-2": "^6.0.5",
"react-native-svg": "^12.3.0", "react-native-svg": "^12.3.0",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"react-native-track-player": "^2.1.3", "react-native-track-player": "^2.2.0-rc3",
"react-native-webview": "^11.18.2", "react-native-webview": "^11.18.2",
"react-redux": "^7.2.6", "react-redux": "^7.2.6",
"redux": "^4.2.0", "redux": "^4.2.0",

View File

@@ -12,6 +12,7 @@ import {
import { useColorScheme } from 'react-native'; import { useColorScheme } from 'react-native';
import { ColorSchemeContext, themes } from './Colors'; import { ColorSchemeContext, themes } from './Colors';
import DownloadManager from './DownloadManager'; import DownloadManager from './DownloadManager';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
// import ErrorReportingAlert from 'utility/ErrorReportingAlert'; // import ErrorReportingAlert from 'utility/ErrorReportingAlert';
export default function App(): JSX.Element { export default function App(): JSX.Element {
@@ -30,7 +31,8 @@ export default function App(): JSX.Element {
Capability.SkipToPrevious, Capability.SkipToPrevious,
Capability.Stop, Capability.Stop,
Capability.SeekTo, Capability.SeekTo,
] ],
stopWithApp: true
}); });
} }
setupTrackPlayer(); setupTrackPlayer();
@@ -41,8 +43,10 @@ export default function App(): JSX.Element {
<PersistGate loading={null} persistor={persistedStore}> <PersistGate loading={null} persistor={persistedStore}>
<ColorSchemeContext.Provider value={theme}> <ColorSchemeContext.Provider value={theme}>
<NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}> <NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Routes /> <GestureHandlerRootView style={{ flex: 1 }}>
<DownloadManager /> <Routes />
<DownloadManager />
</GestureHandlerRootView>
</NavigationContainer> </NavigationContainer>
</ColorSchemeContext.Provider> </ColorSchemeContext.Provider>
</PersistGate> </PersistGate>

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;

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

@@ -0,0 +1,40 @@
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';
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 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, style]}
{...{ animatedProps }}
/>
);
};
export default ReText;

View File

@@ -3,12 +3,12 @@ import Text from './Text';
export const Header = styled(Text)` export const Header = styled(Text)`
margin: 0 0 6px 0; margin: 0 0 6px 0;
font-size: 24px; font-size: 28px;
font-weight: 400; font-weight: 400;
`; `;
export const SubHeader = styled(Text)` export const SubHeader = styled(Text)`
font-size: 14px; font-size: 16px;
margin: 0 0 6px 0; margin: 0 0 6px 0;
font-weight: 400; font-weight: 400;
opacity: 0.5; opacity: 0.5;

View File

@@ -7,12 +7,13 @@ import PlayIcon from 'assets/icons/play.svg';
import PauseIcon from 'assets/icons/pause.svg'; import PauseIcon from 'assets/icons/pause.svg';
import useCurrentTrack from 'utility/useCurrentTrack'; import useCurrentTrack from 'utility/useCurrentTrack';
import TrackPlayer, { State, usePlaybackState, useProgress } from 'react-native-track-player'; import TrackPlayer, { State, usePlaybackState, useProgress } from 'react-native-track-player';
import { THEME_COLOR } from 'CONSTANTS';
import { Shadow } from 'react-native-shadow-2'; import { Shadow } from 'react-native-shadow-2';
import usePrevious from 'utility/usePrevious'; import usePrevious from 'utility/usePrevious';
import Text from 'components/Text'; import Text from 'components/Text';
import useDefaultStyles, { ColoredBlurView } from 'components/Colors'; import useDefaultStyles, { ColoredBlurView } from 'components/Colors';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { calculateProgressTranslation } from 'components/Progresstrack';
import { THEME_COLOR } from 'CONSTANTS';
const NOW_PLAYING_POPOVER_MARGIN = 6; const NOW_PLAYING_POPOVER_MARGIN = 6;
const NOW_PLAYING_POPOVER_WIDTH = Dimensions.get('screen').width - 2 * NOW_PLAYING_POPOVER_MARGIN; const NOW_PLAYING_POPOVER_WIDTH = Dimensions.get('screen').width - 2 * NOW_PLAYING_POPOVER_MARGIN;
@@ -38,6 +39,17 @@ const InnerContainer = styled.Pressable`
align-items: center; align-items: center;
`; `;
const ProgressTrack = styled(Animated.View)<{ stroke?: number; opacity?: number}>`
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;
`;
const ShadowOverlay = styled.View` const ShadowOverlay = styled.View`
position: absolute; position: absolute;
top: 0; top: 0;
@@ -61,21 +73,6 @@ const ActionButton = styled.Pressable`
margin-right: 8px; margin-right: 8px;
`; `;
interface ProgressTrackProps {
opacity?: number;
}
const ProgressTrack = styled(Animated.View)<ProgressTrackProps>`
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background-color: ${THEME_COLOR};
opacity: ${(props) => props.opacity || 1};
border-radius: 99px;
`;
function SelectActionButton() { function SelectActionButton() {
const state = usePlaybackState(); const state = usePlaybackState();
const defaultStyles = useDefaultStyles(); const defaultStyles = useDefaultStyles();
@@ -108,11 +105,6 @@ function SelectActionButton() {
} }
} }
function calculateProgressTranslation(position: number, reference: number) {
const completion = position / reference;
return (1 - (completion || 0)) * -1 * NOW_PLAYING_POPOVER_WIDTH;
}
function NowPlaying() { function NowPlaying() {
const { index, track } = useCurrentTrack(); const { index, track } = useCurrentTrack();
const { buffered, duration, position } = useProgress(); const { buffered, duration, position } = useProgress();
@@ -130,13 +122,13 @@ function NowPlaying() {
const hasChangedTrack = previousIndex !== index || duration === 0; const hasChangedTrack = previousIndex !== index || duration === 0;
Animated.timing(bufferAnimation.current, { Animated.timing(bufferAnimation.current, {
toValue: calculateProgressTranslation(buffered, duration), toValue: calculateProgressTranslation(buffered, duration, NOW_PLAYING_POPOVER_WIDTH),
duration: hasChangedTrack ? 0 : 500, duration: hasChangedTrack ? 0 : 500,
useNativeDriver: true, useNativeDriver: true,
easing: Easing.ease, easing: Easing.ease,
}).start(); }).start();
Animated.timing(progressAnimation.current, { Animated.timing(progressAnimation.current, {
toValue: calculateProgressTranslation(position, duration), toValue: calculateProgressTranslation(position, duration, NOW_PLAYING_POPOVER_WIDTH),
duration: hasChangedTrack ? 0 : 500, duration: hasChangedTrack ? 0 : 500,
useNativeDriver: true, useNativeDriver: true,
}).start(); }).start();
@@ -168,9 +160,11 @@ function NowPlaying() {
<ProgressTrack <ProgressTrack
style={{ transform: [{ translateX: bufferAnimation.current }]}} style={{ transform: [{ translateX: bufferAnimation.current }]}}
opacity={0.15} opacity={0.15}
stroke={4}
/> />
<ProgressTrack <ProgressTrack
style={{ transform: [{ translateX: progressAnimation.current }]}} style={{ transform: [{ translateX: progressAnimation.current }]}}
stroke={4}
/> />
</InnerContainer> </InnerContainer>
</ColoredBlurView> </ColoredBlurView>

View File

@@ -1,95 +1,187 @@
import React, { Component } from 'react'; import React, { useEffect } from 'react';
import TrackPlayer from 'react-native-track-player'; import TrackPlayer, { useProgress } from 'react-native-track-player';
import styled from 'styled-components/native'; import styled from 'styled-components/native';
import { Text, Platform } from 'react-native'; import ProgressTrack, {
import Slider from '@react-native-community/slider'; calculateProgressTranslation,
getMinutes,
getSeconds,
ProgressTrackContainer
} from 'components/Progresstrack';
import { Gesture, GestureDetector, gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { THEME_COLOR } from 'CONSTANTS'; import { THEME_COLOR } from 'CONSTANTS';
import { DefaultStylesProvider } from 'components/Colors'; import Reanimated, {
useSharedValue,
useAnimatedStyle,
withTiming,
Easing,
useDerivedValue,
runOnJS,
} from 'react-native-reanimated';
import ReText from 'components/ReText';
const DRAG_HANDLE_SIZE = 20;
const Container = styled.View`
margin-top: 28px;
`;
const NumberBar = styled.View` const NumberBar = styled.View`
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
padding: 20px 0; padding: 8px 0;
`; `;
function getSeconds(seconds: number): string { const Number = styled(ReText)`
return Math.floor(seconds % 60).toString().padStart(2, '0'); font-size: 13px;
} `;
function getMinutes(seconds: number): number { const DragHandle = styled(Reanimated.View)`
return Math.floor(seconds / 60); width: ${DRAG_HANDLE_SIZE}px;
} height: ${DRAG_HANDLE_SIZE}px;
border-radius: ${DRAG_HANDLE_SIZE}px;
background-color: ${THEME_COLOR};
position: absolute;
left: -${DRAG_HANDLE_SIZE / 2}px;
top: -${DRAG_HANDLE_SIZE / 2 - 2.5}px;
z-index: 14;
`;
interface State { function ProgressBar() {
position: number; const { position, buffered, duration } = useProgress();
duration: number;
gesture?: number;
}
export default class ProgressBar extends Component<{}, State> { const width = useSharedValue(0);
state: State = { const pos = useSharedValue(0);
position: 0, const buf = useSharedValue(0);
duration: 0, const dur = useSharedValue(0);
};
timer: number | null = null; const isDragging = useSharedValue(false);
const offset = useSharedValue(0);
componentDidMount() { const bufferAnimation = useDerivedValue(() => {
this.timer = setInterval(this.updateProgress, 500); return calculateProgressTranslation(buf.value, dur.value, width.value);
} }, [[dur, buf, width.value]]);
componentWillUnmount() { const progressAnimation = useDerivedValue(() => {
if (this.timer) { if (isDragging.value) {
clearInterval(this.timer); return calculateProgressTranslation(offset.value, width.value, width.value);
} else {
return calculateProgressTranslation(pos.value, dur.value, width.value);
} }
} });
updateProgress = async () => { const timePassed = useDerivedValue(() => {
const [position, duration] = await Promise.all([ if (isDragging.value) {
TrackPlayer.getPosition(), const currentPosition = (offset.value - DRAG_HANDLE_SIZE / 2) / (width.value - DRAG_HANDLE_SIZE) * dur.value;
TrackPlayer.getDuration(), return getMinutes(currentPosition) + ':' + getSeconds(currentPosition);
]); } else {
return getMinutes(pos.value) + ':' + getSeconds(pos.value);
}
}, [pos]);
this.setState({ position, duration }); const timeRemaining = useDerivedValue(() => {
}; if (isDragging.value) {
const currentPosition = (offset.value - DRAG_HANDLE_SIZE / 2) / (width.value - DRAG_HANDLE_SIZE) * dur.value;
const remaining = (currentPosition - dur.value) * -1;
return `-${getMinutes(remaining)}:${getSeconds((remaining))}`;
} else {
const remaining = (pos.value - dur.value) * -1;
return `-${getMinutes(remaining)}:${getSeconds((remaining))}`;
}
}, [pos, dur]);
const gesture = Gesture.Pan()
.onBegin(() => {
isDragging.value = true;
}).onUpdate((e) => {
offset.value = Math.min(Math.max(DRAG_HANDLE_SIZE / 2, e.x), width.value - DRAG_HANDLE_SIZE / 2);
}).onFinalize(() => {
pos.value = (offset.value - DRAG_HANDLE_SIZE / 2) / (width.value - DRAG_HANDLE_SIZE) * dur.value;
isDragging.value = false;
runOnJS(TrackPlayer.seekTo)(pos.value);
});
handleGesture = async (gesture: number) => { useEffect(() => {
// Set relative translation in state pos.value = position;
this.setState({ gesture }); buf.value = buffered;
}; dur.value = duration;
}, [position, buffered, duration]);
handleEndOfGesture = (position: number) => { const dragHandleStyles = useAnimatedStyle(() => {
// Calculate and set the new position return {
TrackPlayer.seekTo(position); transform: [
this.setState({ gesture: undefined, position }); { translateX: offset.value },
}; {
scale: withTiming(isDragging.value ? 1 : 0.05, {
render() { duration: 100,
const { position, duration, gesture } = this.state; easing: Easing.out(Easing.ease),
})
return (
<DefaultStylesProvider>
{defaultStyle => (
<>
<Slider
value={gesture || position}
minimumValue={0}
maximumValue={duration || 0}
onValueChange={this.handleGesture}
onSlidingComplete={this.handleEndOfGesture}
minimumTrackTintColor={THEME_COLOR}
thumbTintColor={Platform.OS === 'android' ? THEME_COLOR : undefined}
disabled={!duration}
/>
<NumberBar>
<Text style={defaultStyle.text}>{getMinutes(gesture || position)}:{getSeconds(gesture || position)}</Text>
<Text style={defaultStyle.text}>{getMinutes(duration)}:{getSeconds(duration)}</Text>
</NumberBar>
</>
)
} }
</DefaultStylesProvider> ],
); };
} });
}
const bufferStyles = useAnimatedStyle(() => ({
transform: [
{ translateX: bufferAnimation.value }
]
}));
const progressStyles = useAnimatedStyle(() => {
return {
transform: [
{ translateX: progressAnimation.value }
]
};
});
const timePassedStyles = useAnimatedStyle(() => {
return {
transform: [
{ translateY: withTiming(isDragging.value && offset.value < 48 ? 12 : 0, {
duration: 145,
easing: Easing.ease
}) },
],
};
});
const timeRemainingStyles = useAnimatedStyle(() => {
return {
transform: [
{ translateY: withTiming(isDragging.value && offset.value > width.value - 48 ? 12 : 0, {
duration: 150,
easing: Easing.ease
}) },
],
};
});
return (
<Container onLayout={(e) => { width.value = e.nativeEvent.layout.width; }}>
<GestureDetector gesture={gesture}>
<>
<ProgressTrackContainer>
<ProgressTrack
opacity={0.15}
/>
<ProgressTrack
style={bufferStyles}
opacity={0.15}
/>
<ProgressTrack
style={progressStyles}
/>
</ProgressTrackContainer>
<DragHandle style={dragHandleStyles} />
<NumberBar style={{ flex: 1 }}>
<Number text={timePassed} style={timePassedStyles} />
<Number text={timeRemaining} style={timeRemainingStyles} />
</NumberBar>
</>
</GestureDetector>
</Container>
);
}
export default gestureHandlerRootHOC(ProgressBar);

View File

@@ -21,8 +21,8 @@ export default function Player() {
<ScrollView contentContainerStyle={styles.inner} style={defaultStyles.view}> <ScrollView contentContainerStyle={styles.inner} style={defaultStyles.view}>
<NowPlaying /> <NowPlaying />
<ConnectionNotice /> <ConnectionNotice />
<MediaControls />
<ProgressBar /> <ProgressBar />
<MediaControls />
<Queue /> <Queue />
</ScrollView> </ScrollView>
); );