✨ Add webdav connection persistency
Changes
2 files changed, +82 -31
MODIFY
app/(tabs)/settings.tsx
+7 -6
@@ -28,11 +28,12 @@
28
28
}, [])
29
29
);
30
30
31
- const checkWebDAVStatus = () => {
32
- const isConfigured = webdavService.isConfigured();
31
+ const checkWebDAVStatus = async () => {
32
+ const isConfigured = await webdavService.isConfigured();
33
33
setWebdavConfigured(isConfigured);
34
34
if (isConfigured) {
35
- setWebdavInfo(webdavService.getConfig());
35
+ const config = await webdavService.getConfig();
36
+ setWebdavInfo(config);
36
37
}
37
38
};
38
39
@@ -189,9 +190,9 @@
189
190
{
190
191
text: 'Disconnect',
191
192
style: 'destructive',
192
- onPress: () => {
193
- webdavService.disconnect();
194
- checkWebDAVStatus();
193
+ onPress: async () => {
194
+ await webdavService.disconnect();
195
+ await checkWebDAVStatus();
195
196
Alert.alert('Disconnected', 'WebDAV connection removed');
196
197
},
197
198
},
MODIFY
services/webdav.service.ts
+75 -25
@@ -4,10 +4,12 @@
4
4
*/
5
5
6
6
import { createClient, WebDAVClient, FileStat } from 'webdav';
7
+import AsyncStorage from '@react-native-async-storage/async-storage';
7
8
import { GTDData } from '../types/gtd';
8
9
import storageService from './storage.service';
9
10
10
11
const WEBDAV_FILE_PATH = '/taskflow-data.json';
12
+const WEBDAV_CONFIG_KEY = '@taskflow_webdav_config';
11
13
12
14
interface WebDAVConfig {
13
15
url: string;
@@ -18,34 +20,75 @@
18
20
class WebDAVService {
19
21
private client: WebDAVClient | null = null;
20
22
private config: WebDAVConfig | null = null;
23
+ private initializationPromise: Promise<void> | null = null;
24
+
25
+ constructor() {
26
+ this.initializationPromise = this.loadAndInitialize();
27
+ }
28
+
29
+ /**
30
+ * Load stored credentials and initialize client on app start
31
+ */
32
+ private async loadAndInitialize(): Promise<void> {
33
+ try {
34
+ const configJson = await AsyncStorage.getItem(WEBDAV_CONFIG_KEY);
35
+ if (configJson) {
36
+ const config: WebDAVConfig = JSON.parse(configJson);
37
+ await this.initializeClient(config.url, config.username, config.password, false);
38
+ }
39
+ } catch (error) {
40
+ console.error('Failed to load WebDAV config:', error);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Ensure initialization is complete before operations
46
+ */
47
+ private async ensureInitialized(): Promise<void> {
48
+ if (this.initializationPromise) {
49
+ await this.initializationPromise;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Internal method to initialize client without saving
55
+ */
56
+ private async initializeClient(url: string, username: string, password: string, saveConfig: boolean = true): Promise<void> {
57
+ // Ensure URL ends with /remote.php/dav/files/[username] for Nextcloud
58
+ let webdavUrl = url.trim();
59
+
60
+ // Remove trailing slash
61
+ if (webdavUrl.endsWith('/')) {
62
+ webdavUrl = webdavUrl.slice(0, -1);
63
+ }
64
+
65
+ // If it's a Nextcloud URL without the WebDAV path, add it
66
+ if (!webdavUrl.includes('/remote.php/dav')) {
67
+ webdavUrl = `${webdavUrl}/remote.php/dav/files/${username}`;
68
+ }
69
+
70
+ this.client = createClient(webdavUrl, {
71
+ username,
72
+ password,
73
+ });
74
+
75
+ this.config = { url: webdavUrl, username, password };
76
+
77
+ // Test connection
78
+ await this.client.exists('/');
79
+
80
+ // Save config for persistence
81
+ if (saveConfig) {
82
+ await AsyncStorage.setItem(WEBDAV_CONFIG_KEY, JSON.stringify(this.config));
83
+ }
84
+ }
21
85
22
86
/**
23
87
* Initialize WebDAV client with server configuration
24
88
*/
25
89
async initialize(url: string, username: string, password: string): Promise<void> {
26
90
try {
27
- // Ensure URL ends with /remote.php/dav/files/[username] for Nextcloud
28
- let webdavUrl = url.trim();
29
-
30
- // Remove trailing slash
31
- if (webdavUrl.endsWith('/')) {
32
- webdavUrl = webdavUrl.slice(0, -1);
33
- }
34
-
35
- // If it's a Nextcloud URL without the WebDAV path, add it
36
- if (!webdavUrl.includes('/remote.php/dav')) {
37
- webdavUrl = `${webdavUrl}/remote.php/dav/files/${username}`;
38
- }
39
-
40
- this.client = createClient(webdavUrl, {
41
- username,
42
- password,
43
- });
44
-
45
- this.config = { url: webdavUrl, username, password };
46
-
47
- // Test connection
48
- await this.client.exists('/');
91
+ await this.initializeClient(url, username, password, true);
49
92
} catch (error) {
50
93
console.error('Failed to initialize WebDAV client:', error);
51
94
throw new Error('Failed to connect to WebDAV server. Please check your credentials.');
@@ -55,14 +98,16 @@
55
98
/**
56
99
* Check if WebDAV is configured
57
100
*/
58
- isConfigured(): boolean {
101
+ async isConfigured(): Promise<boolean> {
102
+ await this.ensureInitialized();
59
103
return this.client !== null && this.config !== null;
60
104
}
61
105
62
106
/**
63
107
* Get current configuration (without password)
64
108
*/
65
- getConfig(): { url: string; username: string } | null {
109
+ async getConfig(): Promise<{ url: string; username: string } | null> {
110
+ await this.ensureInitialized();
66
111
if (!this.config) return null;
67
112
return {
68
113
url: this.config.url,
@@ -73,15 +118,17 @@
73
118
/**
74
119
* Disconnect from WebDAV server
75
120
*/
76
- disconnect(): void {
121
+ async disconnect(): Promise<void> {
77
122
this.client = null;
78
123
this.config = null;
124
+ await AsyncStorage.removeItem(WEBDAV_CONFIG_KEY);
79
125
}
80
126
81
127
/**
82
128
* Upload local data to WebDAV server
83
129
*/
84
130
async uploadData(): Promise<void> {
131
+ await this.ensureInitialized();
85
132
if (!this.client) {
86
133
throw new Error('WebDAV client not initialized');
87
134
}
@@ -103,6 +150,7 @@
103
150
* Download data from WebDAV server
104
151
*/
105
152
async downloadData(): Promise<GTDData | null> {
153
+ await this.ensureInitialized();
106
154
if (!this.client) {
107
155
throw new Error('WebDAV client not initialized');
108
156
}
@@ -129,6 +177,7 @@
129
177
* Get last modified timestamp of remote file
130
178
*/
131
179
async getRemoteLastModified(): Promise<number | null> {
180
+ await this.ensureInitialized();
132
181
if (!this.client) {
133
182
throw new Error('WebDAV client not initialized');
134
183
}
@@ -152,6 +201,7 @@
152
201
* Strategy: Last-write-wins with conflict detection
153
202
*/
154
203
async sync(): Promise<{ success: boolean; message: string }> {
204
+ await this.ensureInitialized();
155
205
if (!this.client) {
156
206
throw new Error('WebDAV client not initialized');
157
207
}