Fix tabiew (#939)
* fix: tabview look * fix: error when library is not fetched fully * fix: legacy backup * fix: handle error for plugin methods
This commit is contained in:
@@ -16,7 +16,7 @@ import { getString } from '@strings/translations';
|
||||
const db = SQLite.openDatabase('lnreader.db');
|
||||
|
||||
const insertChapterQuery = `
|
||||
INSERT INTO Chapter (
|
||||
INSERT OR IGNORE INTO Chapter (
|
||||
url, name, releaseTime, novelId, chapterNumber
|
||||
)
|
||||
Values
|
||||
@@ -42,7 +42,6 @@ export const insertChapters = async (
|
||||
chapter.chapterNumber || null,
|
||||
],
|
||||
noop,
|
||||
txnErrorCallback,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ const db = SQLite.openDatabase('lnreader.db');
|
||||
import * as DocumentPicker from 'expo-document-picker';
|
||||
import * as RNFS from 'react-native-fs';
|
||||
|
||||
import { fetchChapters, fetchImage, fetchNovel } from '@services/plugin/fetch';
|
||||
import { fetchImage, fetchNovel } from '@services/plugin/fetch';
|
||||
import { insertChapters } from './ChapterQueries';
|
||||
|
||||
import { showToast } from '@utils/showToast';
|
||||
@@ -183,15 +183,18 @@ export const deleteCachedNovels = async () => {
|
||||
};
|
||||
|
||||
const restoreFromBackupQuery =
|
||||
'INSERT INTO Novel (url, name, pluginId, cover, summary, author, artist, status, genres, inLibrary) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
'INSERT OR REPLACE INTO Novel (url, name, pluginId, cover, summary, author, artist, status, genres) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
|
||||
export const restoreLibrary = async (novel: NovelInfo) => {
|
||||
return new Promise(resolve => {
|
||||
const sourceNovel = await fetchNovel(novel.pluginId, novel.url).catch(e => {
|
||||
throw e;
|
||||
});
|
||||
const novelId: number | undefined = await new Promise(resolve => {
|
||||
db.transaction(tx =>
|
||||
tx.executeSql(
|
||||
restoreFromBackupQuery,
|
||||
[
|
||||
novel.url,
|
||||
sourceNovel.url,
|
||||
novel.name,
|
||||
novel.pluginId,
|
||||
novel.cover || '',
|
||||
@@ -200,29 +203,36 @@ export const restoreLibrary = async (novel: NovelInfo) => {
|
||||
novel.artist || '',
|
||||
novel.status || '',
|
||||
novel.genres || '',
|
||||
Number(novel.inLibrary),
|
||||
],
|
||||
async (txObj, { insertId }) => {
|
||||
if (!insertId) {
|
||||
return;
|
||||
}
|
||||
tx.executeSql(
|
||||
'INSERT INTO NovelCategory (novelId, categoryId) VALUES (?, (SELECT DISTINCT id FROM Category WHERE sort = 1))',
|
||||
[insertId],
|
||||
noop,
|
||||
txnErrorCallback,
|
||||
);
|
||||
const chapters = await fetchChapters(novel.pluginId, novel.url);
|
||||
|
||||
if (chapters) {
|
||||
await insertChapters(insertId, chapters);
|
||||
resolve(insertId);
|
||||
}
|
||||
},
|
||||
txnErrorCallback,
|
||||
async (txObj, { insertId }) => resolve(insertId),
|
||||
),
|
||||
);
|
||||
});
|
||||
if (novelId && novelId > 0) {
|
||||
await new Promise((resolve, reject) => {
|
||||
db.transaction(async tx => {
|
||||
tx.executeSql(
|
||||
'INSERT OR REPLACE INTO NovelCategory (novelId, categoryId) VALUES (?, (SELECT DISTINCT id FROM Category WHERE sort = 1))',
|
||||
[novelId],
|
||||
() => {
|
||||
tx.executeSql('UPDATE Novel SET inLibrary = 1 WHERE id = ?', [
|
||||
novelId,
|
||||
]);
|
||||
resolve(null);
|
||||
},
|
||||
(txObj, err) => {
|
||||
reject(err);
|
||||
return false;
|
||||
},
|
||||
);
|
||||
});
|
||||
}).catch(e => {
|
||||
throw e;
|
||||
});
|
||||
if (sourceNovel.chapters) {
|
||||
await insertChapters(novelId, sourceNovel.chapters);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const updateNovelInfo = async (info: NovelInfo) => {
|
||||
|
||||
@@ -28,14 +28,20 @@ export const useBrowseSource = (
|
||||
if (isScreenMounted.current === true) {
|
||||
try {
|
||||
const plugin = getPlugin(pluginId);
|
||||
const res = await plugin.popularNovels(page, {
|
||||
showLatestNovels,
|
||||
filters,
|
||||
});
|
||||
setNovels(prevState => (page === 1 ? res : [...prevState, ...res]));
|
||||
if (!res.length) {
|
||||
setHasNextPage(false);
|
||||
}
|
||||
await plugin
|
||||
.popularNovels(page, {
|
||||
showLatestNovels,
|
||||
filters,
|
||||
})
|
||||
.then(res => {
|
||||
setNovels(prevState =>
|
||||
page === 1 ? res : [...prevState, ...res],
|
||||
);
|
||||
if (!res.length) {
|
||||
setHasNextPage(false);
|
||||
}
|
||||
})
|
||||
.catch(error => setError(error.message));
|
||||
setFilterValues(plugin.filters);
|
||||
} catch (err: unknown) {
|
||||
setError(`${err}`);
|
||||
|
||||
@@ -120,7 +120,9 @@ const MigrationNovelList = ({
|
||||
<Button
|
||||
onPress={() => {
|
||||
hideMigrateNovelDialog();
|
||||
migrateNovel(pluginId, fromNovel, selectedNovel.url);
|
||||
migrateNovel(pluginId, fromNovel, selectedNovel.url).catch(
|
||||
error => showToast(error.message),
|
||||
);
|
||||
}}
|
||||
title={getString('novelScreen.migrate')}
|
||||
/>
|
||||
|
||||
@@ -85,44 +85,47 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => {
|
||||
|
||||
const renderTabBar = (
|
||||
props: SceneRendererProps & { navigationState: State },
|
||||
) => (
|
||||
<TabBar
|
||||
{...props}
|
||||
scrollEnabled
|
||||
indicatorStyle={{ backgroundColor: theme.primary }}
|
||||
style={[
|
||||
{
|
||||
backgroundColor: theme.surface,
|
||||
borderBottomColor: color(theme.isDark ? '#FFFFFF' : '#000000')
|
||||
.alpha(0.12)
|
||||
.string(),
|
||||
},
|
||||
styles.tabBar,
|
||||
]}
|
||||
renderLabel={({ route, color }) => (
|
||||
<Row>
|
||||
<Text style={{ color }}>{route.title}</Text>
|
||||
{showNumberOfNovels && (
|
||||
<View
|
||||
style={[
|
||||
styles.badgeCtn,
|
||||
{ backgroundColor: theme.surfaceVariant },
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[styles.badgetText, { color: theme.onSurfaceVariant }]}
|
||||
) =>
|
||||
library.length ? (
|
||||
<TabBar
|
||||
{...props}
|
||||
scrollEnabled
|
||||
indicatorStyle={{ backgroundColor: theme.primary, height: 3 }}
|
||||
style={[
|
||||
{
|
||||
backgroundColor: theme.surface,
|
||||
borderBottomColor: color(theme.isDark ? '#FFFFFF' : '#000000')
|
||||
.alpha(0.12)
|
||||
.string(),
|
||||
},
|
||||
styles.tabBar,
|
||||
]}
|
||||
tabStyle={{ width: 'auto' }}
|
||||
gap={8}
|
||||
renderLabel={({ route, color }) => (
|
||||
<Row>
|
||||
<Text style={{ color, fontWeight: '600' }}>{route.title}</Text>
|
||||
{showNumberOfNovels && (
|
||||
<View
|
||||
style={[
|
||||
styles.badgeCtn,
|
||||
{ backgroundColor: theme.surfaceVariant },
|
||||
]}
|
||||
>
|
||||
{(route as any)?.novels.length}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</Row>
|
||||
)}
|
||||
inactiveColor={theme.secondary}
|
||||
activeColor={theme.primary}
|
||||
pressColor={theme.rippleColor}
|
||||
/>
|
||||
);
|
||||
<Text
|
||||
style={[styles.badgetText, { color: theme.onSurfaceVariant }]}
|
||||
>
|
||||
{(route as any)?.novels.length}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</Row>
|
||||
)}
|
||||
inactiveColor={theme.secondary}
|
||||
activeColor={theme.primary}
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const searchbarPlaceholder =
|
||||
selectedNovelIds.length === 0
|
||||
@@ -314,7 +317,6 @@ const styles = StyleSheet.create({
|
||||
globalSearchBtn: {
|
||||
margin: 16,
|
||||
},
|
||||
|
||||
fab: {
|
||||
position: 'absolute',
|
||||
margin: 16,
|
||||
@@ -324,8 +326,9 @@ const styles = StyleSheet.create({
|
||||
badgeCtn: {
|
||||
position: 'relative',
|
||||
borderRadius: 50,
|
||||
marginHorizontal: 6,
|
||||
marginLeft: 2,
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
badgetText: {
|
||||
fontSize: 12,
|
||||
|
||||
@@ -165,10 +165,11 @@ export const ChapterContent = ({
|
||||
if (await RNFS.exists(filePath)) {
|
||||
sourceChapter.chapterText = await RNFS.readFile(filePath);
|
||||
} else {
|
||||
sourceChapter.chapterText = await fetchChapter(
|
||||
novel.pluginId,
|
||||
chapter.url,
|
||||
);
|
||||
await fetchChapter(novel.pluginId, chapter.url)
|
||||
.then(res => {
|
||||
sourceChapter.chapterText = res;
|
||||
})
|
||||
.catch(e => setError(e.message));
|
||||
}
|
||||
const [nextChap, prevChap] = await Promise.all([
|
||||
getNextChapter(chapter.novelId, chapter.id),
|
||||
@@ -276,10 +277,13 @@ export const ChapterContent = ({
|
||||
const onWebViewNavigationStateChange = async ({ url }: WebViewNavigation) => {
|
||||
if (url !== 'about:blank') {
|
||||
setLoading(true);
|
||||
const res = await fetchChapter(novel.pluginId, chapter.url);
|
||||
sourceChapter.chapterText = res;
|
||||
setChapter(sourceChapter);
|
||||
setLoading(false);
|
||||
fetchChapter(novel.pluginId, chapter.url)
|
||||
.then(res => {
|
||||
sourceChapter.chapterText = res;
|
||||
setChapter(sourceChapter);
|
||||
})
|
||||
.catch(e => setError(e.message))
|
||||
.finally(() => setLoading(false));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ export const restoreBackup = async (filePath?: string) => {
|
||||
let novelsString = '';
|
||||
|
||||
if (backup.type === 'success') {
|
||||
novelsString = await StorageAccessFramework.readAsStringAsync(backup.uri);
|
||||
novelsString = await RNFS.readFile(backup.uri);
|
||||
} else if (filePath) {
|
||||
if (!(await RNFS.exists(filePath))) {
|
||||
showToast(getString('backupScreen.legacy.noErrorNovel'));
|
||||
@@ -105,20 +105,17 @@ export const restoreBackup = async (filePath?: string) => {
|
||||
if (BackgroundService.isRunning()) {
|
||||
const plugin = getPlugin(novels[i].pluginId);
|
||||
if (!plugin) {
|
||||
showToast(
|
||||
getString('backupScreen.legacy.pluginNotExist', {
|
||||
id: novels[i].pluginId,
|
||||
}),
|
||||
);
|
||||
errorNovels.push(novels[i]);
|
||||
continue;
|
||||
}
|
||||
await restoreLibrary(novels[i]);
|
||||
await BackgroundService.updateNotification({
|
||||
taskTitle: novels[i].name,
|
||||
taskDesc: '(' + (i + 1) + '/' + novels.length + ')',
|
||||
progressBar: { max: novels.length, value: i + 1 },
|
||||
});
|
||||
await restoreLibrary(novels[i]).catch(error => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
if (novels.length === i + 1) {
|
||||
Notifications.scheduleNotificationAsync({
|
||||
@@ -130,7 +127,6 @@ export const restoreBackup = async (filePath?: string) => {
|
||||
},
|
||||
trigger: null,
|
||||
});
|
||||
MMKVStorage.delete(BACKGROUND_ACTION);
|
||||
resolve();
|
||||
}
|
||||
|
||||
@@ -143,8 +139,7 @@ export const restoreBackup = async (filePath?: string) => {
|
||||
await sleep(taskData?.delay || 0);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
showToast(novels[i].name + ': ' + error.message);
|
||||
} catch (e) {
|
||||
errorNovels.push(novels[i]);
|
||||
continue;
|
||||
}
|
||||
@@ -169,6 +164,9 @@ export const restoreBackup = async (filePath?: string) => {
|
||||
}
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
MMKVStorage.delete(BACKGROUND_ACTION);
|
||||
BackgroundService.stop();
|
||||
});
|
||||
|
||||
if (novels.length > 0) {
|
||||
|
||||
@@ -70,7 +70,9 @@ export const migrateNovel = async (
|
||||
if (toNovel) {
|
||||
toChapters = await getChapters(toNovel.id, '', '');
|
||||
} else {
|
||||
const fetchedNovel = await fetchNovel(pluginId, toNovelUrl);
|
||||
const fetchedNovel = await fetchNovel(pluginId, toNovelUrl).catch(e => {
|
||||
throw e;
|
||||
});
|
||||
await insertNovelAndChapters(pluginId, fetchedNovel);
|
||||
toNovel = await getNovel(toNovelUrl);
|
||||
if (!toNovel) {
|
||||
|
||||
@@ -2,26 +2,36 @@ import { getPlugin } from '@plugins/pluginManager';
|
||||
|
||||
export const fetchNovel = async (pluginId: string, novelUrl: string) => {
|
||||
const plugin = getPlugin(pluginId);
|
||||
const res = await plugin.parseNovelAndChapters(novelUrl);
|
||||
const res = await plugin.parseNovelAndChapters(novelUrl).catch(e => {
|
||||
throw e;
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
export const fetchImage = async (pluginId: string, imageUrl: string) => {
|
||||
return getPlugin(pluginId).fetchImage(imageUrl);
|
||||
return getPlugin(pluginId)
|
||||
.fetchImage(imageUrl)
|
||||
.catch(e => {
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchChapter = async (pluginId: string, chapterUrl: string) => {
|
||||
const plugin = getPlugin(pluginId);
|
||||
let chapterText = `Not found plugin with id: ${pluginId}`;
|
||||
if (plugin) {
|
||||
chapterText = await plugin.parseChapter(chapterUrl);
|
||||
chapterText = await plugin.parseChapter(chapterUrl).catch(e => {
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const fetchChapters = async (pluginId: string, novelUrl: string) => {
|
||||
const plugin = getPlugin(pluginId);
|
||||
const res = await plugin.parseNovelAndChapters(novelUrl);
|
||||
const res = await plugin.parseNovelAndChapters(novelUrl).catch(e => {
|
||||
throw e;
|
||||
});
|
||||
|
||||
const chapters = res.chapters;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user