Skip to content

Commit e04106a

Browse files
committed
Refactor: Modularize app architecture with data-driven design
- Create centralized strings.js for all UI text and labels - Add config.js for app-wide configuration and constants - Implement useStrings hook for consistent string access - Add standardized dialog utility system - Update Sidebar and DeviceSelector to use data-driven approach - Add comprehensive ARCHITECTURE.md documentation - Remove Settings page (to be reimplemented) - Clean up ADB command handling - Make app fully modular and i18n-ready
1 parent 4681d4d commit e04106a

7 files changed

Lines changed: 540 additions & 9 deletions

File tree

ARCHITECTURE.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# DroidOps - Modular Architecture Guide
2+
3+
## 📁 Project Structure
4+
5+
```
6+
src/
7+
├── components/ # Reusable UI components
8+
│ ├── DeviceSelector.jsx
9+
│ ├── FileExplorer.jsx
10+
│ ├── Gallery.jsx
11+
│ └── Sidebar.jsx
12+
├── pages/ # Page-level components
13+
│ ├── Dashboard.jsx
14+
│ ├── AppManager.jsx
15+
│ ├── ShellPage.jsx
16+
│ ├── FastbootPage.jsx
17+
│ └── SideloadPage.jsx
18+
├── lib/ # Core functionality
19+
│ ├── adb.js # ADB command wrappers
20+
│ └── cache.js # File caching utilities
21+
├── data/ # Configuration & strings
22+
│ ├── strings.js # All UI text/labels
23+
│ └── config.js # App configuration
24+
├── hooks/ # Custom React hooks
25+
│ └── useStrings.js # String management hook
26+
├── utils/ # Utility functions
27+
│ └── dialog.js # Dialog helpers
28+
└── App.jsx # Main application
29+
30+
```
31+
32+
## 🔧 Data-Driven Design
33+
34+
### Strings Management (`src/data/strings.js`)
35+
36+
All UI text, labels, and messages are centralized in the `STRINGS` object:
37+
38+
```javascript
39+
import { STRINGS, formatString } from '../data/strings';
40+
41+
// Direct access
42+
const title = STRINGS.dashboard.title;
43+
44+
// With placeholders
45+
const message = formatString(STRINGS.apps.confirmUninstall, { app: 'MyApp' });
46+
```
47+
48+
**Categories:**
49+
- `app` - Application metadata
50+
- `navigation` - Menu labels
51+
- `device` - Device-related strings
52+
- `dashboard`, `apps`, `files`, `gallery`, etc. - Page-specific strings
53+
- `errors` - Error messages
54+
- `common` - Common UI strings
55+
56+
### Configuration (`src/data/config.js`)
57+
58+
App-wide settings and constants:
59+
60+
```javascript
61+
import { APP_CONFIG, ROUTES, DEVICE_STATES } from '../data/config';
62+
63+
// Configuration
64+
const theme = APP_CONFIG.ui.defaultTheme;
65+
66+
// Routes
67+
navigate(ROUTES.DASHBOARD);
68+
69+
// Constants
70+
if (device.state === DEVICE_STATES.OFFLINE) { ... }
71+
```
72+
73+
## 🎣 Custom Hooks
74+
75+
### useStrings Hook
76+
77+
```javascript
78+
import { useStrings } from '../hooks/useStrings';
79+
80+
const MyComponent = () => {
81+
const { strings, format } = useStrings();
82+
83+
return <h1>{strings.app.title}</h1>;
84+
};
85+
```
86+
87+
## 🔔 Dialog System
88+
89+
Centralized dialog handling with i18n support:
90+
91+
```javascript
92+
import { confirmDelete, confirmUninstall, alertDialog } from '../utils/dialog';
93+
94+
// Confirm deletion
95+
if (confirmDel ete(fileName)) {
96+
deleteFile();
97+
}
98+
99+
// Custom alerts
100+
alertDialog(STRINGS.files.uploadSuccess);
101+
```
102+
103+
## 📦 Component Guidelines
104+
105+
### 1. Import Order
106+
```javascript
107+
// 1. React & third-party
108+
import React, { useState } from 'react';
109+
import { Icon } from 'lucide-react';
110+
111+
// 2. Data & config
112+
import { STRINGS } from '../data/strings';
113+
import { APP_CONFIG } from '../data/config';
114+
115+
// 3. Utilities & hooks
116+
import { useStrings } from '../hooks/useStrings';
117+
import { confirmDialog } from '../utils/dialog';
118+
119+
// 4. Local imports
120+
import { myFunction } from '../lib/myLib';
121+
```
122+
123+
### 2. Use Constants
124+
```javascript
125+
// ❌ Bad
126+
if (activeTab === "dashboard") { ... }
127+
128+
// ✅ Good
129+
import { ROUTES } from '../data/config';
130+
if (activeTab === ROUTES.DASHBOARD) { ... }
131+
```
132+
133+
### 3. Use Centralized Strings
134+
```javascript
135+
// ❌ Bad
136+
<button>Delete</button>
137+
138+
// ✅ Good
139+
<button>{STRINGS.files.delete}</button>
140+
```
141+
142+
## 🌐 Adding New Features
143+
144+
### Adding a New Page
145+
146+
1. **Create page component** in `src/pages/`
147+
2. **Add route** to `ROUTES` in `config.js`
148+
3. **Add strings** to appropriate section in `strings.js`
149+
4. **Register route** in `App.jsx`
150+
5. **Add navigation** item to `Sidebar.jsx`
151+
152+
### Adding New Strings
153+
154+
1. Add to appropriate category in `src/data/strings.js`:
155+
```javascript
156+
export const STRINGS = {
157+
myFeature: {
158+
title: "My Feature",
159+
description: "Description here",
160+
action: "Action button label"
161+
}
162+
};
163+
```
164+
165+
2. Use in component:
166+
```javascript
167+
import { STRINGS } from '../data/strings';
168+
<h1>{STRINGS.myFeature.title}</h1>
169+
```
170+
171+
### Adding Configuration
172+
173+
1. Add to `src/data/config.js`:
174+
```javascript
175+
export const APP_CONFIG = {
176+
myFeature: {
177+
setting1: true,
178+
setting2: "value"
179+
}
180+
};
181+
```
182+
183+
2. Use in code:
184+
```javascript
185+
import { APP_CONFIG } from '../data/config';
186+
if (APP_CONFIG.myFeature.setting1) { ... }
187+
```
188+
189+
## 🎨 Styling Conventions
190+
191+
- Use Tailwind CSS classes
192+
- Dark theme by default
193+
- Color palette:
194+
- Primary: `#2ca9bc`
195+
- Background: `#0f0f12`
196+
- Surface: `#1a1a1f`
197+
- Border: `#27272a`
198+
199+
## 📝 Best Practices
200+
201+
1. **Keep components focused** - Single responsibility
202+
2. **Use data files** - No hardcoded strings/config
203+
3. **Leverage hooks** - Extract reusable logic
204+
4. **Handle errors gracefully** - Use standardized dialogs
205+
5. **Document new features** - Update this README
206+
207+
## 🔄 Migration Checklist
208+
209+
When updating existing code:
210+
- [ ] Replace hardcoded strings with `STRINGS` references
211+
- [ ] Replace magic values with `APP_CONFIG` constants
212+
- [ ] Use `ROUTES` for navigation logic
213+
- [ ] Use dialog utilities instead of raw `window.confirm/alert`
214+
- [ ] Extract reusable logic into hooks
215+
216+
## 🚀 Development Workflow
217+
218+
```bash
219+
# Start development server
220+
npm run tauri dev
221+
222+
# Build for production
223+
npm run tauri build
224+
```
225+
226+
## 📚 Additional Resources
227+
228+
- [Tauri Documentation](https://tauri.app/)
229+
- [React Hooks](https://react.dev/reference/react)
230+
- [Tailwind CSS](https://tailwindcss.com/)

src/components/DeviceSelector.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState, useEffect } from "react";
22
import { getConnectedDevices, getFastbootDevices, startDeviceTracking } from "../lib/adb";
33
import { RefreshCw, Smartphone, Monitor } from "lucide-react";
4+
import { STRINGS } from "../data/strings";
45

56
export function DeviceSelector({ selectedDevice, onSelect, className }) {
67
const [devices, setDevices] = useState([]);
@@ -52,7 +53,7 @@ export function DeviceSelector({ selectedDevice, onSelect, className }) {
5253
return (
5354
<div className={`flex items-center gap-4 bg-[#1a1a1f] border border-[#27272a] rounded-lg p-1.5 px-3 shadow-xl ${className}`}>
5455
<div className="flex items-center gap-2 text-gray-400">
55-
<span className="text-xs uppercase font-bold tracking-wider">Device:</span>
56+
<span className="text-xs uppercase font-bold tracking-wider">{STRINGS.device.label}</span>
5657
</div>
5758

5859
<div className="relative">
@@ -65,7 +66,7 @@ export function DeviceSelector({ selectedDevice, onSelect, className }) {
6566
className="bg-black/30 border-none text-white text-sm rounded cursor-pointer min-w-[200px] py-1 pl-2 pr-8 appearance-none focus:ring-1 focus:ring-blue-500 outline-none"
6667
>
6768
{devices.length === 0 ? (
68-
<option value="">No Devices Found</option>
69+
<option value="">{STRINGS.device.noDevicesFound}</option>
6970
) : (
7071
devices.map(d => (
7172
<option key={d.serial} value={d.serial}>

src/components/Sidebar.jsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import React from "react";
22
import { Terminal, Zap, Package, Smartphone, Home, Upload, FolderOpen, Images } from "lucide-react";
3+
import { STRINGS } from "../data/strings";
4+
import { ROUTES } from "../data/config";
35

46
export function Sidebar({ activeTab, setActiveTab }) {
57
const navItems = [
6-
{ id: "dashboard", icon: <Home size={20} />, label: "Dashboard" },
7-
{ id: "apps", icon: <Package size={20} />, label: "App Manager" },
8-
{ id: "files", icon: <FolderOpen size={20} />, label: "File Explorer" },
9-
{ id: "gallery", icon: <Images size={20} />, label: "Gallery" },
10-
{ id: "terminal", icon: <Terminal size={20} />, label: "Terminal" },
11-
{ id: "fastboot", icon: <Zap size={20} />, label: "Fastboot" },
12-
{ id: "sideload", icon: <Upload size={20} />, label: "Sideload" },
8+
{ id: ROUTES.DASHBOARD, icon: <Home size={20} />, label: STRINGS.navigation.dashboard },
9+
{ id: ROUTES.APPS, icon: <Package size={20} />, label: STRINGS.navigation.apps },
10+
{ id: ROUTES.FILES, icon: <FolderOpen size={20} />, label: STRINGS.navigation.files },
11+
{ id: ROUTES.GALLERY, icon: <Images size={20} />, label: STRINGS.navigation.gallery },
12+
{ id: ROUTES.TERMINAL, icon: <Terminal size={20} />, label: STRINGS.navigation.terminal },
13+
{ id: ROUTES.FASTBOOT, icon: <Zap size={20} />, label: STRINGS.navigation.fastboot },
14+
{ id: ROUTES.SIDELOAD, icon: <Upload size={20} />, label: STRINGS.navigation.sideload },
1315
];
1416

1517
return (

src/data/config.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
export const APP_CONFIG = {
2+
version: "0.1.2",
3+
name: "DroidOps",
4+
5+
// UI Configuration
6+
ui: {
7+
sidebarWidth: 240,
8+
headerHeight: 64,
9+
defaultTheme: "dark"
10+
},
11+
12+
// File Explorer Settings
13+
fileExplorer: {
14+
defaultPath: "/sdcard/",
15+
showHiddenFiles: false,
16+
gridView: true,
17+
sortBy: "name" // name, size, date
18+
},
19+
20+
// Gallery Settings
21+
gallery: {
22+
thumbnailSize: 120,
23+
previewQuality: "high",
24+
cacheEnabled: true,
25+
cachePath: "thumbnails"
26+
},
27+
28+
// Terminal Settings
29+
terminal: {
30+
maxHistoryLines: 1000,
31+
fontSize: 14,
32+
fontFamily: "monospace"
33+
},
34+
35+
// Device Settings
36+
device: {
37+
refreshInterval: 5000, // ms
38+
autoConnect: true,
39+
trackDevices: true
40+
},
41+
42+
// App Manager Settings
43+
appManager: {
44+
defaultFilter: "user", // user, system, all
45+
showSystemApps: false,
46+
sortBy: "name"
47+
}
48+
};
49+
50+
export const ROUTES = {
51+
DASHBOARD: "dashboard",
52+
APPS: "apps",
53+
FILES: "files",
54+
GALLERY: "gallery",
55+
TERMINAL: "terminal",
56+
FASTBOOT: "fastboot",
57+
SIDELOAD: "sideload"
58+
};
59+
60+
export const DEVICE_STATES = {
61+
DEVICE: "device",
62+
OFFLINE: "offline",
63+
UNAUTHORIZED: "unauthorized",
64+
FASTBOOT: "fastboot"
65+
};
66+
67+
export const FILE_EXTENSIONS = {
68+
images: ['jpg', 'png', 'jpeg', 'gif', 'webp', 'bmp'],
69+
videos: ['mp4', 'mkv', 'webm', 'avi', 'mov'],
70+
audio: ['mp3', 'wav', 'ogg', 'flac', 'm4a'],
71+
documents: ['txt', 'pdf', 'doc', 'docx', 'xml', 'json'],
72+
archives: ['zip', 'rar', '7z', 'tar', 'gz'],
73+
apk: ['apk']
74+
};

0 commit comments

Comments
 (0)