gitshark

Clone repository

git clone https://git.vilip.de/git/vilip/taskflow.git
git clone git@git.vilip.de:vilip/taskflow.git

← Commits

✨ Add webdav connection persistency

9bafcd13b4ffd40bc8bc684e817e7be7f85899bb · vvilip · 2025-11-10T18:36:44Z

Changes

2 files changed, +82 -31

MODIFY app/(tabs)/settings.tsx +7 -6
diff --git "a/app/\050tabs\051/settings.tsx" "b/app/\050tabs\051/settings.tsx"
index 3a81ead..9bcc2d7 100644
--- "a/app/\050tabs\051/settings.tsx"
+++ "b/app/\050tabs\051/settings.tsx"
@@ -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
diff --git a/services/webdav.service.ts b/services/webdav.service.ts
index f9353c5..e4c9af3 100644
--- a/services/webdav.service.ts
+++ b/services/webdav.service.ts
@@ -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 }

Keyboard shortcuts

?Show this help
g hGo home
EscClose dialog