refactor: Browse & Browse Settings Screen
This commit is contained in:
@@ -22,8 +22,6 @@ const strings = fs.readFileSync(
|
||||
'utf8',
|
||||
);
|
||||
|
||||
console.log(flatten(JSON.parse(strings, '')));
|
||||
|
||||
const stringTypes = `/**\n * This file is auto-generated\n */\n\n
|
||||
|
||||
export interface StringMap ${JSON.stringify(flatten(JSON.parse(strings)))}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { StatusBar } from 'react-native';
|
||||
|
||||
import { Appbar as PaperAppbar } from 'react-native-paper';
|
||||
import { ThemeTypeV1 } from '../../theme/v1/theme/types';
|
||||
|
||||
interface AppbarProps {
|
||||
title: string;
|
||||
handleGoBack: () => void;
|
||||
theme: ThemeTypeV1;
|
||||
}
|
||||
|
||||
const Appbar: React.FC<AppbarProps> = ({ title, handleGoBack, theme }) => (
|
||||
<PaperAppbar.Header
|
||||
style={{ backgroundColor: theme.colorPrimary }}
|
||||
statusBarHeight={StatusBar.currentHeight}
|
||||
>
|
||||
{handleGoBack && <PaperAppbar.BackAction onPress={handleGoBack} />}
|
||||
<PaperAppbar.Content
|
||||
title={title}
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
/>
|
||||
</PaperAppbar.Header>
|
||||
);
|
||||
|
||||
export default Appbar;
|
||||
@@ -0,0 +1,109 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import { List as PaperList, Divider as PaperDivider } from 'react-native-paper';
|
||||
import { ThemeTypeV1 } from '../../theme/v1/theme/types';
|
||||
|
||||
interface ListItemProps {
|
||||
title: string;
|
||||
description?: string | null;
|
||||
icon?: string;
|
||||
onPress?: () => void;
|
||||
theme: ThemeTypeV1;
|
||||
}
|
||||
|
||||
const Section = ({ children }: { children: ReactNode }) => (
|
||||
<PaperList.Section style={styles.listSection}>{children}</PaperList.Section>
|
||||
);
|
||||
|
||||
const SubHeader = ({
|
||||
children,
|
||||
theme,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
theme: ThemeTypeV1;
|
||||
}) => (
|
||||
<PaperList.Subheader style={{ color: theme.colorAccent }}>
|
||||
{children}
|
||||
</PaperList.Subheader>
|
||||
);
|
||||
|
||||
const Item: React.FC<ListItemProps> = ({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
onPress,
|
||||
theme,
|
||||
}) => (
|
||||
<PaperList.Item
|
||||
title={title}
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
description={description}
|
||||
descriptionStyle={{ color: theme.textColorSecondary }}
|
||||
left={() => (
|
||||
<View style={styles.iconContainer}>
|
||||
{icon && (
|
||||
<PaperList.Icon
|
||||
color={theme.colorAccent}
|
||||
icon={icon}
|
||||
style={styles.icon}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
onPress={onPress}
|
||||
rippleColor={theme.rippleColor}
|
||||
/>
|
||||
);
|
||||
|
||||
const Divider = ({ theme }: { theme: ThemeTypeV1 }) => (
|
||||
<PaperDivider
|
||||
style={[styles.divider, { backgroundColor: theme.dividerColor }]}
|
||||
/>
|
||||
);
|
||||
|
||||
const InfoItem = ({
|
||||
title,
|
||||
icon,
|
||||
theme,
|
||||
}: {
|
||||
title: string;
|
||||
icon: string;
|
||||
theme: ThemeTypeV1;
|
||||
}) => (
|
||||
<PaperList.Item
|
||||
title={title}
|
||||
titleStyle={[{ color: theme.textColorSecondary }, styles.infoItem]}
|
||||
titleNumberOfLines={5}
|
||||
left={() =>
|
||||
icon && <PaperList.Icon color={theme.textColorSecondary} icon={icon} />
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export default {
|
||||
Section,
|
||||
SubHeader,
|
||||
Item,
|
||||
Divider,
|
||||
InfoItem,
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
listSection: {
|
||||
flex: 1,
|
||||
marginVertical: 0,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
},
|
||||
iconContainer: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
icon: {
|
||||
marginVertical: 0,
|
||||
},
|
||||
infoItem: {
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { Pressable, StyleSheet, View, Text } from 'react-native';
|
||||
|
||||
import { Switch } from 'react-native-paper';
|
||||
import { ThemeTypeV1 } from '../../theme/v1/theme/types';
|
||||
|
||||
interface SwitchItemProps {
|
||||
value: boolean;
|
||||
label: string;
|
||||
description?: string;
|
||||
onPress: () => void;
|
||||
theme: ThemeTypeV1;
|
||||
}
|
||||
|
||||
const SwitchItem: React.FC<SwitchItemProps> = ({
|
||||
label,
|
||||
description,
|
||||
onPress,
|
||||
theme,
|
||||
value,
|
||||
}) => (
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={[styles.container]}
|
||||
onPress={onPress}
|
||||
>
|
||||
<View style={styles.labelContainer}>
|
||||
<Text style={[{ color: theme.textColorPrimary }, styles.label]}>
|
||||
{label}
|
||||
</Text>
|
||||
{description ? (
|
||||
<Text style={{ color: theme.textColorSecondary }}>{description}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<Switch
|
||||
value={value}
|
||||
onValueChange={onPress}
|
||||
color={theme.colorAccent}
|
||||
style={styles.switch}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
|
||||
export default SwitchItem;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
},
|
||||
labelContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
label: {
|
||||
fontSize: 16,
|
||||
textAlignVertical: 'center',
|
||||
},
|
||||
switch: {
|
||||
marginLeft: 8,
|
||||
},
|
||||
});
|
||||
@@ -7,3 +7,6 @@ export { default as EmptyView } from './EmptyView/EmptyView';
|
||||
export { default as NovelList } from './NovelList/NovelList';
|
||||
export { default as Chip } from './Chip/Chip';
|
||||
export { default as Button } from './Button/Button';
|
||||
export { default as Appbar } from './Appbar/Appbar';
|
||||
export { default as SwitchItem } from './SwitchItem/SwitchItem';
|
||||
export { default as List } from './List/List';
|
||||
|
||||
@@ -19,7 +19,7 @@ import MoreStack from './MoreStack';
|
||||
*/
|
||||
import Novel from '../screens/novel/NovelScreen';
|
||||
import Reader from '../screens/reader/ReaderScreen';
|
||||
import Extension from '../screens/source/SourceScreen';
|
||||
import SourceScreen from '../screens/source/SourceScreen';
|
||||
import GlobalSearch from '../screens/browse/globalsearch/GlobalSearch';
|
||||
import Migration from '../screens/browse/migration/Migration';
|
||||
import SourceNovels from '../screens/browse/SourceNovels';
|
||||
@@ -27,7 +27,7 @@ import MigrateNovel from '../screens/browse/migration/MigrationNovels';
|
||||
|
||||
import MalTopNovels from '../screens/browse/discover/MalTopNovels';
|
||||
import NewUpdateDialog from '../components/NewUpdateDialog';
|
||||
import BrowseSettings from '../screens/browse/SettingsBrowseScreen';
|
||||
import BrowseSettings from '../screens/browse/BrowseSettings';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
@@ -53,7 +53,7 @@ const MainNavigator = () => {
|
||||
<Stack.Screen name="Novel" component={Novel} />
|
||||
<Stack.Screen name="Chapter" component={Reader} />
|
||||
<Stack.Screen name="MoreStack" component={MoreStack} />
|
||||
<Stack.Screen name="Extension" component={Extension} />
|
||||
<Stack.Screen name="SourceScreen" component={SourceScreen} />
|
||||
<Stack.Screen name="BrowseMal" component={MalTopNovels} />
|
||||
<Stack.Screen name="BrowseSettings" component={BrowseSettings} />
|
||||
<Stack.Screen name="GlobalSearch" component={GlobalSearch} />
|
||||
|
||||
@@ -15,15 +15,21 @@ export const useThemeV1 = () =>
|
||||
(state: RootState) => state.settingsReducer.theme,
|
||||
) as ThemeTypeV1;
|
||||
|
||||
export const useSettingsV1 = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducer);
|
||||
|
||||
export const useSettingsV2 = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducerV2);
|
||||
|
||||
export const useBrowseSettings = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducerV2.browse) || {};
|
||||
|
||||
export const useAppearanceSettings = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducerV2.appearance);
|
||||
|
||||
export const useReaderSettings = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducerV2.reader);
|
||||
|
||||
export const useSettingsV1 = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducer);
|
||||
|
||||
export const useReaderSettingsV1 = () =>
|
||||
useAppSelector((state: RootState) => state.settingsReducer.reader);
|
||||
|
||||
@@ -41,3 +47,6 @@ export const useSavedChapterData = (chapterId: number) =>
|
||||
|
||||
export const useDownloadQueue = () =>
|
||||
useAppSelector((state: RootState) => state.downloadsReducer.downloadQueue);
|
||||
|
||||
export const useSourcesReducer = () =>
|
||||
useAppSelector((state: RootState) => state.sourceReducerV2);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { StatusBar } from 'react-native';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { ColorScheme, ThemeType } from '../../theme/types';
|
||||
import { ThemeType } from '../../theme/types';
|
||||
import { defaultDarkTheme } from '../../theme/dark';
|
||||
import { BrowseSettingsMap } from './types';
|
||||
|
||||
export enum DisplayModes {
|
||||
Compact = 'compact',
|
||||
@@ -18,10 +18,10 @@ export enum TextAlignments {
|
||||
Right = 'right',
|
||||
}
|
||||
|
||||
interface ReaderTheme {
|
||||
backgroundColor: string;
|
||||
textColor: string;
|
||||
}
|
||||
// interface ReaderTheme {
|
||||
// backgroundColor: string;
|
||||
// textColor: string;
|
||||
// }
|
||||
|
||||
interface SettingsState {
|
||||
appearance: {
|
||||
@@ -64,6 +64,11 @@ interface SettingsState {
|
||||
incognitoMode: boolean;
|
||||
downloadOnlyMode: boolean;
|
||||
};
|
||||
browse: {
|
||||
onlyShowPinnedSources: boolean;
|
||||
showMyAnimeList: boolean;
|
||||
searchAllSources: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: SettingsState = {
|
||||
@@ -107,142 +112,30 @@ const initialState: SettingsState = {
|
||||
incognitoMode: false,
|
||||
downloadOnlyMode: false,
|
||||
},
|
||||
browse: {
|
||||
onlyShowPinnedSources: false,
|
||||
showMyAnimeList: true,
|
||||
searchAllSources: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const settingsSlice = createSlice({
|
||||
name: 'settingsReducerV2',
|
||||
initialState,
|
||||
reducers: {
|
||||
setTheme: (state, action: PayloadAction<ThemeType>) => {
|
||||
StatusBar.setBarStyle(
|
||||
action.payload.type === ColorScheme.DARK
|
||||
? 'light-content'
|
||||
: 'dark-content',
|
||||
);
|
||||
state.appearance.theme = action.payload;
|
||||
},
|
||||
setAmoledMode: state => {
|
||||
state.appearance.theme.background = '#000000';
|
||||
state.appearance.theme.surface = '#000000';
|
||||
state.appearance.theme.surfaceVariant = '#000000';
|
||||
},
|
||||
setDisplayMode: (state, action: PayloadAction<DisplayModes>) => {
|
||||
state.appearance.displayMode = action.payload;
|
||||
},
|
||||
setGridSizPotrait: (state, action: PayloadAction<number>) => {
|
||||
state.appearance.novelsPerRowPotrait = action.payload;
|
||||
},
|
||||
toggleShowUpdatesTab: state => {
|
||||
state.appearance.showUpdatesTab = !state.appearance.showUpdatesTab;
|
||||
},
|
||||
toggleShowHistoryTab: state => {
|
||||
state.appearance.showHistoryTab = !state.appearance.showHistoryTab;
|
||||
},
|
||||
toggleShowLabelsInNav: state => {
|
||||
state.appearance.showLabelsInNav = !state.appearance.showLabelsInNav;
|
||||
},
|
||||
setReaderTheme: (state, action: PayloadAction<ReaderTheme>) => {
|
||||
state.reader.backgroundColor = action.payload.backgroundColor;
|
||||
state.reader.textColor = action.payload.textColor;
|
||||
},
|
||||
setReaderFont: (state, action: PayloadAction<{ fontFamily: string }>) => {
|
||||
state.reader.fontFamily = action.payload.fontFamily;
|
||||
},
|
||||
setReaderPadding: (
|
||||
setBrowseSettings: (
|
||||
state,
|
||||
action: PayloadAction<{ paddingHorizontal: number }>,
|
||||
action: PayloadAction<{ key: keyof BrowseSettingsMap; value: any }>,
|
||||
) => {
|
||||
state.reader.paddingHorizontal = action.payload.paddingHorizontal;
|
||||
},
|
||||
setReaderFontSize: (state, action: PayloadAction<{ fontSize: number }>) => {
|
||||
state.reader.fontSize = action.payload.fontSize;
|
||||
},
|
||||
setReaderLineHeight: (
|
||||
state,
|
||||
action: PayloadAction<{ lineHeight: number }>,
|
||||
) => {
|
||||
state.reader.lineHeight = action.payload.lineHeight;
|
||||
},
|
||||
setCustomCSS: (state, action: PayloadAction<{ customCSS: string }>) => {
|
||||
state.reader.customCSS = action.payload.customCSS;
|
||||
},
|
||||
toggleReaderFullScreenMode: state => {
|
||||
state.reader.fullScreenMode = !state.reader.fullScreenMode;
|
||||
},
|
||||
toggleWebViewReader: state => {
|
||||
state.reader.useWebViewReader = !state.reader.useWebViewReader;
|
||||
},
|
||||
setLibraryFilter: (state, action: PayloadAction<{ filter: string }>) => {
|
||||
if (state.library.filters.includes(action.payload.filter)) {
|
||||
state.library.filters = state.library.filters.filter(
|
||||
item => item !== action.payload.filter,
|
||||
);
|
||||
} else {
|
||||
state.library.filters = [
|
||||
...state.library.filters,
|
||||
action.payload.filter,
|
||||
];
|
||||
if (state?.browse === undefined) {
|
||||
state.browse = initialState.browse;
|
||||
}
|
||||
},
|
||||
setLibrarySortOrder: (state, action: PayloadAction<{ sort: string }>) => {
|
||||
state.library.sort = action.payload.sort;
|
||||
},
|
||||
toggleShowUnreadBadge: state => {
|
||||
state.library.showUnreadBadge = !state.library.showUnreadBadge;
|
||||
},
|
||||
toggleShowDownloadsBadge: state => {
|
||||
state.library.showDownloadsBadge = !state.library.showDownloadsBadge;
|
||||
},
|
||||
toggleShowProgressPercentage: state => {
|
||||
state.reader.showProgressPercentage =
|
||||
!state.reader.showProgressPercentage;
|
||||
},
|
||||
toggleUpdateLibraryOnLaunch: state => {
|
||||
state.library.updateLibraryOnLaunch =
|
||||
!state.library.updateLibraryOnLaunch;
|
||||
},
|
||||
toggleUpdateNovelMetadata: state => {
|
||||
state.updates.updateNovelMetadata = !state.updates.updateNovelMetadata;
|
||||
},
|
||||
toggleOnlyUpdateOngoingNovels: state => {
|
||||
state.updates.onlyUpdateOngoingNovels =
|
||||
!state.updates.onlyUpdateOngoingNovels;
|
||||
},
|
||||
toggleShowLastUpdateTime: state => {
|
||||
state.updates.showLastUpdateTime = !state.updates.showLastUpdateTime;
|
||||
},
|
||||
toggleUseSwipeGestures: state => {
|
||||
state.reader.useSwipeGestures = !state.reader.useSwipeGestures;
|
||||
|
||||
state.browse[action.payload.key] = action.payload.value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setTheme,
|
||||
setDisplayMode,
|
||||
toggleShowUpdatesTab,
|
||||
toggleShowHistoryTab,
|
||||
toggleShowLabelsInNav,
|
||||
setReaderTheme,
|
||||
setReaderFont,
|
||||
setReaderPadding,
|
||||
setReaderFontSize,
|
||||
setReaderLineHeight,
|
||||
setCustomCSS,
|
||||
toggleReaderFullScreenMode,
|
||||
setLibraryFilter,
|
||||
setLibrarySortOrder,
|
||||
toggleShowUnreadBadge,
|
||||
toggleShowDownloadsBadge,
|
||||
setAmoledMode,
|
||||
toggleWebViewReader,
|
||||
toggleShowProgressPercentage,
|
||||
setGridSizPotrait,
|
||||
toggleUpdateLibraryOnLaunch,
|
||||
toggleOnlyUpdateOngoingNovels,
|
||||
toggleUpdateNovelMetadata,
|
||||
toggleShowLastUpdateTime,
|
||||
toggleUseSwipeGestures,
|
||||
} = settingsSlice.actions;
|
||||
export const { setBrowseSettings } = settingsSlice.actions;
|
||||
|
||||
export default settingsSlice.reducer;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// export interface SettingsMap {
|
||||
// 'app.downloadOnlyMode': 'string';
|
||||
// 'app.incognitoMode': 'string';
|
||||
// 'appearance.displayMode': 'string';
|
||||
// 'appearance.novelsPerRowLandscape': 'string';
|
||||
// 'appearance.novelsPerRowPotrait': 'string';
|
||||
// 'appearance.showHistoryTab': 'string';
|
||||
// 'appearance.showLabelsInNav': 'string';
|
||||
// 'appearance.showUpdatesTab': 'string';
|
||||
// 'appearance.theme': 'string';
|
||||
// 'library.showDownloadsBadge': 'string';
|
||||
// 'library.showUnreadBadge': 'string';
|
||||
// 'library.sort': 'string';
|
||||
// 'library.updateLibraryOnLaunch': 'string';
|
||||
// 'reader.backgroundColor': 'string';
|
||||
// 'reader.customCSS': 'string';
|
||||
// 'reader.fontFamily': 'string';
|
||||
// 'reader.fontSize': 'string';
|
||||
// 'reader.fullScreenMode': 'string';
|
||||
// 'reader.lineHeight': 'string';
|
||||
// 'reader.paddingHorizontal': 'string';
|
||||
// 'reader.showProgressPercentage': 'string';
|
||||
// 'reader.textAlign': 'string';
|
||||
// 'reader.textColor': 'string';
|
||||
// 'reader.useSwipeGestures': 'string';
|
||||
// 'reader.useWebViewReader': 'string';
|
||||
// 'updates.lastUpdateTime': 'string';
|
||||
// 'updates.onlyUpdateOngoingNovels': 'string';
|
||||
// 'updates.showLastUpdateTime': 'string';
|
||||
// 'updates.updateNovelMetadata': 'string';
|
||||
// }
|
||||
|
||||
export interface BrowseSettingsMap {
|
||||
onlyShowPinnedSources: 'string';
|
||||
searchAllSources: 'string';
|
||||
showMyAnimeList: 'string';
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import sourceList from '../../sources/sources.json';
|
||||
import AllSources from '../../sources/sources.json';
|
||||
|
||||
import { Source } from '../../sources/types';
|
||||
|
||||
interface SourcesState {
|
||||
allSources: Source[];
|
||||
pinnedSources: number[];
|
||||
pinnedSourceIds: number[];
|
||||
lastUsed: number | null;
|
||||
languageFilters: string[];
|
||||
}
|
||||
|
||||
const initialState: SourcesState = {
|
||||
allSources: [],
|
||||
pinnedSources: [],
|
||||
allSources: AllSources,
|
||||
pinnedSourceIds: [],
|
||||
lastUsed: null,
|
||||
languageFilters: ['English'],
|
||||
};
|
||||
@@ -22,37 +22,39 @@ export const sourcesSlice = createSlice({
|
||||
initialState,
|
||||
reducers: {
|
||||
getSourcesAction: state => {
|
||||
let sources: Source[] = sourceList;
|
||||
|
||||
sources = sources.sort((a, b) =>
|
||||
a.sourceName.localeCompare(b.sourceName),
|
||||
);
|
||||
|
||||
sources = sources.filter(
|
||||
const sources = AllSources.filter(
|
||||
source => state.languageFilters.indexOf(source.lang) !== -1,
|
||||
);
|
||||
).sort((a, b) => a.sourceName.localeCompare(b.sourceName));
|
||||
|
||||
state.allSources = sources;
|
||||
},
|
||||
searchSourcesAction: (state, action: PayloadAction<string>) => {
|
||||
state.allSources = sourceList.filter(
|
||||
(source: Source) =>
|
||||
source.sourceName
|
||||
.toLowerCase()
|
||||
.includes(action.payload.toLowerCase()) &&
|
||||
state.languageFilters.indexOf(source.lang) !== -1,
|
||||
);
|
||||
state.allSources = AllSources.filter(
|
||||
source => state.languageFilters.indexOf(source.lang) !== -1,
|
||||
)
|
||||
.sort((a, b) => a.sourceName.localeCompare(b.sourceName))
|
||||
.filter(
|
||||
(source: Source) =>
|
||||
source.sourceName
|
||||
.toLowerCase()
|
||||
.includes(action.payload.toLowerCase()) &&
|
||||
state.languageFilters.indexOf(source.lang) !== -1,
|
||||
);
|
||||
},
|
||||
setLastUsedSource: (state, action: PayloadAction<{ sourceId: number }>) => {
|
||||
state.lastUsed = action.payload.sourceId;
|
||||
},
|
||||
togglePinSource: (state, action: PayloadAction<number>) => {
|
||||
if (state.pinnedSources.indexOf(action.payload) > -1) {
|
||||
state.pinnedSources = state.pinnedSources.filter(
|
||||
if (state?.pinnedSourceIds === undefined) {
|
||||
state.pinnedSourceIds = [];
|
||||
}
|
||||
|
||||
if (state.pinnedSourceIds?.indexOf(action.payload) > -1) {
|
||||
state.pinnedSourceIds = state.pinnedSourceIds.filter(
|
||||
source => source !== action.payload,
|
||||
);
|
||||
} else {
|
||||
state.pinnedSources = [...state.pinnedSources, action.payload];
|
||||
state.pinnedSourceIds = [...state.pinnedSourceIds, action.payload];
|
||||
}
|
||||
},
|
||||
toggleLanguageFilter: (
|
||||
|
||||
@@ -7,6 +7,7 @@ import settingsReducerV2 from './settings/settingsSlice';
|
||||
import localStorageReducer from './localStorage/localStorageSlice';
|
||||
import libraryReducer from './library/library.reducer';
|
||||
import sourceReducer from './source/source.reducers';
|
||||
import sourceReducerV2 from './source/sourcesSlice';
|
||||
import novelReducer from './novel/novel.reducer';
|
||||
import updatesReducer from './updates/updates.reducer';
|
||||
import trackerReducer from './tracker/tracker.reducer';
|
||||
@@ -26,6 +27,7 @@ const reducers = combineReducers({
|
||||
localStorageReducer,
|
||||
libraryReducer,
|
||||
sourceReducer,
|
||||
sourceReducerV2,
|
||||
novelReducer,
|
||||
updatesReducer,
|
||||
trackerReducer,
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Text,
|
||||
FlatList,
|
||||
ActivityIndicator,
|
||||
RefreshControl,
|
||||
} from 'react-native';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import {
|
||||
getSourcesAction,
|
||||
pinSourceAction,
|
||||
searchSourcesAction,
|
||||
} from '../../redux/source/source.actions';
|
||||
import { useSettings, useTheme } from '../../hooks/reduxHooks';
|
||||
import { showToast } from '../../hooks/showToast';
|
||||
|
||||
import { Searchbar } from '../../components/Searchbar/Searchbar';
|
||||
import EmptyView from '../../components/EmptyView';
|
||||
import SourceCard from './SourceCard/SourceCard';
|
||||
import MalCard from './discover/MalCard/MalCard';
|
||||
import { getString } from '../../../strings/translations';
|
||||
|
||||
const Browse = ({ navigation }) => {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
let {
|
||||
sources,
|
||||
search,
|
||||
loading,
|
||||
pinned,
|
||||
filters = [],
|
||||
showMyAnimeList = true,
|
||||
} = useSelector(state => state.sourceReducer);
|
||||
|
||||
const { onlyShowPinnedSources = false } = useSettings();
|
||||
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isPinned = sourceId => (pinned.indexOf(sourceId) === -1 ? false : true);
|
||||
|
||||
sources = sources.filter(source => filters.indexOf(source.lang) === -1);
|
||||
search = search.filter(source => filters.indexOf(source.lang) === -1);
|
||||
|
||||
let pinnedSources = sources.filter(source => isPinned(source.sourceId));
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getSourcesAction());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const onRefresh = () => {
|
||||
showToast('Updating extension list');
|
||||
setRefreshing(true);
|
||||
getSourcesAction();
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
const clearSearchbar = () => {
|
||||
setSearchText('');
|
||||
};
|
||||
|
||||
const onChangeText = text => {
|
||||
setSearchText(text);
|
||||
dispatch(searchSourcesAction(text));
|
||||
};
|
||||
|
||||
const navigateToSource = useCallback(
|
||||
source =>
|
||||
navigation.navigate('Extension', {
|
||||
sourceId: source.sourceId,
|
||||
sourceName: source.sourceName,
|
||||
url: source.url,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
);
|
||||
|
||||
const renderItem = ({ item }) => (
|
||||
<SourceCard
|
||||
source={item}
|
||||
isPinned={isPinned(item.sourceId)}
|
||||
navigateToSource={navigateToSource}
|
||||
onTogglePinSource={sourceId => dispatch(pinSourceAction(sourceId))}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
|
||||
const Header = ({ title }) => (
|
||||
<Text style={[styles.header, { color: theme.textColorSecondary }]}>
|
||||
{title}
|
||||
</Text>
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[styles.container, { backgroundColor: theme.colorPrimaryDark }]}
|
||||
>
|
||||
<Searchbar
|
||||
theme={theme}
|
||||
placeholder={getString('browseScreen.searchbar')}
|
||||
backAction="magnify"
|
||||
searchText={searchText}
|
||||
onChangeText={onChangeText}
|
||||
clearSearchbar={clearSearchbar}
|
||||
actions={[
|
||||
{
|
||||
icon: 'book-search',
|
||||
onPress: () => navigation.navigate('GlobalSearch'),
|
||||
},
|
||||
{
|
||||
icon: 'swap-vertical-variant',
|
||||
onPress: () => navigation.navigate('Migration'),
|
||||
},
|
||||
{
|
||||
icon: 'cog-outline',
|
||||
onPress: () => navigation.navigate('BrowseSettings'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FlatList
|
||||
contentContainerStyle={{ flexGrow: 1, paddingBottom: 32 }}
|
||||
data={onlyShowPinnedSources ? [] : !searchText ? sources : search}
|
||||
keyExtractor={item => item.sourceId.toString()}
|
||||
renderItem={renderItem}
|
||||
extraData={pinnedSources}
|
||||
ListHeaderComponent={
|
||||
<View>
|
||||
{showMyAnimeList && (
|
||||
<>
|
||||
<Header title="Discover" />
|
||||
{showMyAnimeList && <MalCard theme={theme} />}
|
||||
</>
|
||||
)}
|
||||
{pinnedSources.length > 0 && (
|
||||
<FlatList
|
||||
contentContainerStyle={{ paddingBottom: 16 }}
|
||||
data={pinnedSources}
|
||||
keyExtractor={item => item.sourceId.toString()}
|
||||
renderItem={renderItem}
|
||||
extraData={pinnedSources}
|
||||
ListHeaderComponent={<Header title="Pinned" />}
|
||||
/>
|
||||
)}
|
||||
{sources.length > 0 && !onlyShowPinnedSources && (
|
||||
<Header title="Sources" />
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
ListEmptyComponent={
|
||||
loading ? (
|
||||
<ActivityIndicator
|
||||
size="small"
|
||||
color={theme.colorAccent}
|
||||
style={{ marginTop: 16 }}
|
||||
/>
|
||||
) : (
|
||||
sources.length === 0 && (
|
||||
<EmptyView
|
||||
icon="(・Д・。"
|
||||
description="Enable languages from settings"
|
||||
/>
|
||||
)
|
||||
)
|
||||
}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={['white']}
|
||||
progressBackgroundColor={theme.colorAccent}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Browse;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
import { SectionList, StyleSheet, Text } from 'react-native';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
|
||||
import { EmptyView, SearchbarV2 } from '../../components';
|
||||
import {
|
||||
getSourcesAction,
|
||||
searchSourcesAction,
|
||||
setLastUsedSource,
|
||||
togglePinSource,
|
||||
} from '../../redux/source/sourcesSlice';
|
||||
import {
|
||||
useAppDispatch,
|
||||
useSourcesReducer,
|
||||
useThemeV1,
|
||||
} from '../../redux/hooks';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useSearch } from '../../hooks';
|
||||
import SourceCard from './components/SourceCard/SourceCard';
|
||||
import { getString } from '../../../strings/translations';
|
||||
import { Source } from '../../sources/types';
|
||||
|
||||
const BrowseScreen = () => {
|
||||
const { navigate } = useNavigation();
|
||||
const theme = useThemeV1();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { searchText, setSearchText, clearSearchbar } = useSearch();
|
||||
|
||||
const onChangeText = (text: string) => {
|
||||
setSearchText(text);
|
||||
dispatch(searchSourcesAction(text));
|
||||
};
|
||||
|
||||
const handleClearSearchbar = () => {
|
||||
clearSearchbar();
|
||||
dispatch(getSourcesAction());
|
||||
};
|
||||
|
||||
const {
|
||||
allSources,
|
||||
pinnedSourceIds = [],
|
||||
languageFilters = ['English'],
|
||||
lastUsed,
|
||||
} = useSourcesReducer();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getSourcesAction());
|
||||
}, [dispatch, languageFilters]);
|
||||
|
||||
const isPinned = (sourceId: number) => pinnedSourceIds.indexOf(sourceId) > -1;
|
||||
|
||||
const pinnedSources = allSources.filter(source => isPinned(source.sourceId));
|
||||
const lastUsedSource = lastUsed
|
||||
? allSources.filter(source => source.sourceId === lastUsed)
|
||||
: [];
|
||||
|
||||
const navigateToSource = useCallback((source: Source) => {
|
||||
navigate(
|
||||
'SourceScreen' as never,
|
||||
{
|
||||
sourceId: source.sourceId,
|
||||
sourceName: source.sourceName,
|
||||
url: source.url,
|
||||
} as never,
|
||||
);
|
||||
dispatch(setLastUsedSource({ sourceId: source.sourceId }));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchbarV2
|
||||
searchText={searchText}
|
||||
placeholder="Search sources"
|
||||
leftIcon="magnify"
|
||||
onChangeText={onChangeText}
|
||||
clearSearchbar={handleClearSearchbar}
|
||||
theme={theme}
|
||||
rightIcons={[
|
||||
{
|
||||
iconName: 'book-search',
|
||||
onPress: () => navigate('GlobalSearch' as never),
|
||||
},
|
||||
{
|
||||
iconName: 'swap-vertical-variant',
|
||||
onPress: () => navigate('Migration' as never),
|
||||
},
|
||||
{
|
||||
iconName: 'cog-outline',
|
||||
onPress: () => navigate('BrowseSettings' as never),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{allSources.length === 0 ? (
|
||||
<EmptyView
|
||||
icon="(・Д・。"
|
||||
description={getString('browseScreen.listEmpty')}
|
||||
theme={theme}
|
||||
/>
|
||||
) : (
|
||||
<SectionList
|
||||
sections={[
|
||||
{
|
||||
header: getString('browseScreen.lastUsed'),
|
||||
data: lastUsedSource,
|
||||
},
|
||||
{ header: getString('browseScreen.pinned'), data: pinnedSources },
|
||||
{ header: getString('browseScreen.all'), data: allSources },
|
||||
]}
|
||||
keyExtractor={(_, index) => index.toString()}
|
||||
renderSectionHeader={({ section: { header, data } }) =>
|
||||
data.length > 0 ? (
|
||||
<Text
|
||||
style={[
|
||||
styles.sectionHeader,
|
||||
{ color: theme.textColorSecondary },
|
||||
]}
|
||||
>
|
||||
{header}
|
||||
</Text>
|
||||
) : null
|
||||
}
|
||||
renderItem={({ item }) => (
|
||||
<SourceCard
|
||||
source={item}
|
||||
isPinned={isPinned(item.sourceId)}
|
||||
navigateToSource={navigateToSource}
|
||||
onTogglePinSource={sourceId =>
|
||||
dispatch(togglePinSource(sourceId))
|
||||
}
|
||||
theme={theme}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrowseScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
sectionHeader: {
|
||||
fontSize: 14,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
import { FlatList, StyleSheet } from 'react-native';
|
||||
import React from 'react';
|
||||
import { Appbar, List, SwitchItem } from '../../components';
|
||||
|
||||
import {
|
||||
useAppDispatch,
|
||||
useBrowseSettings,
|
||||
useSourcesReducer,
|
||||
useThemeV1,
|
||||
} from '../../redux/hooks';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { getString } from '../../../strings/translations';
|
||||
import { languages } from '../../utils/constants/languages';
|
||||
import { toggleLanguageFilter } from '../../redux/source/sourcesSlice';
|
||||
import { setBrowseSettings } from '../../redux/settings/settingsSlice';
|
||||
|
||||
const BrowseSettings = () => {
|
||||
const theme = useThemeV1();
|
||||
const { goBack } = useNavigation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { languageFilters } = useSourcesReducer();
|
||||
const {
|
||||
searchAllSources = false,
|
||||
showMyAnimeList = true,
|
||||
onlyShowPinnedSources = false,
|
||||
} = useBrowseSettings();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar
|
||||
title={getString('moreScreen.settingsScreen.browseSettings')}
|
||||
handleGoBack={goBack}
|
||||
theme={theme}
|
||||
/>
|
||||
<FlatList
|
||||
contentContainerStyle={styles.container}
|
||||
ListHeaderComponent={
|
||||
<>
|
||||
<List.SubHeader theme={theme}>
|
||||
{getString('browseScreen.globalSearch')}
|
||||
</List.SubHeader>
|
||||
<SwitchItem
|
||||
label={getString(
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.searchAllSources',
|
||||
)}
|
||||
value={searchAllSources}
|
||||
onPress={() =>
|
||||
dispatch(
|
||||
setBrowseSettings({
|
||||
key: 'searchAllSources',
|
||||
value: !searchAllSources,
|
||||
}),
|
||||
)
|
||||
}
|
||||
theme={theme}
|
||||
/>
|
||||
<List.InfoItem
|
||||
title={getString(
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.searchAllWarning',
|
||||
)}
|
||||
icon="information-outline"
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>
|
||||
{getString('browseScreen.discover')}
|
||||
</List.SubHeader>
|
||||
<SwitchItem
|
||||
label="Show MyAnimeList"
|
||||
value={showMyAnimeList}
|
||||
onPress={() =>
|
||||
dispatch(
|
||||
setBrowseSettings({
|
||||
key: 'showMyAnimeList',
|
||||
value: !showMyAnimeList,
|
||||
}),
|
||||
)
|
||||
}
|
||||
theme={theme}
|
||||
/>
|
||||
<SwitchItem
|
||||
label={getString(
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.onlyShowPinnedSources',
|
||||
)}
|
||||
value={onlyShowPinnedSources}
|
||||
onPress={() =>
|
||||
dispatch(
|
||||
setBrowseSettings({
|
||||
key: 'onlyShowPinnedSources',
|
||||
value: !onlyShowPinnedSources,
|
||||
}),
|
||||
)
|
||||
}
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>
|
||||
{getString(
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.languages',
|
||||
)}
|
||||
</List.SubHeader>
|
||||
</>
|
||||
}
|
||||
keyExtractor={item => item}
|
||||
data={languages}
|
||||
renderItem={({ item }) => (
|
||||
<SwitchItem
|
||||
label={item}
|
||||
value={languageFilters.indexOf(item) !== -1}
|
||||
onPress={() => dispatch(toggleLanguageFilter({ language: item }))}
|
||||
theme={theme}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrowseSettings;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
paddingBottom: 40,
|
||||
},
|
||||
});
|
||||
@@ -1,164 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
import { Switch } from 'react-native-paper';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { Appbar } from '../../components/Appbar';
|
||||
import { List } from '../../components/List';
|
||||
|
||||
import { useSettings, useTheme } from '../../hooks/reduxHooks';
|
||||
import { setAppSettings } from '../../redux/settings/settings.actions';
|
||||
import {
|
||||
enableDiscover,
|
||||
filterLanguage,
|
||||
} from '../../redux/source/source.actions';
|
||||
|
||||
const BrowseSettings = ({ navigation }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
|
||||
const { filters = [], showMyAnimeList = true } = useSelector(
|
||||
state => state.sourceReducer,
|
||||
);
|
||||
|
||||
const languages = useMemo(
|
||||
() =>
|
||||
[
|
||||
'English',
|
||||
'Spanish',
|
||||
'Japanese',
|
||||
'Chinese',
|
||||
'Arabic',
|
||||
'Indonesian',
|
||||
'Russian',
|
||||
'Turkish',
|
||||
'French',
|
||||
'Portuguese (Brazil)',
|
||||
'Vietnamese',
|
||||
].sort(),
|
||||
[],
|
||||
);
|
||||
|
||||
const { searchAllSources = false, onlyShowPinnedSources = false } =
|
||||
useSettings();
|
||||
|
||||
const renderItem = ({ item }) => {
|
||||
return (
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={styles.switch}
|
||||
onPress={() => dispatch(filterLanguage(item))}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>{item}</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={filters.indexOf(item) === -1 ? true : false}
|
||||
onValueChange={() => dispatch(filterLanguage(item))}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Appbar onBackAction={navigation.goBack} title="Browse" />
|
||||
<FlatList
|
||||
contentContainerStyle={{ paddingBottom: bottom }}
|
||||
data={languages}
|
||||
keyExtractor={item => item}
|
||||
renderItem={renderItem}
|
||||
ListHeaderComponent={
|
||||
<View>
|
||||
<List.SubHeader theme={theme}>Global Search</List.SubHeader>
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={styles.switch}
|
||||
onPress={() =>
|
||||
dispatch(setAppSettings('searchAllSources', !searchAllSources))
|
||||
}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>
|
||||
Search all sources
|
||||
</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={searchAllSources}
|
||||
onValueChange={() =>
|
||||
dispatch(
|
||||
setAppSettings('searchAllSources', !searchAllSources),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
<List.InfoItem
|
||||
title="Searching a large number of sources may freeze the app till searching is finished."
|
||||
icon="information-outline"
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>Browse</List.SubHeader>
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={styles.switch}
|
||||
onPress={() => dispatch(enableDiscover('showMyAnimeList'))}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>MyAnimeList</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={showMyAnimeList}
|
||||
onValueChange={() =>
|
||||
dispatch(enableDiscover('showMyAnimeList'))
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
style={styles.switch}
|
||||
onPress={() =>
|
||||
dispatch(
|
||||
setAppSettings(
|
||||
'onlyShowPinnedSources',
|
||||
!onlyShowPinnedSources,
|
||||
),
|
||||
)
|
||||
}
|
||||
>
|
||||
<Text style={{ color: theme.textColorPrimary }}>
|
||||
Only show pinned sources
|
||||
</Text>
|
||||
<Switch
|
||||
color={theme.colorAccent}
|
||||
value={onlyShowPinnedSources}
|
||||
onValueChange={() =>
|
||||
dispatch(
|
||||
setAppSettings(
|
||||
'onlyShowPinnedSources',
|
||||
!onlyShowPinnedSources,
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>Languages</List.SubHeader>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrowseSettings;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
switch: {
|
||||
padding: 16,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
});
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View, Image, Pressable } from 'react-native';
|
||||
import { getString } from '../../../../strings/translations';
|
||||
import { Button, IconButtonV2 } from '../../../components';
|
||||
import { ButtonVariation } from '../../../components/Button/Button';
|
||||
import { getString } from '../../../../../strings/translations';
|
||||
import { Button, IconButtonV2 } from '../../../../components';
|
||||
import { ButtonVariation } from '../../../../components/Button/Button';
|
||||
|
||||
import { Source } from '../../../sources/types';
|
||||
import { ThemeTypeV1 } from '../../../theme/v1/theme/types';
|
||||
import { Source } from '../../../../sources/types';
|
||||
import { ThemeTypeV1 } from '../../../../theme/v1/theme/types';
|
||||
|
||||
interface Props {
|
||||
source: Source;
|
||||
@@ -0,0 +1,13 @@
|
||||
export const languages = [
|
||||
'Arabic',
|
||||
'Chinese',
|
||||
'English',
|
||||
'French',
|
||||
'Indonesian',
|
||||
'Japanese',
|
||||
'Portuguese (Brazil)',
|
||||
'Russian',
|
||||
'Spanish',
|
||||
'Turkish',
|
||||
'Vietnamese',
|
||||
];
|
||||
@@ -38,7 +38,13 @@
|
||||
},
|
||||
"browse": "Browse",
|
||||
"browseScreen": {
|
||||
"searchbar": "Search sources"
|
||||
"discover": "Discover",
|
||||
"searchbar": "Search sources",
|
||||
"globalSearch": "Global search",
|
||||
"listEmpty": "Enable languages from settings",
|
||||
"lastUsed": "Last used",
|
||||
"pinned": "Pinned",
|
||||
"all": "All"
|
||||
},
|
||||
"novelScreen": {
|
||||
"addToLibaray": "Add to library",
|
||||
@@ -59,7 +65,14 @@
|
||||
"generalSettings": "General",
|
||||
"generalSettingsScreen": {},
|
||||
"readerSettings": "Reader",
|
||||
"readerSettingsScreen": {}
|
||||
"readerSettingsScreen": {},
|
||||
"browseSettings": "Browse Settings",
|
||||
"browseSettingsScreen": {
|
||||
"onlyShowPinnedSources": "Only show pinned sources",
|
||||
"languages": "Languages",
|
||||
"searchAllSources": "Search all sources",
|
||||
"searchAllWarning": "Searching a large number of sources may freeze the app till searching is finished."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,13 @@ export interface StringMap {
|
||||
'history': 'string';
|
||||
'historyScreen.searchbar': 'string';
|
||||
'browse': 'string';
|
||||
'browseScreen.discover': 'string';
|
||||
'browseScreen.searchbar': 'string';
|
||||
'browseScreen.globalSearch': 'string';
|
||||
'browseScreen.listEmpty': 'string';
|
||||
'browseScreen.lastUsed': 'string';
|
||||
'browseScreen.pinned': 'string';
|
||||
'browseScreen.all': 'string';
|
||||
'novelScreen.addToLibaray': 'string';
|
||||
'novelScreen.inLibaray': 'string';
|
||||
'novelScreen.continueReading': 'string';
|
||||
@@ -40,4 +46,9 @@ export interface StringMap {
|
||||
'moreScreen.settings': 'string';
|
||||
'moreScreen.settingsScreen.generalSettings': 'string';
|
||||
'moreScreen.settingsScreen.readerSettings': 'string';
|
||||
'moreScreen.settingsScreen.browseSettings': 'string';
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.onlyShowPinnedSources': 'string';
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.languages': 'string';
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.searchAllSources': 'string';
|
||||
'moreScreen.settingsScreen.browseSettingsScreen.searchAllWarning': 'string';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user