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:
nyagami
2024-02-03 10:19:09 +07:00
committed by GitHub
parent 4bf615477e
commit e5a1753e52
9 changed files with 130 additions and 96 deletions
+1 -2
View File
@@ -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,
);
});
});
+33 -23
View File
@@ -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')}
/>
+42 -39
View File
@@ -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,
+12 -8
View File
@@ -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));
}
};
+8 -10
View File
@@ -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) {
+3 -1
View File
@@ -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) {
+14 -4
View File
@@ -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;