Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Log of Nextcloud file synchronization in app #1151

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions lib/components/nextcloud/log_messages.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:logging/logging.dart';
import 'package:saber/data/prefs.dart';

class NextcloudMessages extends StatefulWidget {
/// creates widget with last Nextcloud synchronization messages
const NextcloudMessages({
super.key,
});

@override
State<NextcloudMessages> createState() => _NextcloudMessagesState();
}

class _NextcloudMessagesState extends State<NextcloudMessages> {
@override
void initState() {
Prefs.nextcloudLogMessages.addListener(messageListener); // add listener to changes in nextcloud messages.
// if any new message, then messageListener will be called
super.initState();
messageListener(); // fill messages text
}

@override
void dispose() {
Prefs.nextcloudLogMessages.removeListener(messageListener); // remove listener
super.dispose();
}

final TextEditingController _txt = TextEditingController(); // TextField controller to enable editing of text
void messageListener() {
/// called when there is an update of upload/download
setState(() {
String text="";
for (var msg in Prefs.nextcloudLogMessages.value) {
text=text+msg.toString();
}
_txt.text=text;
});
}
@override
Widget build(BuildContext context) {
return TextField(
style:TextStyle(fontSize:10),
controller: _txt,
maxLines: 10,
decoration: InputDecoration(
border: OutlineInputBorder(),
),
);
}
}
/// types of messages in nextcloud synchronization logs
enum NextcloudLogMessageType {
info,
successUpload,
successDownload,
errorDownload,
errorUpload,
}

class NextcloudLogMessages {
/// Nextcloud synchronization messages
static final log = Logger('SyncMsg');

void add(NextcloudLogMessageType type,String localFile,String remoteFile,String textError){
/// add message to messages buffer
final String text;
final DateTime now = DateTime.now();
final String formattedDate = DateFormat('yyyy-MM-dd kk:mm').format(now);

switch (type) {
case NextcloudLogMessageType.successDownload:
text = "↓: ✓ " + localFile;
case NextcloudLogMessageType.errorDownload:
text = "↓: ❌ " + localFile + " from " + remoteFile+" Error:"+textError;
case NextcloudLogMessageType.successUpload:
text = "↑: ✓ " + localFile;
case NextcloudLogMessageType.errorUpload:
text = "↑: ❌ " + localFile + " to " + remoteFile+" Error:"+textError;
case NextcloudLogMessageType.info:
text = textError;
}
while (Prefs.nextcloudLogMessages.value.length>20) {
// more than 10 messages, remove the first one
Prefs.nextcloudLogMessages.value.remove(Prefs.nextcloudLogMessages.value.elementAt(0));
}
Prefs.nextcloudLogMessages.value.add(formattedDate+" "+text+"\n");
Prefs.nextcloudLogMessages.notifyListeners();
}
}

/// for (var e in mySet) {
// print(e);
// }
10 changes: 10 additions & 0 deletions lib/components/nextcloud/login_group.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:regexed_validator/regexed_validator.dart';
import 'package:saber/components/nextcloud/log_messages.dart';
import 'package:saber/components/nextcloud/spinning_loading_icon.dart';
import 'package:saber/components/settings/app_info.dart';
import 'package:saber/components/theming/adaptive_button.dart';
Expand Down Expand Up @@ -91,6 +92,9 @@ class _LoginInputGroupState extends State<LoginInputGroup> {
final TextEditingController _ncPasswordController = TextEditingController();
final TextEditingController _encPasswordController = TextEditingController();

// class used to keep nextcloud upload/download log in Preferences page
static final NextcloudLogMessages nextcloudSyncMessages=NextcloudLogMessages();

void _login() async {
bool valid = _formKey.currentState?.validate() ?? false;
if (!valid) return;
Expand All @@ -113,11 +117,17 @@ class _LoginInputGroupState extends State<LoginInputGroup> {
setState(() {
_errorMessage = t.login.feedbacks.loginSuccess;
});
nextcloudSyncMessages.add(
NextcloudLogMessageType.info,"","","Successfull login to nextcloud server"
);
FileSyncer.startSync();
} on LoginFailure catch (e) {
setState(() {
_errorMessage = e.message;
});
nextcloudSyncMessages.add(
NextcloudLogMessageType.info,"","","Login to nextcloud server failed Error: "+e.message
);
} finally {
setState(() {
_isLoading = false;
Expand Down
20 changes: 20 additions & 0 deletions lib/data/nextcloud/file_syncer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:mutex/mutex.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:saber/components/nextcloud/log_messages.dart';
import 'package:saber/data/file_manager/file_manager.dart';
import 'package:saber/data/nextcloud/nextcloud_client_extension.dart';
import 'package:saber/data/prefs.dart';
Expand Down Expand Up @@ -42,6 +43,10 @@ abstract class FileSyncer {
'/Readme.md',
];

// class used to keep nextcloud upload/download log in Preferences page
static final NextcloudLogMessages nextcloudSyncMessages=NextcloudLogMessages();


static void startSync() async {
log.fine('startSync: Starting sync');

Expand Down Expand Up @@ -194,6 +199,9 @@ abstract class FileSyncer {
await FileManager.readFile(filePathUnencrypted);
if (localDataUnencrypted == null) {
log.severe('Failed to read file $filePathUnencrypted to upload');
nextcloudSyncMessages.add(
NextcloudLogMessageType.errorUpload,filePathUnencrypted,"","Failed to read file"
);
return;
}

Expand Down Expand Up @@ -241,10 +249,16 @@ abstract class FileSyncer {
PathUri.parse(syncFile.remotePath),
lastModified: lastModified,
);
nextcloudSyncMessages.add(
NextcloudLogMessageType.successUpload,filePathUnencrypted,syncFile.remotePath,""
);
} on SocketException catch (e) {
// network error
log.warning('Failed to upload $filePathUnencrypted: network error', e);
_uploadQueue.value.add(filePathUnencrypted);
nextcloudSyncMessages.add(
NextcloudLogMessageType.errorUpload,filePathUnencrypted,syncFile.remotePath,e.toString()
);
// wait 2 seconds before trying to upload the next file
await Future.delayed(const Duration(seconds: 2));
}
Expand Down Expand Up @@ -390,11 +404,17 @@ abstract class FileSyncer {
'Decrypted data is empty but file.webDavFile!.size is ${file.webDavFile!.size}');
FileManager.writeFile(file.localPath, decryptedData,
awaitWrite: awaitWrite, alsoUpload: false);
nextcloudSyncMessages.add(
NextcloudLogMessageType.successDownload,file.localPath,"",""
);
return true;
} catch (e) {
log.severe(
'Failed to download file ${file.localPath} (${file.remotePath}): $e',
e);
nextcloudSyncMessages.add(
NextcloudLogMessageType.errorDownload,file.localPath,file.remotePath,e.toString()
);
return false;
}
}
Expand Down
6 changes: 6 additions & 0 deletions lib/data/prefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ abstract class Prefs {
/// File paths that are known to be corrupted on Nextcloud
static late final PlainPref<Set<String>> fileSyncCorruptFiles;

/// Nextcloud synchronization messages
static late final PlainPref<Set<String>> nextcloudLogMessages;


/// Set when we want to resync everything.
/// Files on the server older than this date will be
/// reuploaded with the local version.
Expand Down Expand Up @@ -252,6 +256,8 @@ abstract class Prefs {
fileSyncUploadQueue = PlainPref('fileSyncUploadQueue', Queue<String>());
fileSyncAlreadyDeleted = PlainPref('fileSyncAlreadyDeleted', {});
fileSyncCorruptFiles = PlainPref('fileSyncCorruptFiles', {});
nextcloudLogMessages= PlainPref('nextcloudLogMessages', {}); // initialize nextcloud log messages to empty

// By default, we resync everything uploaded before v0.18.4, since uploads before then resulted in 0B files.
fileSyncResyncEverythingDate = PlainPref('fileSyncResyncEverythingDate',
DateTime.parse('2023-12-10T10:06:31.000Z'));
Expand Down
16 changes: 16 additions & 0 deletions lib/i18n/strings.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ class _StringsProfileQuickLinksEn {
String get title => 'Quick links';
String get serverHomepage => 'Server homepage';
String get deleteAccount => 'Delete account';
String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -1432,6 +1433,7 @@ class _StringsProfileQuickLinksAr extends _StringsProfileQuickLinksEn {
@override String get title => 'روابط سريعة';
@override String get serverHomepage => 'الصفحة الرئيسية للخادم';
@override String get deleteAccount => 'حذف الحساب';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -2210,6 +2212,7 @@ class _StringsProfileQuickLinksCs extends _StringsProfileQuickLinksEn {
@override String get title => 'Rychlé odkazy';
@override String get serverHomepage => 'Webové stránky serveru';
@override String get deleteAccount => 'Odstranit účet';
@override String get synchronizationLog => 'Historie synchronizace';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -2988,6 +2991,7 @@ class _StringsProfileQuickLinksDe extends _StringsProfileQuickLinksEn {
@override String get title => 'Schnellzugriff';
@override String get serverHomepage => 'Server-Startseite';
@override String get deleteAccount => 'Account löschen';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -3764,6 +3768,7 @@ class _StringsProfileQuickLinksEs extends _StringsProfileQuickLinksEn {
@override String get title => 'Enlaces rápidos';
@override String get serverHomepage => 'Página de inicio del servidor';
@override String get deleteAccount => 'Eliminar cuenta';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -4542,6 +4547,7 @@ class _StringsProfileQuickLinksFa extends _StringsProfileQuickLinksEn {
@override String get title => 'لینک های سریع';
@override String get serverHomepage => 'صفحه اصلی سرور';
@override String get deleteAccount => 'حذف حساب';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -5320,6 +5326,7 @@ class _StringsProfileQuickLinksFr extends _StringsProfileQuickLinksEn {
@override String get title => 'Raccourcis';
@override String get serverHomepage => 'Page d\'accueil du serveur';
@override String get deleteAccount => 'Supprimer le compte';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -6098,6 +6105,7 @@ class _StringsProfileQuickLinksHe extends _StringsProfileQuickLinksEn {
@override String get title => 'קישורים מהירים';
@override String get serverHomepage => 'דף הבית של השרת';
@override String get deleteAccount => 'מחק משתמש';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -6876,6 +6884,7 @@ class _StringsProfileQuickLinksHu extends _StringsProfileQuickLinksEn {
@override String get title => 'Gyors linkek';
@override String get serverHomepage => 'Szerver honlapja';
@override String get deleteAccount => 'Fiók törlése';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -7654,6 +7663,7 @@ class _StringsProfileQuickLinksIt extends _StringsProfileQuickLinksEn {
@override String get title => 'Collegamenti veloci';
@override String get serverHomepage => 'Homepage del server';
@override String get deleteAccount => 'Elimina account';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -8432,6 +8442,7 @@ class _StringsProfileQuickLinksJa extends _StringsProfileQuickLinksEn {
@override String get title => 'クイックリンク';
@override String get serverHomepage => 'サーバーホームページ';
@override String get deleteAccount => 'アカウントの削除';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -9210,6 +9221,7 @@ class _StringsProfileQuickLinksPtBr extends _StringsProfileQuickLinksEn {
@override String get title => 'Links rápidos';
@override String get serverHomepage => 'Página inicial do servidor';
@override String get deleteAccount => 'Apagar conta';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -9988,6 +10000,7 @@ class _StringsProfileQuickLinksRu extends _StringsProfileQuickLinksEn {
@override String get title => 'Быстрые ссылки';
@override String get serverHomepage => 'Домашняя страница сервера';
@override String get deleteAccount => 'Удалить учётную запись';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -10766,6 +10779,7 @@ class _StringsProfileQuickLinksTr extends _StringsProfileQuickLinksEn {
@override String get title => 'Hızlı linkler';
@override String get serverHomepage => 'Sunucu anasayfası';
@override String get deleteAccount => 'Hesabı sil';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -11544,6 +11558,7 @@ class _StringsProfileQuickLinksZhHansCn extends _StringsProfileQuickLinksEn {
@override String get title => '快速链接';
@override String get serverHomepage => '服务器主页';
@override String get deleteAccount => '删除帐户';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down Expand Up @@ -12322,6 +12337,7 @@ class _StringsProfileQuickLinksZhHantTw extends _StringsProfileQuickLinksEn {
@override String get title => '快速連結';
@override String get serverHomepage => '伺服器主頁';
@override String get deleteAccount => '刪除帳戶';
@override String get synchronizationLog => 'Synchronization log';
}

// Path: profile.faq.0
Expand Down
19 changes: 19 additions & 0 deletions lib/pages/user/profile_page.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'package:flutter/material.dart';
import 'package:saber/components/misc/faq.dart';
import 'package:saber/components/nextcloud/log_messages.dart';
import 'package:saber/components/theming/sliver_width_box.dart';
import 'package:saber/data/prefs.dart';
import 'package:saber/i18n/strings.g.dart';
import 'package:url_launcher/url_launcher.dart';

class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
// class used to keep nextcloud upload/download log in Preferences page
static final NextcloudLogMessages nextcloudSyncMessages=NextcloudLogMessages();

void logout() {
Prefs.url.value = '';
Expand All @@ -17,6 +20,9 @@ class ProfilePage extends StatelessWidget {
Prefs.lastStorageQuota.value = null;
Prefs.key.value = '';
Prefs.iv.value = '';
nextcloudSyncMessages.add(
NextcloudLogMessageType.info,"","","Logged out from nextcloud server"
);
}

@override
Expand Down Expand Up @@ -84,6 +90,19 @@ class ProfilePage extends StatelessWidget {
],
),
const SizedBox(height: 16),
Text(t.profile.quickLinks.synchronizationLog),
NextcloudMessages( // nextcloud synchronization log
),

const SizedBox(height: 16),
],
),
),
SliverWidthBox(
width: 350,
sliver: SliverFaq(
items: [
for (final item in t.profile.faq) FaqItem(item.q, item.a),
],
),
),
Expand Down