Skip to content

Commit aa1db64

Browse files
authored
Merge pull request #142 from FireFistisDead/feature/csv-filename
Feature of csv file rename
2 parents 87e4525 + 1f590ae commit aa1db64

File tree

4 files changed

+178
-71
lines changed

4 files changed

+178
-71
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
<!-- Internet access -->
55
<uses-permission android:name="android.permission.INTERNET" />
66

7+
<!-- For Android 13+ (API 33+) -->
8+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
9+
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
10+
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
11+
712
<!-- For Android 10 and below -->
813
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
914
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
1015
android:maxSdkVersion="29" />
1116

12-
<!-- For Android 11+ -->
13-
<!-- Only use MANAGE_EXTERNAL_STORAGE if you need full access, else rely on SAF -->
17+
<!-- Optional: only if your app is a file manager or needs all-file access -->
1418
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
15-
1619
<application
1720
android:name=".MainApplication"
1821
android:label="@string/app_name"

src/screens/user-settings/SettingsScreen.tsx

Lines changed: 95 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// src/screens/user-settings/SettingsScreen.tsx
2+
13
import React, { useState, useEffect } from 'react';
24
import {
35
View,
@@ -17,7 +19,7 @@ import {
1719
import Slider from '@react-native-community/slider';
1820
import MultiSelect from 'react-native-multiple-select';
1921
import useStore from '../../store/store';
20-
import { saveScheduleToDevice, shareSchedule } from '../../utils/exportSchedule';
22+
import { saveScheduleToDevice, shareSchedule, saveScheduleToDeviceWithName } from '../../utils/exportSchedule';
2123
import pickCSVFile, { pickCSVFileRaw } from '../../utils/csv-picker';
2224
import { importAndAddToRegisterFromContent } from '../../utils/csv-import';
2325

@@ -37,6 +39,8 @@ const SettingsScreen: React.FC = () => {
3739
registers,
3840
activeRegister,
3941
selectedRegisters,
42+
viewingRegisters,
43+
setViewingRegisters,
4044
setDefaultTargetPercentage,
4145
updateAllRegistersTargetPercentage,
4246
selectedSchedules,
@@ -54,7 +58,15 @@ const SettingsScreen: React.FC = () => {
5458
const [newTargetValue, setNewTargetValue] = useState(defaultTargetPercentage.toString());
5559
const [localLeadTime, setLocalLeadTime] = useState(notificationLeadTime);
5660
const [localSchedules, setLocalSchedules] = useState<string[]>(selectedSchedules);
57-
61+
// CSV filename modal state
62+
const [showFileNameModal, setShowFileNameModal] = useState(false);
63+
const [csvFileName, setCsvFileName] = useState('MySchedule');
64+
const [isSaving, setIsSaving] = useState(false);
65+
66+
const registerOptions = Object.keys(registers).map(key => ({
67+
id: key,
68+
name: registers[parseInt(key)].name,
69+
}));
5870
// Effects
5971
useEffect(() => {
6072
setAppVersion(packageJson.version);
@@ -103,56 +115,56 @@ const SettingsScreen: React.FC = () => {
103115
};
104116

105117
// Export functionality handlers
106-
const handleSaveScheduleToDevice = async () => {
118+
// Show modal to enter filename
119+
const handleSaveScheduleToDevice = () => {
120+
setShowFileNameModal(true);
121+
};
122+
123+
// Actually save with filename
124+
const handleConfirmSaveFileName = async () => {
125+
setIsSaving(true);
107126
try {
108-
console.log('Starting save to device...');
109-
console.log('Available registers:', Object.keys(registers));
110-
console.log('Selected registers from store:', selectedRegisters);
111-
112-
// Use all registers if no specific selection is available
113-
const currentSelectedRegisters = selectedRegisters && selectedRegisters.length > 0
114-
? selectedRegisters
127+
const currentSelectedRegisters = selectedRegisters && selectedRegisters.length > 0
128+
? selectedRegisters
115129
: Object.keys(registers).map(key => parseInt(key, 10));
116-
117-
console.log('Using registers for export:', currentSelectedRegisters);
118-
119130
if (currentSelectedRegisters.length === 0) {
120131
Alert.alert('No Data', 'No registers found to export. Please create some schedules first.');
132+
setIsSaving(false);
121133
return;
122134
}
123-
124-
await saveScheduleToDevice({
125-
selectedRegisters: currentSelectedRegisters,
126-
registers
127-
});
135+
await saveScheduleToDeviceWithName({
136+
selectedRegisters: currentSelectedRegisters,
137+
registers
138+
}, csvFileName);
139+
setShowFileNameModal(false);
128140
} catch (error) {
129-
console.error('Save to device error:', error);
130141
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
131142
Alert.alert('Error', `Failed to save schedule to device: ${errorMessage}`);
132143
}
144+
setIsSaving(false);
133145
};
134146

135147
const handleShareSchedule = async () => {
136148
try {
137149
console.log('Starting share schedule...');
138150
console.log('Available registers:', Object.keys(registers));
139151
console.log('Selected registers from store:', selectedRegisters);
140-
152+
141153
// Use all registers if no specific selection is available
142-
const currentSelectedRegisters = selectedRegisters && selectedRegisters.length > 0
143-
? selectedRegisters
154+
const currentSelectedRegisters = selectedRegisters && selectedRegisters.length > 0
155+
? selectedRegisters
144156
: Object.keys(registers).map(key => parseInt(key, 10));
145-
157+
146158
console.log('Using registers for share:', currentSelectedRegisters);
147-
159+
148160
if (currentSelectedRegisters.length === 0) {
149161
Alert.alert('No Data', 'No registers found to share. Please create some schedules first.');
150162
return;
151163
}
152-
153-
await shareSchedule({
154-
selectedRegisters: currentSelectedRegisters,
155-
registers
164+
165+
await shareSchedule({
166+
selectedRegisters: currentSelectedRegisters,
167+
registers
156168
});
157169
} catch (error) {
158170
console.error('Share schedule error:', error);
@@ -164,7 +176,7 @@ const SettingsScreen: React.FC = () => {
164176
const handleImportSchedule = async () => {
165177
try {
166178
console.log('Starting CSV import...');
167-
179+
168180
if (!registers[activeRegister]) {
169181
Alert.alert('Error', 'No active register found. Please create a register first.');
170182
return;
@@ -173,7 +185,7 @@ const SettingsScreen: React.FC = () => {
173185
// Use raw CSV content instead of parsed data
174186
const csvContent = await pickCSVFileRaw();
175187
console.log('Raw CSV Content received:', csvContent);
176-
188+
177189
if (!csvContent) {
178190
console.log('No CSV content received (user cancelled or error)');
179191
return;
@@ -238,7 +250,6 @@ const SettingsScreen: React.FC = () => {
238250
<Text style={styles.infoValue}>{registerInfo.name}</Text>
239251
<Text style={styles.infoSubtext}>{registerInfo.totalCards} subjects</Text>
240252
</View>
241-
242253
{/* Preferences Section */}
243254
<View style={styles.settingsSection}>
244255
<Text style={styles.sectionTitle}>Preferences</Text>
@@ -294,6 +305,7 @@ const SettingsScreen: React.FC = () => {
294305
</View>
295306
</View>
296307

308+
297309
{/* Notifications & Alerts Section */}
298310
<View style={styles.settingsSection}>
299311
<Text style={styles.sectionTitle}>Notifications & Alerts</Text>
@@ -333,29 +345,71 @@ const SettingsScreen: React.FC = () => {
333345
/>
334346
<Button title="Save Settings" onPress={handleSave} color="#4CAF50" />
335347
</View>
336-
337348
{/* Utilities Section */}
338349
<View style={styles.settingsSection}>
339350
<Text style={styles.sectionTitle}>Utilities</Text>
340351

341352
<TouchableOpacity style={styles.utilityButton} onPress={handleSaveScheduleToDevice}>
342353
<View style={styles.utilityButtonContent}>
343-
<Image
344-
source={require('../../assets/icons/save.png')}
345-
style={styles.utilityIcon}
354+
<Image
355+
source={require('../../assets/icons/save.png')}
356+
style={styles.utilityIcon}
346357
/>
347358
<View style={styles.utilityTextContainer}>
348359
<Text style={styles.utilityButtonText}>Save Schedule to Device</Text>
349360
<Text style={styles.utilityButtonDescription}>Save your schedule as CSV to Downloads</Text>
350361
</View>
351362
</View>
352363
</TouchableOpacity>
364+
{/* CSV Filename Modal */}
365+
<Modal
366+
visible={showFileNameModal}
367+
transparent={true}
368+
animationType="fade"
369+
onRequestClose={() => setShowFileNameModal(false)}
370+
>
371+
<View style={styles.modalOverlay}>
372+
<View style={styles.modalContainer}>
373+
<Text style={styles.modalTitle}>Enter CSV Filename</Text>
374+
<Text style={styles.modalSubtitle}>This will be the name of your exported CSV file.</Text>
375+
<View style={styles.inputContainer}>
376+
<TextInput
377+
style={styles.input}
378+
value={csvFileName}
379+
onChangeText={setCsvFileName}
380+
placeholder="Enter filename"
381+
placeholderTextColor="#71717A"
382+
maxLength={40}
383+
selectTextOnFocus={true}
384+
autoFocus={true}
385+
/>
386+
<Text style={styles.percentSymbol}>.csv</Text>
387+
</View>
388+
<View style={styles.modalButtons}>
389+
<TouchableOpacity
390+
style={[styles.modalButton, styles.cancelButton]}
391+
onPress={() => setShowFileNameModal(false)}
392+
disabled={isSaving}
393+
>
394+
<Text style={styles.cancelButtonText}>Cancel</Text>
395+
</TouchableOpacity>
396+
<TouchableOpacity
397+
style={[styles.modalButton, styles.saveButton]}
398+
onPress={handleConfirmSaveFileName}
399+
disabled={isSaving || !csvFileName.trim()}
400+
>
401+
<Text style={styles.saveButtonText}>{isSaving ? 'Saving...' : 'Save'}</Text>
402+
</TouchableOpacity>
403+
</View>
404+
</View>
405+
</View>
406+
</Modal>
353407

354408
<TouchableOpacity style={styles.utilityButton} onPress={handleShareSchedule}>
355409
<View style={styles.utilityButtonContent}>
356-
<Image
357-
source={require('../../assets/icons/share.png')}
358-
style={styles.utilityIcon}
410+
<Image
411+
source={require('../../assets/icons/share.png')}
412+
style={styles.utilityIcon}
359413
/>
360414
<View style={styles.utilityTextContainer}>
361415
<Text style={styles.utilityButtonText}>Share Schedule</Text>
@@ -366,9 +420,9 @@ const SettingsScreen: React.FC = () => {
366420

367421
<TouchableOpacity style={styles.utilityButton} onPress={handleImportSchedule}>
368422
<View style={styles.utilityButtonContent}>
369-
<Image
370-
source={require('../../assets/icons/export.png')}
371-
style={styles.utilityIcon}
423+
<Image
424+
source={require('../../assets/icons/export.png')}
425+
style={styles.utilityIcon}
372426
/>
373427
<View style={styles.utilityTextContainer}>
374428
<Text style={styles.utilityButtonText}>Import Schedule from CSV</Text>
@@ -456,7 +510,6 @@ const SettingsScreen: React.FC = () => {
456510
</>
457511
);
458512
};
459-
460513
// Styles
461514
const styles = StyleSheet.create({
462515
container: {
@@ -713,5 +766,4 @@ const styles = StyleSheet.create({
713766
color: '#A1A1AA',
714767
},
715768
});
716-
717769
export default SettingsScreen;

0 commit comments

Comments
 (0)