Skip to content
Merged

Dev #23

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
71 changes: 60 additions & 11 deletions mobile/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { router, useFocusEffect } from 'expo-router';
import { useCallback, useMemo, useRef, useState } from 'react';
import { Alert, FlatList, Pressable, Text, View, useWindowDimensions } from 'react-native';
import { Alert, FlatList, Pressable, StyleSheet, Text, View, useWindowDimensions } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { WebView } from 'react-native-webview';
import BookCard from '../../components/BookCard';
Expand Down Expand Up @@ -186,24 +186,24 @@ const LibraryScreen = () => {
}, [books, sort]);

return (
<SafeAreaView edges={['top']} style={{ flex: 1, backgroundColor: colors.paperBg }}>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', height: 44, paddingHorizontal: 16 }}>
<Text style={{ fontSize: 20, fontWeight: '600', color: colors.ink }}>
書櫃 <Text style={{ fontSize: 13, fontWeight: '400', color: colors.ink3 }}>{books.length} 本</Text>
<SafeAreaView edges={['top']} style={[styles.safeArea, { backgroundColor: colors.paperBg }]}>
<View style={styles.header}>
<Text style={[styles.headerTitle, { color: colors.ink }]}>
書櫃 <Text style={[styles.headerCount, { color: colors.ink3 }]}>{books.length} 本</Text>
</Text>
<Pressable onPress={handleAddBook} hitSlop={12}>
<Text style={{ fontSize: 16, color: '#2563eb' }}>+ 加入書籍</Text>
<Text style={styles.addButtonText}>+ 加入書籍</Text>
</Pressable>
</View>

{books.length > 0 && (
<View style={{ paddingHorizontal: 16, paddingBottom: 12 }}>
<View style={styles.sortWrapper}>
<SortControl sort={sort} onSortChange={setSort} />
</View>
)}

{!loading && books.length === 0 ? (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<View style={styles.emptyState}>
Comment on lines +189 to +206

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Use className and remove inline style props.

The stylesheet extraction still leaves inline JSX style objects in every reviewed component. Replace these style props with className bindings, including conditional theme and active-state classes.

  • mobile/app/(tabs)/index.tsx#L189-L206: Replace the safe-area, title, and empty-state style props.
  • mobile/app/(tabs)/settings.tsx#L9-L27: Replace the safe-area, row, label, and switch style props.
  • mobile/app/reader/[id].tsx#L127-L155: Replace the reader header and control style props.
  • mobile/components/BookCard.tsx#L32-L44: Replace cover and placeholder style props.
  • mobile/components/ListPanel.tsx#L118-L123: Replace table-of-contents style props and apply the same change to the remaining panel controls.
  • mobile/components/SelectionBar.tsx#L30-L44: Replace selection-bar and color-swatch style props.
  • mobile/components/SettingsPanel.tsx#L46-L60: Replace helper-generated and JSX style props.
  • mobile/components/SortControl.tsx#L21-L30: Replace sort-control style props.

As per coding guidelines, **/*.tsx: React JSX 頁面/元件中應使用 className 寫法,並避免使用 inline style.

📍 Affects 8 files
  • mobile/app/(tabs)/index.tsx#L189-L206 (this comment)
  • mobile/app/(tabs)/settings.tsx#L9-L27
  • mobile/app/reader/[id].tsx#L127-L155
  • mobile/components/BookCard.tsx#L32-L44
  • mobile/components/ListPanel.tsx#L118-L123
  • mobile/components/SelectionBar.tsx#L30-L44
  • mobile/components/SettingsPanel.tsx#L46-L60
  • mobile/components/SortControl.tsx#L21-L30
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/app/`(tabs)/index.tsx around lines 189 - 206, Replace all reviewed
inline JSX style props with className bindings, preserving conditional theme and
active-state behavior: update mobile/app/(tabs)/index.tsx lines 189-206 for the
safe-area, title, and empty-state styles; mobile/app/(tabs)/settings.tsx lines
9-27 for the safe-area, rows, labels, and switch; mobile/app/reader/[id].tsx
lines 127-155 for the reader header and controls; mobile/components/BookCard.tsx
lines 32-44 for cover and placeholder; mobile/components/ListPanel.tsx lines
118-123 and the remaining panel controls; mobile/components/SelectionBar.tsx
lines 30-44 for the selection bar and swatches;
mobile/components/SettingsPanel.tsx lines 46-60 for helper-generated and JSX
styles; and mobile/components/SortControl.tsx lines 21-30 for sort-control
styles. Use the existing stylesheet/extraction classes rather than introducing
new inline objects.

Source: Coding guidelines

<Text style={{ color: colors.ink3 }}>尚未加入任何書籍</Text>
</View>
) : (
Expand All @@ -212,8 +212,8 @@ const LibraryScreen = () => {
data={shown}
keyExtractor={(item) => item.id}
numColumns={COLUMNS}
contentContainerStyle={{ padding: H_PADDING, gap: GRID_GAP }}
columnWrapperStyle={{ gap: GRID_GAP }}
contentContainerStyle={styles.listContent}
columnWrapperStyle={styles.columnWrapper}
renderItem={({ item }) => (
<BookCard
record={item}
Expand All @@ -236,11 +236,60 @@ const LibraryScreen = () => {
// 用 1x1 近乎歸零的尺寸隱藏這個 WebView 時,WKWebView(iOS)有可能不會確實跑內容的 JS
// (近似 headless/離屏極小視圖被系統節流),改用較合理的尺寸(100x100)搬到畫面外,
// 只用 opacity:0 隱藏,避免這個因素導致整個 extractMeta 流程收不到任何回應。
style={{ position: 'absolute', width: 100, height: 100, opacity: 0, top: -1000, left: 0 }}
style={styles.hiddenExtractorWebview}
pointerEvents="none"
/>
</SafeAreaView>
);
};

const styles = StyleSheet.create({
safeArea: {
flex: 1,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
height: 44,
paddingHorizontal: 16,
},
headerTitle: {
fontSize: 20,
fontWeight: '600',
},
headerCount: {
fontSize: 13,
fontWeight: '400',
},
addButtonText: {
fontSize: 16,
color: '#2563eb',
},
sortWrapper: {
paddingHorizontal: 16,
paddingBottom: 12,
},
emptyState: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
listContent: {
padding: H_PADDING,
gap: GRID_GAP,
},
columnWrapper: {
gap: GRID_GAP,
},
hiddenExtractorWebview: {
position: 'absolute',
width: 100,
height: 100,
opacity: 0,
top: -1000,
left: 0,
},
});

export default LibraryScreen;
84 changes: 53 additions & 31 deletions mobile/app/(tabs)/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,74 @@
import { Pressable, Text, View } from 'react-native';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useTheme } from '../../lib/theme';

const SettingsScreen = () => {
const { darkMode, toggleDarkMode, colors } = useTheme();

return (
<SafeAreaView edges={['top']} style={{ flex: 1, backgroundColor: colors.paperBg }}>
<Text style={{ fontSize: 20, fontWeight: '600', color: colors.ink, paddingHorizontal: 16, height: 44, lineHeight: 44 }}>
設定
</Text>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 16,
marginTop: 8,
paddingHorizontal: 16,
paddingVertical: 14,
borderRadius: 10,
borderWidth: 1,
borderColor: colors.borderColor,
backgroundColor: colors.paperBg2,
}}
>
<Text style={{ fontSize: 15, color: colors.ink }}>深色模式</Text>
<SafeAreaView edges={['top']} style={[styles.safeArea, { backgroundColor: colors.paperBg }]}>
<Text style={[styles.title, { color: colors.ink }]}>設定</Text>
<View style={[styles.toggleRow, { borderColor: colors.borderColor, backgroundColor: colors.paperBg2 }]}>
<Text style={[styles.toggleLabel, { color: colors.ink }]}>深色模式</Text>
<Pressable
onPress={toggleDarkMode}
accessibilityRole="switch"
accessibilityState={{ checked: darkMode }}
accessibilityLabel="切換深色模式"
hitSlop={8}
style={{
width: 48,
height: 28,
borderRadius: 14,
padding: 2,
backgroundColor: darkMode ? colors.progressFill : colors.borderColor,
justifyContent: 'center',
alignItems: darkMode ? 'flex-end' : 'flex-start',
}}
style={[
styles.switchTrack,
{
backgroundColor: darkMode ? colors.progressFill : colors.borderColor,
alignItems: darkMode ? 'flex-end' : 'flex-start',
},
]}
>
<View style={{ width: 24, height: 24, borderRadius: 12, backgroundColor: '#fff' }} />
<View style={styles.switchThumb} />
</Pressable>
</View>
</SafeAreaView>
);
};

const styles = StyleSheet.create({
safeArea: {
flex: 1,
},
title: {
fontSize: 20,
fontWeight: '600',
paddingHorizontal: 16,
height: 44,
lineHeight: 44,
},
toggleRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 16,
marginTop: 8,
paddingHorizontal: 16,
paddingVertical: 14,
borderRadius: 10,
borderWidth: 1,
},
toggleLabel: {
fontSize: 15,
},
switchTrack: {
width: 48,
height: 28,
borderRadius: 14,
padding: 2,
justifyContent: 'center',
},
switchThumb: {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: '#fff',
},
});

export default SettingsScreen;
Loading
Loading