Skip to content

Commit 3cccf35

Browse files
authored
Merge pull request #57 from groot007/electron-dev-to-tauri
Add clear table button
2 parents 85b8ee1 + 0751a22 commit 3cccf35

File tree

8 files changed

+430
-119
lines changed

8 files changed

+430
-119
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "flippio",
3-
"version": "0.3.9",
3+
"version": "0.3.10",
44
"description": "Database viewer with device connection",
55
"author": "koliastanis",
66
"homepage": "https://github.com/groot007/flippio",

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "Flippio"
3-
version = "0.3.9"
3+
version = "0.3.10"
44
description = "Database viewer with device connection"
55
authors = ["koliastanis"]
66
edition = "2021"

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"productName": "Flippio",
3-
"version": "0.3.9",
3+
"version": "0.3.10",
44
"identifier": "com.flippio.app",
55
"build": {
66
"beforeDevCommand": "npm run dev:renderer",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
Text,
3+
} from '@chakra-ui/react'
4+
import FLModal from '../common/FLModal'
5+
6+
interface ClearTableDialogProps {
7+
isOpen: boolean
8+
onClose: () => void
9+
onClear: () => void
10+
isLoading: boolean
11+
}
12+
13+
export const ClearTableDialog: React.FC<ClearTableDialogProps> = ({
14+
isOpen,
15+
onClose,
16+
onClear,
17+
isLoading,
18+
}) => {
19+
if (isLoading) {
20+
return <>Loading..</>
21+
}
22+
23+
return (
24+
<FLModal
25+
isOpen={isOpen}
26+
body={(
27+
<Text>
28+
Are you sure you want to clear this table? This will delete all rows in the table and cannot be undone.
29+
</Text>
30+
)}
31+
title="Clear Table"
32+
acceptBtn="Clear Table"
33+
onAccept={() => {
34+
onClear()
35+
}}
36+
rejectBtn="Cancel"
37+
onReject={() => {
38+
onClose()
39+
}}
40+
/>
41+
)
42+
}

src/renderer/src/components/SidePanel/SidePanel.tsx

Lines changed: 58 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import {
66
Portal,
77
Stack,
88
} from '@chakra-ui/react'
9+
import { useClearTableMutation, useDeleteRowMutation } from '@renderer/hooks/useTableMutations'
910
import { useCurrentDatabaseSelection, useCurrentDeviceSelection, useTableData } from '@renderer/store'
1011
import { useRowEditingStore } from '@renderer/store/useRowEditingStore'
1112
import { useColorMode } from '@renderer/ui/color-mode'
12-
import { toaster } from '@renderer/ui/toaster'
13-
import { buildUniqueCondition } from '@renderer/utils'
14-
import { useQueryClient } from '@tanstack/react-query'
1513
import { useCallback, useState } from 'react'
1614
import { LuX } from 'react-icons/lu'
15+
import { ClearTableDialog } from './ClearTableDialog'
1716
import { DeleteRowDialog } from './DeleteRowDialog'
1817
import { FieldItem } from './Field'
1918
import { RowEditor } from './RowEditor'
@@ -24,117 +23,65 @@ export function SidePanel() {
2423
const { selectedRow, setSelectedRow } = useRowEditingStore()
2524
const isOpen = !!selectedRow
2625
const { selectedDevice, selectedApplication } = useCurrentDeviceSelection()
27-
const { selectedDatabaseFile, selectedDatabaseTable, pulledDatabaseFilePath } = useCurrentDatabaseSelection()
26+
const { selectedDatabaseFile, selectedDatabaseTable } = useCurrentDatabaseSelection()
2827
const tableData = useTableData(state => state.tableData)
29-
const setTableData = useTableData(state => state.setTableData)
30-
const queryClient = useQueryClient()
3128
const [isLoading, setIsLoading] = useState(false)
3229
const [isEditing, setIsEditing] = useState(false)
3330
const [editedData, setEditedData] = useState<Record<string, any>>({})
3431
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
32+
const [isClearTableDialogOpen, setIsClearTableDialogOpen] = useState(false)
3533

3634
const closePanel = useCallback(() => {
3735
setSelectedRow(null)
3836
setIsEditing(false)
3937
}, [setSelectedRow])
4038

41-
const handleDeleteRow = useCallback(async () => {
42-
if (!selectedRow || !selectedDatabaseTable)
43-
return
39+
const clearTableMutation = useClearTableMutation()
4440

45-
try {
46-
const condition = buildUniqueCondition(
47-
tableData?.columns,
48-
selectedRow?.originalData || selectedRow?.rowData,
49-
)
50-
const result = await window.api.deleteTableRow(
51-
selectedDatabaseTable?.name || '',
52-
condition,
53-
)
41+
const handleClearTable = useCallback(async () => {
42+
if (!selectedDatabaseTable)
43+
return
5444

55-
if (!result.success) {
56-
throw new Error(result.error || 'Failed to delete row')
57-
}
45+
await clearTableMutation.mutateAsync({
46+
selectedDatabaseTable,
47+
selectedDatabaseFile,
48+
selectedDevice,
49+
selectedApplication,
50+
})
5851

59-
console.log('Row deleted successfully:', selectedDatabaseFile)
60-
// Push changes back to device if needed
61-
if (
62-
selectedDatabaseFile
63-
&& selectedDevice
64-
&& selectedDatabaseFile.packageName
65-
&& selectedDatabaseFile.path // Ensure we have the local file path
66-
&& (selectedDatabaseFile?.deviceType === 'android'
67-
|| selectedDatabaseFile?.deviceType === 'iphone'
68-
|| selectedDatabaseFile?.deviceType === 'iphone-device'
69-
|| selectedDatabaseFile?.deviceType === 'simulator')
70-
) {
71-
// Use the correct paths: local temp file and remote path on device
72-
const localPath = selectedDatabaseFile.path // This is the local temp file path
73-
const remotePath = selectedDatabaseFile.remotePath || `/${selectedDatabaseFile.location}/${selectedDatabaseFile.filename}`
74-
75-
console.log('Pushing database file:', {
76-
deviceId: selectedDevice.id,
77-
localPath,
78-
packageName: selectedDatabaseFile.packageName,
79-
remotePath,
80-
deviceType: selectedDatabaseFile.deviceType,
81-
})
82-
83-
await window.api.pushDatabaseFile(
84-
selectedDevice.id,
85-
localPath,
86-
selectedDatabaseFile.packageName,
87-
remotePath,
88-
selectedDatabaseFile.deviceType,
89-
)
90-
91-
// Invalidate and refetch database files to get updated data
92-
await queryClient.invalidateQueries({
93-
queryKey: ['databaseFiles', selectedDevice.id, selectedApplication?.bundleId],
94-
})
95-
}
52+
setIsClearTableDialogOpen(false)
53+
}, [
54+
selectedDatabaseTable,
55+
selectedDatabaseFile,
56+
selectedDevice,
57+
selectedApplication,
58+
clearTableMutation,
59+
])
9660

97-
setIsDeleteDialogOpen(false)
61+
const deleteRowMutation = useDeleteRowMutation()
9862

99-
// Show success message
100-
toaster.create({
101-
title: 'Row deleted',
102-
description: 'The row has been successfully deleted',
103-
type: 'success',
104-
duration: 3000,
105-
})
63+
const handleDeleteRow = useCallback(async () => {
64+
if (!selectedRow || !selectedDatabaseTable)
65+
return
10666

107-
// Close the panel
108-
closePanel()
67+
await deleteRowMutation.mutateAsync({
68+
selectedRow,
69+
selectedDatabaseTable,
70+
selectedDatabaseFile,
71+
selectedDevice,
72+
selectedApplication,
73+
tableColumns: tableData?.columns,
74+
})
10975

110-
// Refresh table data to reflect deletion
111-
if (selectedDatabaseTable?.name) {
112-
const response = await window.api.getTableInfo(selectedDatabaseTable.name)
113-
if (response.success && response.columns && response.rows) {
114-
setTableData({
115-
columns: response.columns,
116-
rows: response.rows,
117-
})
118-
}
119-
}
120-
}
121-
catch (error) {
122-
console.error('Error deleting row:', error)
123-
toaster.create({
124-
title: 'Delete failed',
125-
description: error instanceof Error ? error.message : 'Failed to delete row',
126-
type: 'error',
127-
duration: 5000,
128-
})
129-
}
76+
setIsDeleteDialogOpen(false)
13077
}, [
13178
selectedRow,
13279
selectedDatabaseTable,
133-
tableData?.columns,
13480
selectedDatabaseFile,
13581
selectedDevice,
136-
pulledDatabaseFilePath,
137-
closePanel,
82+
selectedApplication,
83+
tableData?.columns,
84+
deleteRowMutation,
13885
])
13986

14087
return (
@@ -202,17 +149,36 @@ export function SidePanel() {
202149
size="md"
203150
width="full"
204151
mt={8}
205-
mb={4}
152+
mb={2}
206153
_hover={{ bg: 'red.50', _dark: { bg: 'red.900' } }}
207154
>
208155
Remove Row
209156
</Button>
157+
<Button
158+
colorScheme="orange"
159+
variant="outline"
160+
onClick={() => {
161+
setIsClearTableDialogOpen(true)
162+
}}
163+
size="md"
164+
width="full"
165+
mb={4}
166+
_hover={{ bg: 'orange.50', _dark: { bg: 'orange.900' } }}
167+
>
168+
Clear Whole Table
169+
</Button>
210170
<DeleteRowDialog
211171
isOpen={isDeleteDialogOpen}
212172
onClose={() => setIsDeleteDialogOpen(false)}
213173
onDelete={handleDeleteRow}
214174
isLoading={isLoading}
215175
/>
176+
<ClearTableDialog
177+
isOpen={isClearTableDialogOpen}
178+
onClose={() => setIsClearTableDialogOpen(false)}
179+
onClear={handleClearTable}
180+
isLoading={isLoading}
181+
/>
216182
</Stack>
217183
)}
218184
</Drawer.Body>

src/renderer/src/components/layout/SubHeader.tsx

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useDatabaseTables } from '@renderer/hooks/useDatabaseTables'
1111
import { useTableDataQuery } from '@renderer/hooks/useTableDataQuery'
1212
import { useCurrentDatabaseSelection, useCurrentDeviceSelection, useTableData } from '@renderer/store'
1313
import { toaster } from '@renderer/ui/toaster'
14+
import { useDatabaseRefresh } from '@renderer/utils/databaseRefresh'
1415
import { useCallback, useEffect, useMemo, useState } from 'react'
1516
import { LuDatabase, LuFilter, LuFolderOpen, LuRefreshCcw, LuTable, LuUpload, LuX } from 'react-icons/lu'
1617
import { CustomQueryModal } from '../data/CustomQueryModal'
@@ -35,7 +36,6 @@ export function SubHeader() {
3536
const {
3637
data: databaseFiles = [],
3738
isLoading,
38-
refetch: refetchDatabaseFiles,
3939
} = useDatabaseFiles(selectedDevice, selectedApplication)
4040

4141
const { data: tableData, refetch: refetchTable } = useTableDataQuery(selectedDatabaseTable?.name || '')
@@ -55,7 +55,6 @@ export function SubHeader() {
5555

5656
const {
5757
data: tablesData,
58-
refetch: refetchDatabaseTables,
5958
isError,
6059
} = useDatabaseTables(selectedDatabaseFile, selectedDevice)
6160

@@ -116,27 +115,11 @@ export function SubHeader() {
116115
}
117116
}, [selectedDatabaseFile])
118117

119-
const handleDBRefresh = useCallback(async () => {
120-
await refetchDatabaseFiles()
121-
.then(() => {
122-
toaster.create({
123-
title: 'Success',
124-
description: 'Database refreshed',
125-
type: 'success',
126-
duration: 3000,
127-
})
128-
})
129-
.catch((err) => {
130-
toaster.create({
131-
title: 'Error refreshing database',
132-
description: err.message,
133-
type: 'error',
134-
duration: 3000,
135-
})
136-
})
137-
await refetchDatabaseTables()
138-
await refetchTable()
139-
}, [refetchDatabaseFiles, refetchDatabaseTables, refetchTable])
118+
const handleDBRefresh = useDatabaseRefresh()
119+
120+
const onRefreshClick = useCallback(async () => {
121+
await handleDBRefresh()
122+
}, [handleDBRefresh])
140123

141124
const isNoDB = !databaseFiles?.length && !isDBPulling && selectedApplication?.bundleId && selectedDevice?.id
142125

@@ -252,7 +235,7 @@ export function SubHeader() {
252235
<Button
253236
data-testid="refresh-db"
254237
data-state={isLoading ? 'open' : 'closed'}
255-
onClick={handleDBRefresh}
238+
onClick={onRefreshClick}
256239
variant="ghost"
257240
size="sm"
258241
color="textSecondary"

0 commit comments

Comments
 (0)