diff --git a/components/AppInfoFooter.js b/components/AppInfoFooter.js
new file mode 100644
index 000000000..dd9dd76ad
--- /dev/null
+++ b/components/AppInfoFooter.js
@@ -0,0 +1,31 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import React from 'react';
+import { StyleSheet, View } from 'react-native';
+import { colors, Text } from 'react-native-elements';
+import Constants from 'expo-constants';
+
+import { getAppName } from '../utils/Device';
+
+const AppInfoFooter = () => (
+
+ {`${getAppName()}`}
+ {`${Constants.nativeAppVersion} (${Constants.nativeBuildVersion})`}
+ {`Expo Version: ${Constants.expoVersion}`}
+
+);
+
+const styles = StyleSheet.create({
+ container: {
+ margin: 15
+ },
+ text: {
+ color: colors.grey4,
+ fontSize: 15
+ }
+});
+
+export default AppInfoFooter;
diff --git a/components/BrowserListItem.js b/components/BrowserListItem.js
new file mode 100644
index 000000000..7526184cd
--- /dev/null
+++ b/components/BrowserListItem.js
@@ -0,0 +1,34 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import React from 'react';
+import { ListItem } from 'react-native-elements';
+import PropTypes from 'prop-types';
+
+import { openBrowser } from '../utils/WebBrowser';
+
+const BrowserListItem = ({item, index}) => (
+ {
+ openBrowser(item.url);
+ }}
+ />
+);
+
+BrowserListItem.propTypes = {
+ item: PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired,
+ url: PropTypes.string.isRequired
+ }).isRequired,
+ index: PropTypes.number.isRequired
+};
+
+export default BrowserListItem;
diff --git a/components/ButtonListItem.js b/components/ButtonListItem.js
new file mode 100644
index 000000000..99e718e0b
--- /dev/null
+++ b/components/ButtonListItem.js
@@ -0,0 +1,25 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import React from 'react';
+import { StyleSheet } from 'react-native';
+import { Button } from 'react-native-elements';
+import PropTypes from 'prop-types';
+
+const ButtonListItem = ({item}) => (
+
+);
+
+ButtonListItem.propTypes = {
+ item: PropTypes.object.isRequired
+};
+
+const styles = StyleSheet.create({
+ button: {
+ margin: 15
+ }
+});
+
+export default ButtonListItem;
diff --git a/components/ServerListItem.js b/components/ServerListItem.js
new file mode 100644
index 000000000..5619d5501
--- /dev/null
+++ b/components/ServerListItem.js
@@ -0,0 +1,79 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import React from 'react';
+import { StyleSheet, View, Platform } from 'react-native';
+import { Button, Icon, ListItem, colors } from 'react-native-elements';
+import PropTypes from 'prop-types';
+
+import { getIconName } from '../utils/Icons';
+
+const ServerListItem = ({item, index, activeServer, onDelete, onPress}) => {
+ let title;
+ let subtitle;
+ if (item.info) {
+ title = item.info.ServerName;
+ subtitle = `Version: ${item.info.Version}\n${item.urlString}`;
+ } else {
+ title = item.url.host;
+ subtitle = `Version: unknown\n${item.urlString}`;
+ }
+
+ return (
+
+ ) : (
+
+ )
+ )}
+ rightElement={(
+ onDelete(index)}
+ />
+ )}
+ topDivider={index === 0}
+ bottomDivider
+ onPress={() => onPress(index)}
+ />
+ );
+};
+
+ServerListItem.propTypes = {
+ item: PropTypes.object.isRequired,
+ index: PropTypes.number.isRequired,
+ activeServer: PropTypes.number.isRequired,
+ onDelete: PropTypes.func.isRequired,
+ onPress: PropTypes.func.isRequired
+};
+
+const styles = StyleSheet.create({
+ title: {
+ marginBottom: 2
+ },
+ leftElement: {
+ width: 12
+ },
+ deleteButton: {
+ color: Platform.OS === 'ios' ? colors.platform.ios.error : colors.platform.android.error
+ }
+});
+
+export default ServerListItem;
diff --git a/components/SettingsSection.js b/components/SettingsSection.js
deleted file mode 100644
index c6c92b071..000000000
--- a/components/SettingsSection.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-import React from 'react';
-import { StyleSheet, View } from 'react-native';
-import { colors, Text } from 'react-native-elements';
-import PropTypes from 'prop-types';
-
-export default class SettingsSection extends React.Component {
- static propTypes = {
- children: PropTypes.element,
- heading: PropTypes.string
- };
-
- render() {
- return (
-
-
- {this.props.heading}
-
- {this.props.children}
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- container: {
- marginTop: 15,
- marginBottom: 15
- },
- heading: {
- color: colors.grey4,
- fontSize: 17,
- fontWeight: '600',
- marginBottom: 12,
- marginLeft: 15,
- marginRight: 15
- }
-});
diff --git a/constants/Links.js b/constants/Links.js
index d071b0f9a..2558fd0bc 100644
--- a/constants/Links.js
+++ b/constants/Links.js
@@ -7,6 +7,7 @@ import { Platform } from 'react-native';
export default [
{
+ key: 'links-website',
name: 'Jellyfin Website',
url: 'https://jellyfin.org/',
icon: {
@@ -15,6 +16,7 @@ export default [
}
},
{
+ key: 'links-documentation',
name: 'Documentation',
url: 'https://docs.jellyfin.org',
icon: {
@@ -23,6 +25,7 @@ export default [
}
},
{
+ key: 'links-source',
name: 'Source Code',
url: 'https://github.com/jellyfin/jellyfin-expo',
icon: {
@@ -31,6 +34,7 @@ export default [
}
},
{
+ key: 'links-feature',
name: 'Request a Feature',
url: 'https://features.jellyfin.org/',
icon: {
@@ -39,6 +43,7 @@ export default [
}
},
{
+ key: 'links-issue',
name: 'Report an Issue',
url: 'https://github.com/jellyfin/jellyfin-expo/issues',
icon: {
diff --git a/models/ServerModel.js b/models/ServerModel.js
new file mode 100644
index 000000000..ffef62f55
--- /dev/null
+++ b/models/ServerModel.js
@@ -0,0 +1,53 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { action, autorun, computed, observable } from 'mobx';
+import { ignore } from 'mobx-sync';
+import { task } from 'mobx-task';
+
+import JellyfinValidator from '../utils/JellyfinValidator';
+
+export default class ServerModel {
+ @observable
+ id
+
+ @observable
+ url
+
+ @ignore
+ @observable
+ online = false
+
+ @observable
+ info
+
+ constructor(id, url, info) {
+ this.id = id;
+ this.url = url;
+ this.info = info;
+
+ autorun(() => {
+ this.urlString = this.parseUrlString;
+ });
+ }
+
+ @computed
+ get parseUrlString() {
+ try {
+ return JellyfinValidator.getServerUrl(this);
+ } catch (ex) {
+ return '';
+ }
+ }
+
+ @task
+ async fetchInfo() {
+ return await JellyfinValidator.fetchServerInfo(this)
+ .then(action(info => {
+ this.online = true;
+ this.info = info;
+ }));
+ }
+}
diff --git a/navigation/AppNavigator.js b/navigation/AppNavigator.js
index d13f636e0..43684bcd6 100644
--- a/navigation/AppNavigator.js
+++ b/navigation/AppNavigator.js
@@ -71,7 +71,7 @@ const AppNavigator = observer(() => {
return (
0) ? 'Main' : 'AddServer'}
+ initialRouteName={(rootStore.serverStore.servers?.length > 0) ? 'Main' : 'AddServer'}
headerMode='screen'
screenOptions={{ headerShown: false }}
>
@@ -95,7 +95,7 @@ const AppNavigator = observer(() => {
name='AddServer'
component={AddServerScreen}
options={{
- headerShown: rootStore.serverStore.servers.length > 0,
+ headerShown: rootStore.serverStore.servers?.length > 0,
title: 'Add Server'
}}
/>
diff --git a/package.json b/package.json
index a4ac88ee0..94f377c40 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"mobx": "^5.15.4",
"mobx-react": "^6.2.2",
"mobx-sync": "^3.0.0",
+ "mobx-task": "^2.0.1",
"prop-types": "^15.7.2",
"react": "16.9.0",
"react-lifecycles-compat": "^3.0.4",
diff --git a/screens/HomeScreen.js b/screens/HomeScreen.js
index 9c3401c11..0584f30ae 100644
--- a/screens/HomeScreen.js
+++ b/screens/HomeScreen.js
@@ -18,7 +18,6 @@ import PropTypes from 'prop-types';
import { useStores } from '../hooks/useStores';
import Colors from '../constants/Colors';
import { getAppName, getSafeDeviceName } from '../utils/Device';
-import JellyfinValidator from '../utils/JellyfinValidator';
import NativeShell from '../utils/NativeShell';
import { openBrowser } from '../utils/WebBrowser';
@@ -38,8 +37,6 @@ true;
@observer
class HomeScreen extends React.Component {
state = {
- server: null,
- serverUrl: null,
isError: false,
isFullscreen: false,
isLoading: true,
@@ -52,29 +49,6 @@ class HomeScreen extends React.Component {
rootStore: PropTypes.object.isRequired
}
- async bootstrapAsync() {
- const servers = this.props.rootStore.serverStore.servers;
- let activeServer = this.props.rootStore.settingStore.activeServer;
-
- // If the activeServer is greater than the length of the server array, reset it to 0
- if (activeServer && servers.length && activeServer > servers.length - 1) {
- this.props.rootStore.settingStore.activeServer = 0;
- activeServer = 0;
- }
-
- let server;
- if (servers.length > 0) {
- server = servers[activeServer];
- }
-
- const serverUrl = JellyfinValidator.getServerUrl(server);
-
- this.setState({
- server,
- serverUrl
- });
- }
-
getErrorView() {
return (
@@ -183,15 +157,9 @@ class HomeScreen extends React.Component {
this.onGoHome();
}
});
- // Bootstrap component state
- this.bootstrapAsync();
}
componentDidUpdate(prevProps, prevState) {
- if (typeof this.props.route.params?.activeServer != 'undefined' &&
- prevProps.route.params?.activeServer !== this.props.route.params?.activeServer) {
- this.bootstrapAsync();
- }
if (prevState.isFullscreen !== this.state.isFullscreen) {
// Update the screen orientation
this.updateScreenOrientation();
@@ -210,6 +178,11 @@ class HomeScreen extends React.Component {
// Hide webview until loaded
const webviewStyle = (this.state.isError || this.state.isLoading) ? styles.loading : styles.container;
+ if (!this.props.rootStore.serverStore.servers || this.props.rootStore.serverStore.servers.length === 0) {
+ return null;
+ }
+ const server = this.props.rootStore.serverStore.servers[this.props.rootStore.settingStore.activeServer];
+
return (
{!this.state.isFullscreen && (
@@ -228,10 +201,10 @@ class HomeScreen extends React.Component {
) : null
}
>
- {this.state.serverUrl && (
+ {server && server.urlString && (
(this.webview = ref)}
- source={{ uri: this.state.serverUrl }}
+ source={{ uri: server.urlString }}
style={webviewStyle}
// Inject javascript for NativeShell
injectedJavaScriptBeforeContentLoaded={injectedJavaScript}
diff --git a/screens/SettingsScreen.js b/screens/SettingsScreen.js
index f6a29db43..cd4e5e0e0 100644
--- a/screens/SettingsScreen.js
+++ b/screens/SettingsScreen.js
@@ -3,283 +3,166 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-import React from 'react';
-import {
- ActivityIndicator,
- Alert,
- FlatList,
- Platform,
- ScrollView,
- StyleSheet,
- View
-} from 'react-native';
-import { Button, colors, ListItem, Text, Icon } from 'react-native-elements';
+import React, { useEffect } from 'react';
+import { Alert, AsyncStorage, Platform, SectionList, StyleSheet } from 'react-native';
+import { colors, Text } from 'react-native-elements';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useNavigation } from '@react-navigation/native';
import { observer } from 'mobx-react';
-import Constants from 'expo-constants';
-import Url from 'url';
-import PropTypes from 'prop-types';
-import { useStores } from '../hooks/useStores';
-import SettingsSection from '../components/SettingsSection';
+import AppInfoFooter from '../components/AppInfoFooter';
+import BrowserListItem from '../components/BrowserListItem';
+import ButtonListItem from '../components/ButtonListItem';
+import ServerListItem from '../components/ServerListItem';
import Colors from '../constants/Colors';
import Links from '../constants/Links';
-import JellyfinValidator from '../utils/JellyfinValidator';
-import { getAppName } from '../utils/Device';
-import { openBrowser } from '../utils/WebBrowser';
-
-@observer
-class SettingsScreen extends React.Component {
- static propTypes = {
- navigation: PropTypes.object.isRequired,
- rootStore: PropTypes.object.isRequired
- }
-
- state = {
- servers: null
- };
-
- _keyExtractor = (item, index) => `${item.name}-${index}`;
-
- _renderLink = ({ item, index }) => {
- console.log('renderLink', item);
- return (
- {
- openBrowser(item.url);
- }}
- />
- );
- };
+import { useStores } from '../hooks/useStores';
- _renderServer = ({ item, index }) => {
- const { info, serverUrl, online = false } = item;
- console.log('renderServer', info, serverUrl, online);
+const SettingsScreen = observer(() => {
+ const { rootStore } = useStores();
+ const navigation = useNavigation();
- let title;
- let subtitle;
- if (info) {
- title = info.ServerName;
- subtitle = `Version: ${info.Version}\n${serverUrl}`;
- } else {
- title = Url.parse(serverUrl).host;
- subtitle = `Version: unknown\n${serverUrl}`;
- }
+ useEffect(() => {
+ // Fetch server info
+ rootStore.serverStore.fetchInfo();
+ }, []);
- return (
- ) : (
-
- )
- )}
- rightElement={(
- this.onDeleteServer(index)}
- />
- )}
- topDivider={index === 0}
- bottomDivider
- onPress={async () => {
- this.props.rootStore.settingStore.activeServer = index;
- this.props.navigation.navigate('Home', { activeServer: index });
- }}
- />);
+ const onAddServer = () => {
+ navigation.navigate('AddServer');
};
- async bootstrapAsync() {
- let { servers } = this.props.rootStore.serverStore;
-
- servers = servers.map(async (server) => {
- let serverUrl;
- try {
- serverUrl = JellyfinValidator.getServerUrl(server);
- } catch (err) {
- serverUrl = '';
- }
- // Try to fetch the server's public info
- try {
- const serverInfo = await JellyfinValidator.fetchServerInfo(server);
- return Object.assign(
- {},
- server,
- {
- info: serverInfo,
- serverUrl,
- online: true
- }
- );
- } catch (err) {
- return Object.assign(
- {},
- server,
- {
- serverUrl,
- online: false
- }
- );
- }
- });
-
- servers = await Promise.all(servers);
-
- console.log('bootstrapAsync', servers);
-
- this.setState({
- servers
- });
- }
-
- async deleteServer(index) {
- // Remove server and update active server
- this.props.rootStore.serverStore.removeServer(index);
- this.props.rootStore.settingStore.activeServer = 0;
-
- if (this.props.rootStore.serverStore.servers.length > 0) {
- // More servers exist, update state and navigate home
- this.bootstrapAsync();
- this.props.navigation.navigate('Home', { activeServer: 0 });
- } else {
- // No servers are present, navigate to add server screen
- this.props.navigation.replace('AddServer');
- }
- }
-
- async resetApplication() {
- // Reset data in stores
- this.props.rootStore.serverStore.servers = [];
- this.props.rootStore.settingStore.activeServer = 0;
- // Navigate to the loading screen
- this.props.navigation.replace('AddServer');
- }
-
- onDeleteServer(index) {
+ const onDeleteServer = index => {
Alert.alert(
'Delete Server',
'Are you sure you want to delete this server?',
[
{ text: 'Cancel' },
- { text: 'Delete', onPress: () => this.deleteServer(index), style: 'destructive' }
+ {
+ text: 'Delete',
+ onPress: () => {
+ // Remove server and update active server
+ rootStore.serverStore.removeServer(index);
+ rootStore.settingStore.activeServer = 0;
+
+ if (rootStore.serverStore.servers.length > 0) {
+ // More servers exist, navigate home
+ navigation.navigate('Home');
+ } else {
+ // No servers are present, navigate to add server screen
+ navigation.replace('AddServer');
+ }
+ },
+ style: 'destructive'
+ }
]
);
- }
+ };
+
+ const onSelectServer = index => {
+ rootStore.settingStore.activeServer = index;
+ navigation.navigate('Home');
+ };
- onResetApplication() {
+ const onResetApplication = () => {
Alert.alert(
'Reset Application',
'Are you sure you want to reset all settings?',
[
{ text: 'Cancel' },
- { text: 'Reset', onPress: () => this.resetApplication(), style: 'destructive' }
+ {
+ text: 'Reset',
+ onPress: () => {
+ // Reset data in stores
+ rootStore.reset();
+ AsyncStorage.clear();
+ // Navigate to the loading screen
+ navigation.replace('AddServer');
+ },
+ style: 'destructive'
+ }
]
);
- }
-
- componentDidMount() {
- this.bootstrapAsync();
- }
-
- render() {
- return (
-
-
-
- {
- this.state.servers ? (
-
- ) : (
-
- )
- }
-
-
- this.props.navigation.navigate('AddServer')}
- />
-
-
-
-
+ };
- this.onResetApplication()}
- />
+ const AugmentedServerListItem = (props) => (
+
+ );
+
+ const getSections = () => {
+ return [
+ {
+ title: 'Servers',
+ data: rootStore.serverStore.servers.slice(),
+ keyExtractor: (item, index) => `server-${index}`,
+ renderItem: AugmentedServerListItem
+ },
+ {
+ title: 'Add Server',
+ hideHeader: true,
+ data: [{
+ key: 'add-server-button',
+ title: 'Add Server',
+ onPress: onAddServer
+ }],
+ renderItem: ButtonListItem
+ },
+ {
+ title: 'Links',
+ data: Links,
+ renderItem: BrowserListItem
+ },
+ {
+ title: 'Reset Application',
+ hideHeader: true,
+ data: [{
+ key: 'reset-app-button',
+ title: 'Reset Application',
+ buttonStyle: {
+ backgroundColor: Platform.OS === 'ios' ? colors.platform.ios.error : colors.platform.android.error
+ },
+ onPress: onResetApplication
+ }],
+ renderItem: ButtonListItem
+ }
+ ];
+ };
-
- {`${getAppName()}`}
- {`${Constants.nativeAppVersion} (${Constants.nativeBuildVersion})`}
- {`Expo Version: ${Constants.expoVersion}`}
-
-
-
- );
- }
-}
+ return (
+
+ {JSON.stringify(item)} }
+ renderSectionHeader={({ section: { title, hideHeader } }) => hideHeader ? null : {title} }
+ ListFooterComponent={AppInfoFooter}
+ showsVerticalScrollIndicator={false}
+ />
+
+ );
+});
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.backgroundColor
},
- infoContainer: {
- margin: 15
- },
- infoText: {
+ header: {
+ backgroundColor: Colors.backgroundColor,
color: colors.grey4,
- fontSize: 15
+ fontSize: 17,
+ fontWeight: '600',
+ paddingVertical: 8,
+ paddingHorizontal: 15,
+ marginBottom: 1
}
});
-// Inject the Navigation Hook as a prop to mimic the legacy behavior
-const SettingsScreenWithNavigation = observer((props) => {
- const stores = useStores();
- return ;
-});
-
-export default SettingsScreenWithNavigation;
+export default SettingsScreen;
diff --git a/stores/RootStore.js b/stores/RootStore.js
index 3fa361fd3..aa3d0d084 100644
--- a/stores/RootStore.js
+++ b/stores/RootStore.js
@@ -11,6 +11,12 @@ import SettingStore from "./SettingStore";
export default class RootStore {
@ignore
storeLoaded = false
+
serverStore = new ServerStore()
settingStore = new SettingStore()
+
+ reset() {
+ this.serverStore.reset();
+ this.settingStore.reset();
+ }
}
diff --git a/stores/ServerStore.js b/stores/ServerStore.js
index 3dbfdf420..546c033d8 100644
--- a/stores/ServerStore.js
+++ b/stores/ServerStore.js
@@ -4,18 +4,35 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { action, observable } from 'mobx';
+import { format } from 'mobx-sync';
+import { task } from 'mobx-task';
+
+import ServerModel from '../models/ServerModel';
export default class ServerStore {
+ @format(data => data.map(value => new ServerModel(value.id, value.url, value.info)))
@observable
servers = []
@action
addServer(server) {
- this.servers.push(server);
+ this.servers.push(new ServerModel(this.servers.length, server.url));
}
@action
removeServer(index) {
this.servers.splice(index, 1);
}
+
+ @action
+ reset() {
+ this.servers = [];
+ }
+
+ @task
+ async fetchInfo() {
+ await Promise.all(
+ this.servers.map(server => server.fetchInfo())
+ );
+ }
}
diff --git a/stores/SettingStore.js b/stores/SettingStore.js
index 96a5f313c..8f1206bf4 100644
--- a/stores/SettingStore.js
+++ b/stores/SettingStore.js
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-import { observable } from 'mobx';
+import { action, observable } from 'mobx';
/**
* Data store for application settings
@@ -11,4 +11,9 @@ import { observable } from 'mobx';
export default class SettingStore {
@observable
activeServer = 0
+
+ @action
+ reset() {
+ this.activeServer = 0;
+ }
}
diff --git a/utils/Icons.js b/utils/Icons.js
new file mode 100644
index 000000000..2b5d993de
--- /dev/null
+++ b/utils/Icons.js
@@ -0,0 +1,13 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { Platform } from 'react-native';
+
+export const getIconName = (name = '') => {
+ if (name) {
+ return Platform.OS === 'ios' ? `ios-${name}` : `md-${name}`;
+ }
+ return name;
+};
diff --git a/utils/JellyfinValidator.js b/utils/JellyfinValidator.js
index e3d46fc20..9310525d6 100644
--- a/utils/JellyfinValidator.js
+++ b/utils/JellyfinValidator.js
@@ -35,7 +35,7 @@ export default class JellyfinValidator {
}
static async fetchServerInfo(server = {}) {
- const serverUrl = this.getServerUrl(server);
+ const serverUrl = server.urlString || this.getServerUrl(server);
const infoUrl = `${serverUrl}system/info/public`;
console.log('info url', infoUrl);
@@ -61,7 +61,7 @@ export default class JellyfinValidator {
static getServerUrl(server = {}) {
if (!server || !server.url || !server.url.href) {
- throw new Error(`Cannot get server url for invalid server ${server}`);
+ throw new Error('Cannot get server url for invalid server', server);
}
// Strip the query string or hash if present
diff --git a/yarn.lock b/yarn.lock
index e842763c3..51d52d7c0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9780,6 +9780,13 @@ mobx-sync@^3.0.0:
dependencies:
tslib "^2.0.0"
+mobx-task@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/mobx-task/-/mobx-task-2.0.1.tgz#bd9e335fd2f2cb6738eb6d83cae222d3b41c9d29"
+ integrity sha512-CWAqDYfNi6fKvdaPCO/qbns1VHKVF/yX5MezysieOwld4l+B77XFCDrP+E/W6W46gclPnyVRWllJ0fDYwC7S/g==
+ dependencies:
+ tslib "^1.9.3"
+
mobx@^5.15.4:
version "5.15.4"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-5.15.4.tgz#9da1a84e97ba624622f4e55a0bf3300fb931c2ab"