Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/database/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { createNovelCategoryTableQuery } from './tables/NovelCategoryTable';
import {
createChapterTableQuery,
createChapterNovelIdIndexQuery,
addHiddenColumnToTable,
} from './tables/ChapterTable';
import { dbTxnErrorCallback } from './utils/helpers';
import { noop } from 'lodash-es';
Expand All @@ -32,6 +33,7 @@ export const createTables = () => {

db.transaction(tx => {
tx.executeSql(createRepositoryTableQuery);
addHiddenColumnToTable(tx);
});
};

Expand Down
20 changes: 18 additions & 2 deletions src/database/queries/ChapterQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ export const insertChapters = async (
if (!chapters?.length) {
return;
}
const existingChapters = await getPageChapters(
novelId,
'',
'',
chapters[0].page || '1',
);
const chaptersToHide = existingChapters.filter(
c => !chapters.some(ch => ch.path === c.path),
);

db.transaction(tx => {
chapters.forEach((chapter, index) => {
tx.executeSql(
Expand All @@ -40,8 +50,8 @@ export const insertChapters = async (
tx.executeSql(
`
UPDATE Chapter SET
page = ?, position = ?
WHERE path = ? AND novelId = ? AND (page != ? OR position != ?)
page = ?, position = ?, hidden = 0
WHERE path = ? AND novelId = ? AND (page != ? OR position != ? OR hidden != 0)
`,
[
chapter.page || '1',
Expand All @@ -56,6 +66,12 @@ export const insertChapters = async (
},
);
});
chaptersToHide.forEach(chapter => {
tx.executeSql(
'UPDATE Chapter SET hidden = 1 WHERE path = ? AND novelId = ?',
[chapter.path, novelId],
);
});
});
};

Expand Down
1 change: 1 addition & 0 deletions src/database/queries/LibraryQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const getLibraryWithCategoryQuery = `
SUM(unread) as chaptersUnread, SUM(isDownloaded) as chaptersDownloaded,
novelId, MAX(readTime) as lastReadAt, MAX(updatedTime) as lastUpdatedAt
FROM Chapter
WHERE hidden = 0
GROUP BY novelId
) as C ON NIL.id = C.novelId
) WHERE 1 = 1
Expand Down
6 changes: 3 additions & 3 deletions src/database/queries/StatsQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ const getChaptersReadCountQuery = `
FROM Chapter
JOIN Novel
ON Chapter.novelId = Novel.id
WHERE Chapter.unread = 0 AND Novel.inLibrary = 1
WHERE Chapter.unread = 0 AND Novel.inLibrary = 1 AND Chapter.hidden = 0
`;

const getChaptersTotalCountQuery = `
SELECT COUNT(*) as chaptersCount
FROM Chapter
JOIN Novel
ON Chapter.novelId = Novel.id
WHERE Novel.inLibrary = 1
WHERE Novel.inLibrary = 1 AND Chapter.hidden = 0
`;

const getChaptersUnreadCountQuery = `
SELECT COUNT(*) as chaptersUnread
FROM Chapter
JOIN Novel
ON Chapter.novelId = Novel.id
WHERE Chapter.unread = 1 AND Novel.inLibrary = 1
WHERE Chapter.unread = 1 AND Novel.inLibrary = 1 AND Chapter.hidden = 0
`;

const getChaptersDownloadedCountQuery = `
Expand Down
22 changes: 22 additions & 0 deletions src/database/tables/ChapterTable.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { SQLTransaction } from 'expo-sqlite';
import { txnErrorCallback } from '@database/utils/helpers';

export const createChapterTableQuery = `
CREATE TABLE IF NOT EXISTS Chapter (
id INTEGER PRIMARY KEY AUTOINCREMENT,
Expand All @@ -14,11 +17,30 @@ export const createChapterTableQuery = `
page TEXT DEFAULT "1",
position INTEGER DEFAULT 0,
progress INTEGER,
hidden INTEGER DEFAULT 0,
UNIQUE(path, novelId),
FOREIGN KEY (novelId) REFERENCES Novel(id) ON DELETE CASCADE
)
`;

export const addHiddenColumnToTable = (tx: SQLTransaction) => {
tx.executeSql(
'PRAGMA table_info("Chapter");',
[],
(tx2, resultSet) => {
const hasSortContents = !!resultSet.rows._array.find(
v => v.name === 'hidden',
);
if (!hasSortContents) {
tx.executeSql(
'ALTER TABLE Chapter ADD COLUMN hidden INTEGER DEFAULT 0;',
);
}
},
txnErrorCallback,
);
};

export const createChapterNovelIdIndexQuery = `
CREATE INDEX
IF NOT EXISTS
Expand Down
1 change: 1 addition & 0 deletions src/hooks/persisted/useNovel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface NovelSettings {

const defaultNovelSettings: NovelSettings = {
showChapterTitles: true,
filter: 'AND hidden=0',
};
const defaultPageIndex = 0;

Expand Down
1 change: 1 addition & 0 deletions src/hooks/persisted/usePlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export default function usePlugins() {
name: _plg.name,
version: _plg.version,
hasUpdate: false,
hasSettings: !!_plg.pluginSettings,
};
if (newPlugin.id === lastUsedPlugin?.id) {
setLastUsedPlugin(newPlugin);
Expand Down
21 changes: 19 additions & 2 deletions src/plugins/helpers/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,25 @@ export const fetchApi = async (
init?: FetchInit,
): Promise<Response> => {
init = makeInit(init);
return await fetch(url, init);
return await fetch(url, init).then(checkCloudflareError);
};

async function checkCloudflareError(res: Response) {
const text = await res.clone().text();
const title = text.match(/<title>(.*?)<\/title>/)?.[1];
if (
title == 'Bot Verification' ||
title == 'You are being redirected...' ||
title == 'Un instant...' ||
title == 'Just a moment...' ||
title == 'Redirecting...'
) {
throw new Error('Captcha error, please open in webview');
}

return res;
}

const FILE_READER_PREFIX_LENGTH = 'data:application/octet-stream;base64,'
.length;

Expand Down Expand Up @@ -79,7 +95,7 @@ export const fetchText = async (
): Promise<string> => {
init = makeInit(init);
try {
const res = await fetch(url, init);
const res = await fetch(url, init).then(checkCloudflareError);
if (!res.ok) {
throw new Error();
}
Expand Down Expand Up @@ -163,6 +179,7 @@ export const fetchProto = async function (
...init,
body: bodyArray,
} as RequestInit)
.then(checkCloudflareError)
.then(r => r.blob())
.then(blob => {
// decode response data
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/pluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,17 @@ const getPlugin = (pluginId: string) => {
return plugins[pluginId];
};

const unloadPlugin = (pluginId: string) => {
plugins[pluginId] = undefined;
};

const LOCAL_PLUGIN_ID = 'local';

export {
getPlugin,
installPlugin,
uninstallPlugin,
unloadPlugin,
updatePlugin,
fetchPlugins,
LOCAL_PLUGIN_ID,
Expand Down
42 changes: 28 additions & 14 deletions src/screens/browse/components/Modals/SourceSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { Button } from '@components/index';
import { useTheme } from '@hooks/persisted';
import { getString } from '@strings/translations';
import { Storage } from '@plugins/helpers/storage';
import { unloadPlugin } from '@plugins/pluginManager';
import { SwitchItem } from '@components';

interface PluginSetting {
value: string;
label: string;
type?: 'Switch';
}

interface PluginSettings {
Expand Down Expand Up @@ -75,6 +78,7 @@ const SourceSettingsModal: React.FC<SourceSettingsModal> = ({
Object.entries(formValues).forEach(([key, value]) => {
storage.set(key, value);
});
unloadPlugin(pluginId);
onDismiss();
};

Expand Down Expand Up @@ -112,20 +116,30 @@ const SourceSettingsModal: React.FC<SourceSettingsModal> = ({
</Text>
<Text style={[{ color: theme.onSurfaceVariant }]}>{description}</Text>

{Object.entries(pluginSettings).map(([key, setting]) => (
<TextInput
key={key}
mode="outlined"
label={setting.label}
value={formValues[key] || ''}
onChangeText={value => handleChange(key, value)}
placeholder={`Enter ${setting.label}`}
placeholderTextColor={theme.onSurfaceDisabled}
underlineColor={theme.outline}
style={[{ color: theme.onSurface }, styles.textInput]}
theme={{ colors: { ...theme } }}
/>
))}
{Object.entries(pluginSettings).map(([key, setting]) =>
setting?.type === 'Switch' ? (
<SwitchItem
key={key}
label={setting.label}
value={!!formValues[key]}
onPress={() => handleChange(key, formValues[key] ? '' : 'true')}
theme={theme}
/>
) : (
<TextInput
key={key}
mode="outlined"
label={setting.label}
value={formValues[key] || ''}
onChangeText={value => handleChange(key, value)}
placeholder={`Enter ${setting.label}`}
placeholderTextColor={theme.onSurfaceDisabled}
underlineColor={theme.outline}
style={[{ color: theme.onSurface }, styles.textInput]}
theme={{ colors: { ...theme } }}
/>
),
)}

<View style={styles.customCSSButtons}>
<Button
Expand Down
3 changes: 2 additions & 1 deletion src/screens/novel/NovelScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const Novel = ({ route, navigation }: NovelScreenProps) => {
lastRead,
novelSettings: {
sort = defaultChapterSort,
filter = '',
filter = 'AND hidden=0',
showChapterTitles = false,
},
openPage,
Expand All @@ -89,6 +89,7 @@ const Novel = ({ route, navigation }: NovelScreenProps) => {
refreshChapters,
deleteChapters,
} = useNovel(path, pluginId);

const theme = useTheme();
const { top: topInset, bottom: bottomInset } = useSafeAreaInsets();

Expand Down
6 changes: 5 additions & 1 deletion src/screens/novel/components/Info/NovelInfoHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,11 @@ const NovelInfoHeader = ({
) : null}
<IconButton
icon="filter-variant"
iconColor={filter ? filterColor(theme.isDark) : theme.onSurface}
iconColor={
filter.trim() !== 'AND hidden=0'
? filterColor(theme.isDark)
: theme.onSurface
}
size={24}
onPress={() => novelBottomSheetRef.current?.present()}
/>
Expand Down
12 changes: 11 additions & 1 deletion src/screens/novel/components/NovelBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ const ChaptersSettingsSheet = ({
: filterChapters(filter + ' AND bookmark=1');
}}
/>
<Checkbox
theme={theme}
label={getString('novelScreen.bottomSheet.filters.dontExist')}
status={!filter.match('AND hidden=0')}
onPress={() => {
filter.match('AND hidden=0')
? filterChapters(filter.replace(/ ?AND hidden=0/, ''))
: filterChapters(filter + ' AND hidden=0');
}}
/>
</View>
);

Expand Down Expand Up @@ -184,7 +194,7 @@ const ChaptersSettingsSheet = ({
);

return (
<BottomSheet snapPoints={[240]} bottomSheetRef={bottomSheetRef}>
<BottomSheet snapPoints={[280]} bottomSheetRef={bottomSheetRef}>
<BottomSheetView
style={[
styles.contentContainer,
Expand Down
27 changes: 24 additions & 3 deletions src/services/updates/LibraryUpdateQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { NOVEL_STORAGE } from '@utils/Storages';
import { downloadFile } from '@plugins/helpers/fetch';
import ServiceManager from '@services/ServiceManager';
import { db } from '@database/db';
import { getPageChapters } from '@database/queries/ChapterQueries';
import { ChapterInfo } from '@database/types';

const updateNovelMetadata = (
pluginId: string,
Expand Down Expand Up @@ -73,13 +75,26 @@ const updateNovelTotalPages = (novelId: number, totalPages: number) => {
});
};

const updateNovelChapters = (
const updateNovelChapters = async (
novelName: string,
novelId: number,
chapters: ChapterItem[],
downloadNewChapters?: boolean,
page?: string,
) => {
let chaptersToHide: ChapterInfo[] = [];
if (chapters.length > 0) {
const existingChapters = await getPageChapters(
novelId,
'',
'',
chapters[0].page || '1',
);
chaptersToHide = existingChapters.filter(
c => !chapters.some(ch => ch.path === c.path),
);
}

return new Promise((resolve, reject) => {
db.transaction(async tx => {
for (let position = 0; position < chapters.length; position++) {
Expand Down Expand Up @@ -124,8 +139,8 @@ const updateNovelChapters = (
tx.executeSql(
`
UPDATE Chapter SET
name = ?, releaseTime = ?, updatedTime = datetime('now','localtime'), page = ?, position = ?
WHERE path = ? AND novelId = ? AND (name != ? OR releaseTime != ? OR page != ? OR position != ?);
name = ?, releaseTime = ?, updatedTime = datetime('now','localtime'), page = ?, position = ?, hidden = 0
WHERE path = ? AND novelId = ? AND (name != ? OR releaseTime != ? OR page != ? OR position != ? OR hidden != 0);
`,
[
name,
Expand Down Expand Up @@ -153,6 +168,12 @@ const updateNovelChapters = (
},
);
}
chaptersToHide.forEach(chapter => {
tx.executeSql(
'UPDATE Chapter SET hidden = 1 WHERE path = ? AND novelId = ?',
[chapter.path, novelId],
);
});
resolve(null);
});
});
Expand Down
Loading