Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1edeb00631 | ||
|
|
d422c1ff1e | ||
|
|
28b330ad4c | ||
|
|
a867513212 | ||
|
|
14a6341fae |
39
Gemfile.lock
39
Gemfile.lock
@@ -6,21 +6,21 @@ GEM
|
||||
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.434.0)
|
||||
aws-sdk-core (3.113.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-kms (1.43.0)
|
||||
aws-sdk-core (~> 3, >= 3.112.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.88.0)
|
||||
aws-sdk-s3 (1.92.0)
|
||||
aws-sdk-core (~> 3, >= 3.112.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-sigv4 (1.2.3)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.0.3)
|
||||
@@ -35,7 +35,7 @@ GEM
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (3.2.1)
|
||||
emoji_regex (3.2.2)
|
||||
excon (0.79.0)
|
||||
faraday (1.3.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
@@ -47,8 +47,8 @@ GEM
|
||||
faraday-net_http (1.0.1)
|
||||
faraday_middleware (1.0.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.2)
|
||||
fastlane (2.174.0)
|
||||
fastimage (2.2.3)
|
||||
fastlane (2.178.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@@ -72,6 +72,7 @@ GEM
|
||||
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)
|
||||
@@ -85,7 +86,7 @@ GEM
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-sentry (1.8.0)
|
||||
fastlane-plugin-sentry (1.8.1)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.38.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
@@ -95,7 +96,7 @@ GEM
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.12)
|
||||
google-apis-core (0.2.1)
|
||||
google-apis-core (0.3.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.14)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
@@ -105,17 +106,17 @@ GEM
|
||||
rexml
|
||||
signet (~> 0.14)
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.1.0)
|
||||
google-apis-iamcredentials_v1 (0.2.0)
|
||||
google-apis-core (~> 0.1)
|
||||
google-apis-storage_v1 (0.2.0)
|
||||
google-apis-storage_v1 (0.3.0)
|
||||
google-apis-core (~> 0.1)
|
||||
google-cloud-core (1.5.0)
|
||||
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.31.0)
|
||||
addressable (~> 2.5)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
@@ -123,7 +124,7 @@ GEM
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (~> 0.9)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (0.15.1)
|
||||
googleauth (0.16.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
@@ -158,7 +159,7 @@ GEM
|
||||
ruby2_keywords (0.0.4)
|
||||
rubyzip (2.3.0)
|
||||
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)
|
||||
|
||||
@@ -542,7 +542,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
CURRENT_PROJECT_VERSION = 13;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -573,7 +573,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
CURRENT_PROJECT_VERSION = 13;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
INFOPLIST_FILE = JellyfinAudioPlayer/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>12</string>
|
||||
<string>13</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>12</string>
|
||||
<string>13</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -261,13 +261,15 @@ PODS:
|
||||
- React-jsi (= 0.64.0)
|
||||
- React-perflogger (= 0.64.0)
|
||||
- React-jsinspector (0.64.0)
|
||||
- react-native-airplay-button (1.0.4):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (3.2.0):
|
||||
- React-Core
|
||||
- react-native-slider (3.0.3):
|
||||
- React
|
||||
- react-native-track-player (1.2.3):
|
||||
- react-native-track-player (1.2.6):
|
||||
- React
|
||||
- react-native-webview (11.2.3):
|
||||
- react-native-webview (11.3.1):
|
||||
- React-Core
|
||||
- React-perflogger (0.64.0)
|
||||
- React-RCTActionSheet (0.64.0):
|
||||
@@ -433,6 +435,7 @@ DEPENDENCIES:
|
||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-airplay-button (from `../node_modules/react-native-airplay-button`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- react-native-track-player (from `../node_modules/react-native-track-player`)
|
||||
@@ -511,6 +514,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
React-jsinspector:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
react-native-airplay-button:
|
||||
:path: "../node_modules/react-native-airplay-button"
|
||||
react-native-safe-area-context:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-slider:
|
||||
@@ -594,10 +599,11 @@ SPEC CHECKSUMS:
|
||||
React-jsi: 74341196d9547cbcbcfa4b3bbbf03af56431d5a1
|
||||
React-jsiexecutor: 06a9c77b56902ae7ffcdd7a4905f664adc5d237b
|
||||
React-jsinspector: 0ae35a37b20d5e031eb020a69cc5afdbd6406301
|
||||
react-native-airplay-button: 6899e488bff6b4d87b33c1def54c919dad2e3803
|
||||
react-native-safe-area-context: e471852c5ed67eea4b10c5d9d43c1cebae3b231d
|
||||
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
||||
react-native-track-player: ba2416753b58f3cdf9db5a07daa65876d659f925
|
||||
react-native-webview: 36561eaf7508e67f72d8c959b713bac841f3652e
|
||||
react-native-track-player: 92eaa90aeb24ce1a7149f983c75128075004e7a2
|
||||
react-native-webview: 30f048378c6cee522ed9bbbedbc34acb21e58188
|
||||
React-perflogger: 9c547d8f06b9bf00cb447f2b75e8d7f19b7e02af
|
||||
React-RCTActionSheet: 3080b6e12e0e1a5b313c8c0050699b5c794a1b11
|
||||
React-RCTAnimation: 3f96f21a497ae7dabf4d2f150ee43f906aaf516f
|
||||
|
||||
836
package-lock.json
generated
836
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -19,8 +19,8 @@
|
||||
"@react-navigation/bottom-tabs": "^5.11.8",
|
||||
"@react-navigation/native": "^5.9.3",
|
||||
"@react-navigation/stack": "^5.14.3",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@sentry/react-native": "^2.3.0",
|
||||
"@reduxjs/toolkit": "^1.5.1",
|
||||
"@sentry/react-native": "^2.4.0",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"date-fns": "^2.19.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
@@ -34,41 +34,41 @@
|
||||
"react-native-fast-image": "^8.3.4",
|
||||
"react-native-gesture-handler": "^1.10.3",
|
||||
"react-native-localize": "^2.0.2",
|
||||
"react-native-reanimated": "^2.0.0",
|
||||
"react-native-reanimated": "^2.1.0",
|
||||
"react-native-safe-area-context": "^3.2.0",
|
||||
"react-native-screens": "^2.18.1",
|
||||
"react-native-svg": "^12.1.0",
|
||||
"react-native-svg-transformer": "^0.14.3",
|
||||
"react-native-track-player": "^1.2.6",
|
||||
"react-native-webview": "^11.3.1",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-native-track-player": "^1.2.7",
|
||||
"react-native-webview": "^11.3.2",
|
||||
"react-redux": "^7.2.3",
|
||||
"redux": "^4.0.5",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-persist": "^6.0.0",
|
||||
"styled-components": "^5.2.1"
|
||||
"styled-components": "^5.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.13.10",
|
||||
"@babel/core": "^7.13.14",
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@react-native-community/eslint-config": "^2.0.0",
|
||||
"@sentry/cli": "^1.63.1",
|
||||
"@sentry/cli": "^1.63.2",
|
||||
"@types/i18n-js": "^3.8.0",
|
||||
"@types/jest": "^26.0.21",
|
||||
"@types/react-native": "^0.64.0",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/react-native": "^0.64.2",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
"@types/react-test-renderer": "^17.0.1",
|
||||
"@types/redux-logger": "^3.0.8",
|
||||
"@types/styled-components": "^5.1.9",
|
||||
"@types/styled-components-react-native": "^5.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.20.0",
|
||||
"@typescript-eslint/parser": "^4.20.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint": "^7.23.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^26.6.3",
|
||||
"metro-react-native-babel-preset": "^0.65.2",
|
||||
"react-test-renderer": "^17.0.1",
|
||||
"react-test-renderer": "^17.0.2",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
} from '@react-navigation/native';
|
||||
import { useColorScheme } from 'react-native';
|
||||
import { ColorSchemeContext, themes } from './Colors';
|
||||
import ErrorReportingAlert from 'utility/ErrorReportingAlert';
|
||||
// import ErrorReportingAlert from 'utility/ErrorReportingAlert';
|
||||
import PlayerStateUpdater from './PlayerStateUpdater';
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
const colorScheme = useColorScheme();
|
||||
@@ -41,6 +42,7 @@ export default function App(): JSX.Element {
|
||||
<ColorSchemeContext.Provider value={theme}>
|
||||
<NavigationContainer theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<Routes />
|
||||
<PlayerStateUpdater />
|
||||
</NavigationContainer>
|
||||
</ColorSchemeContext.Provider>
|
||||
</PersistGate>
|
||||
|
||||
@@ -13,21 +13,14 @@ interface ButtonProps extends PressableProps {
|
||||
style?: ViewProps['style'];
|
||||
}
|
||||
|
||||
interface PressableStyleProps {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
const BaseButton = styled.Pressable<PressableStyleProps>`
|
||||
const BaseButton = styled.Pressable`
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
|
||||
${props => props.active && css`
|
||||
background-color: ${THEME_COLOR};
|
||||
`}
|
||||
`;
|
||||
|
||||
const ButtonText = styled.Text<{ active?: boolean }>`
|
||||
@@ -51,8 +44,10 @@ export default function Button(props: ButtonProps) {
|
||||
{...rest}
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
active={isPressed}
|
||||
style={[ defaultStyles.button, props.style ]}
|
||||
style={[
|
||||
props.style,
|
||||
{ backgroundColor: isPressed ? THEME_COLOR : defaultStyles.button.backgroundColor }
|
||||
]}
|
||||
>
|
||||
{Icon &&
|
||||
<Icon
|
||||
|
||||
54
src/components/PlayerStateUpdater.ts
Normal file
54
src/components/PlayerStateUpdater.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import TrackPlayer, { TrackPlayerEvents } from 'react-native-track-player';
|
||||
import { shallowEqual, useDispatch } from 'react-redux';
|
||||
import { useTypedSelector } from 'store';
|
||||
import player from 'store/player';
|
||||
|
||||
function PlayerStateUpdater() {
|
||||
const dispatch = useDispatch();
|
||||
const trackId = useTypedSelector(state => state.player.currentTrack?.id, shallowEqual);
|
||||
|
||||
const handleUpdate = useCallback(async () => {
|
||||
const currentTrackId = await TrackPlayer.getCurrentTrack();
|
||||
|
||||
// GUARD: Only retrieve new track if it is different from the one we
|
||||
// have currently in state.
|
||||
if (currentTrackId === trackId){
|
||||
return;
|
||||
}
|
||||
|
||||
// GUARD: Only fetch current track if there is a current track
|
||||
if (!currentTrackId) {
|
||||
dispatch(player.actions.setCurrentTrack(undefined));
|
||||
}
|
||||
|
||||
// If it is different, retrieve the track and save it
|
||||
try {
|
||||
const currentTrack = await TrackPlayer.getTrack(currentTrackId);
|
||||
dispatch(player.actions.setCurrentTrack(currentTrack));
|
||||
} catch {
|
||||
// Due to the async nature, a track might be removed at the
|
||||
// point when we try to retrieve it. If this happens, we'll just
|
||||
// smother the error and wait for a new track update to
|
||||
// finish.
|
||||
}
|
||||
}, [trackId, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
function handler() {
|
||||
handleUpdate();
|
||||
}
|
||||
|
||||
handler();
|
||||
|
||||
const subscription = TrackPlayer.addEventListener(TrackPlayerEvents.PLAYBACK_TRACK_CHANGED, handler);
|
||||
|
||||
return () => {
|
||||
subscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default PlayerStateUpdater;
|
||||
@@ -80,7 +80,7 @@ const Album: React.FC = () => {
|
||||
const refresh = useCallback(() => { dispatch(fetchTracksByAlbum(id)); }, [id, dispatch]);
|
||||
const selectTrack = useCallback(async (trackId) => {
|
||||
const tracks = await playAlbum(id, false);
|
||||
|
||||
|
||||
if (tracks) {
|
||||
const track = tracks.find((t) => t.id.startsWith(trackId));
|
||||
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { Track } from 'react-native-track-player';
|
||||
|
||||
interface State {
|
||||
addedTrackCount: number,
|
||||
currentTrack: Track | undefined,
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
addedTrackCount: 0,
|
||||
currentTrack: undefined,
|
||||
};
|
||||
|
||||
const player = createSlice({
|
||||
name: 'player',
|
||||
initialState: 0,
|
||||
initialState,
|
||||
reducers: {
|
||||
addNewTrackToPlayer: (state) => state + 1,
|
||||
addNewTrackToPlayer: (state) => {
|
||||
state.addedTrackCount += 1;
|
||||
},
|
||||
setCurrentTrack: (state, action: PayloadAction<Track | undefined>) => {
|
||||
state.currentTrack = action.payload;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,42 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
|
||||
import { Track } from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
|
||||
const idEqual = (left: Track | undefined, right: Track | undefined) => {
|
||||
return left?.id === right?.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* This hook retrieves the current playing track from TrackPlayer
|
||||
*/
|
||||
export default function useCurrentTrack(): Track | undefined {
|
||||
const state = usePlaybackState();
|
||||
const [track, setTrack] = useState<Track>();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTrack = async () => {
|
||||
const currentTrackId = await TrackPlayer.getCurrentTrack();
|
||||
|
||||
// GUARD: Only fetch current track if there is a current track
|
||||
if (!currentTrackId) {
|
||||
setTrack(undefined);
|
||||
}
|
||||
|
||||
// GUARD: Only retrieve new track if it is different from the one we
|
||||
// have currently in state.
|
||||
if (currentTrackId === track?.id){
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is different, retrieve the track and save it
|
||||
try {
|
||||
const currentTrack = await TrackPlayer.getTrack(currentTrackId);
|
||||
setTrack(currentTrack);
|
||||
} catch {
|
||||
// Due to the async nature, a track might be removed at the
|
||||
// point when we try to retrieve it. If this happens, we'll just
|
||||
// smother the error and wait for a new track update to
|
||||
// finish.
|
||||
}
|
||||
};
|
||||
|
||||
fetchTrack();
|
||||
}, [state, track, setTrack]);
|
||||
|
||||
const track = useTypedSelector(state => state.player.currentTrack, idEqual);
|
||||
|
||||
return track;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { useTypedSelector } from 'store';
|
||||
export default function useQueue(): Track[] {
|
||||
const state = usePlaybackState();
|
||||
const [queue, setQueue] = useState<Track[]>([]);
|
||||
const addedTrackCount = useTypedSelector(state => state.player);
|
||||
const addedTrackCount = useTypedSelector(state => state.player.addedTrackCount);
|
||||
|
||||
useEffect(() => {
|
||||
TrackPlayer.getQueue().then(setQueue);
|
||||
|
||||
Reference in New Issue
Block a user