Skip to content

Commit

Permalink
Add export selected notes
Browse files Browse the repository at this point in the history
  • Loading branch information
SankethBK committed Jan 12, 2025
1 parent 003a1f8 commit 3a22902
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 62 deletions.
87 changes: 73 additions & 14 deletions lib/core/widgets/home_page_app_bar.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import 'dart:io';

import 'package:dairy_app/app/themes/theme_extensions/appbar_theme_extensions.dart';
import 'package:dairy_app/app/themes/theme_extensions/popup_theme_extensions.dart';
import 'package:dairy_app/core/dependency_injection/injection_container.dart';
import 'package:dairy_app/core/pages/settings_page.dart';
import 'package:dairy_app/core/utils/utils.dart';
import 'package:dairy_app/core/widgets/cancel_button.dart';
import 'package:dairy_app/core/widgets/date_input_field.dart';
import 'package:dairy_app/core/widgets/glass_dialog.dart';
import 'package:dairy_app/core/widgets/glassmorphism_cover.dart';
import 'package:dairy_app/core/widgets/settings_tile.dart';
import 'package:dairy_app/core/widgets/submit_button.dart';
import 'package:dairy_app/features/auth/data/models/user_config_model.dart';
import 'package:dairy_app/features/notes/domain/repositories/export_notes_repository.dart';
import 'package:dairy_app/features/notes/presentation/bloc/notes/notes_bloc.dart';
import 'package:dairy_app/features/notes/presentation/bloc/notes_fetch/notes_fetch_cubit.dart';
import 'package:dairy_app/features/notes/presentation/bloc/selectable_list/selectable_list_cubit.dart';
import 'package:dairy_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';

class HomePageAppBar extends StatefulWidget implements PreferredSizeWidget {
const HomePageAppBar({
Expand Down Expand Up @@ -623,34 +631,85 @@ class ExportIcon extends StatelessWidget {
.extension<PopupThemeExtensions>()!
.mainTextColor;

final selectableListCubit =
BlocProvider.of<SelectableListCubit>(context);

bool? result = await showCustomDialog(
context: context,
child: LayoutBuilder(
builder: (context, constraints) {
return Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width * 0.6,
padding:
const EdgeInsets.symmetric(horizontal: 35, vertical: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"You are about to delete $exportCount item${exportCount > 1 ? "s" : ""}",
style: TextStyle(
fontSize: 18.0,
color: mainTextColor,
SettingsTile(
onTap: () async {
// create a text file from the notes
final directory =
await getApplicationDocumentsDirectory();

final now = DateTime.now();
final formattedTimestamp =
DateFormat('yyyyMMdd_HHmmss').format(now);

final file = File(
'${directory.path}/diaryvault_notes_export_$formattedTimestamp.txt',
);

try {
String filePath = await sl<IExportNotesRepository>()
.exportNotesToTextFile(
file: file,
noteList: selectableListCubit
.state.selectedItems);

// Share the file and await its completion
await Share.shareXFiles([XFile(filePath)],
text: 'diaryvault_notes_export');

await file.delete();
} on Exception catch (e) {
showToast(
e.toString().replaceAll("Exception: ", ""));
}
},
child: Text(
S.current.exportToPlainText,
style: TextStyle(
fontSize: 16.0,
color: mainTextColor,
),
),
),
const SizedBox(height: 15),
const Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
_CancelButton(),
SizedBox(width: 10),
_DeleteButton(),
],
SettingsTile(
onTap: () async {
try {
String filePath = await sl<IExportNotesRepository>()
.exportNotesToPDF(
noteList: selectableListCubit
.state.selectedItems);

// Share the file and await its completion
await Share.shareXFiles([XFile(filePath)],
text: 'diaryvault_notes_export');
} on Exception catch (e) {
showToast(
e.toString().replaceAll("Exception: ", ""));
}
},
child: Text(
S.current.exportToPDF,
style: TextStyle(
fontSize: 16.0,
color: mainTextColor,
),
),
),
],
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,26 @@ class NotesLocalDataSource implements INotesLocalDataSource {
}

@override
Future<List<NoteModel>> fetchNotes(String authorId) async {
Future<List<NoteModel>> fetchNotes(
{required String authorId, List<String>? noteIds}) async {
List<Map<String, dynamic>> result;
try {
result = await database.query(
Notes.TABLE_NAME,
where:
"${Notes.DELETED} != 1 and ( ${Notes.AUTHOR_ID} = '$authorId' or ${Notes.AUTHOR_ID} = '${GuestUserDetails.guestUserId}' )",
);
if (noteIds != null && noteIds.isNotEmpty) {
// Format the noteIds list for the SQL query
String noteIdsString = noteIds.map((id) => "'$id'").join(', ');

result = await database.query(
Notes.TABLE_NAME,
where:
"${Notes.DELETED} != 1 and ( ${Notes.AUTHOR_ID} = '$authorId' or ${Notes.AUTHOR_ID} = '${GuestUserDetails.guestUserId}' ) AND ${Notes.ID} IN ($noteIdsString)",
);
} else {
result = await database.query(
Notes.TABLE_NAME,
where:
"${Notes.DELETED} != 1 and ( ${Notes.AUTHOR_ID} = '$authorId' or ${Notes.AUTHOR_ID} = '${GuestUserDetails.guestUserId}' )",
);
}
} catch (e) {
log.e("Local database query for fetching notes failed $e");
throw const DatabaseQueryException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ abstract class INotesLocalDataSource {
/// Fetches all notes
///
/// Throws [DatabaseQueryException] if something goes wrong
Future<List<NoteModel>> fetchNotes(String authorId);
Future<List<NoteModel>> fetchNotes(
{required String authorId, List<String>? noteIds});

// Fetch all notes with only columns required to diplay the preview
Future<List<NotePreviewModel>> fetchNotesPreview(String authorId);
Expand Down
88 changes: 50 additions & 38 deletions lib/features/notes/data/repositories/export_notes_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import 'dart:convert';
import 'dart:io';

import 'package:dairy_app/core/logger/logger.dart';
import 'package:dairy_app/features/notes/core/failures/failure.dart';
import 'package:dairy_app/features/notes/data/models/notes_model.dart';
import 'package:dairy_app/features/notes/domain/repositories/export_notes_repository.dart';
import 'package:dairy_app/features/notes/domain/repositories/notes_repository.dart';
import 'package:dartz/dartz.dart';
import 'package:delta_markdown/delta_markdown.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html_to_pdf/flutter_html_to_pdf.dart';
Expand All @@ -22,28 +25,33 @@ class ExportNotesRepository implements IExportNotesRepository {
Future<String> exportNotesToTextFile(
{required File file, List<String>? noteList}) async {
try {
Either<NotesFailure, List<NoteModel>> result;

if (noteList == null) {
log.i("Generating text file for all notes");

final result = await notesRepository.fetchNotes();
result = await notesRepository.fetchNotes();
} else {
log.i("Generating text file for $noteList");

var fileContent = "";
result = await notesRepository.fetchNotes(noteIds: noteList);
}

result.fold((l) => null, (allNotes) async {
for (var note in allNotes) {
fileContent += note.title + "\n";
var fileContent = "";

fileContent += "Created at: " + formatDate(note.createdAt) + "\n";
fileContent += note.plainText;
fileContent += "\n\n---------------------------------\n\n";
}
});
await file.writeAsString(fileContent);
result.fold((l) => null, (allNotes) async {
for (var note in allNotes) {
fileContent += note.title + "\n";

return file.path;
}
fileContent += "Created at: " + formatDate(note.createdAt) + "\n";
fileContent += note.plainText;
fileContent += "\n\n---------------------------------\n\n";
}
});

await file.writeAsString(fileContent);

return "";
return file.path;
} catch (e) {
log.e(e);
rethrow;
Expand Down Expand Up @@ -85,45 +93,49 @@ class ExportNotesRepository implements IExportNotesRepository {
final file = File('${directory.path}/diaryvault_notes_export.txt');

try {
Either<NotesFailure, List<NoteModel>> result;

if (noteList == null) {
log.i("Generating PDF for all notes");

final result = await notesRepository.fetchNotes();
result = await notesRepository.fetchNotes();
} else {
log.i("Generating PDF for $noteList");

var fileContent = "";
result = await notesRepository.fetchNotes(noteIds: noteList);
}

String watermarkFile =
await getImageFileFromAssets('assets/images/watermark.webp');
var fileContent = "";

fileContent +=
"<img width=\"1000\" src=\"$watermarkFile\" alt=\"web-img\">";
String watermarkFile =
await getImageFileFromAssets('assets/images/watermark.webp');

result.fold((l) => null, (allNotes) async {
for (var note in allNotes) {
fileContent += "<h2>${note.title}</h2>";
fileContent +=
"<img width=\"1000\" src=\"$watermarkFile\" alt=\"web-img\">";

fileContent += "<i>Created at: ${formatDate(note.createdAt)} </i>";
fileContent += "<br>";
result.fold((l) => null, (allNotes) async {
for (var note in allNotes) {
fileContent += "<h2>${note.title}</h2>";

final preprocessedDelta = preprocessDeltaForPDFExport(note.body);
fileContent += quillDeltaToHtml(preprocessedDelta);
fileContent += "<i>Created at: ${formatDate(note.createdAt)} </i>";
fileContent += "<br>";

fileContent += "<hr><br>";
}
});
final preprocessedDelta = preprocessDeltaForPDFExport(note.body);
fileContent += quillDeltaToHtml(preprocessedDelta);

// Add margins to the HTML content
final htmlWithMargins = addMarginsToHTML(fileContent);
fileContent += "<hr><br>";
}
});

await file.writeAsString(htmlWithMargins);
// Add margins to the HTML content
final htmlWithMargins = addMarginsToHTML(fileContent);

var generatedPdfFile = await FlutterHtmlToPdf.convertFromHtmlFile(
file, directory.path, "diaryvault_pdf_export");
await file.writeAsString(htmlWithMargins);

return generatedPdfFile.path;
}
var generatedPdfFile = await FlutterHtmlToPdf.convertFromHtmlFile(
file, directory.path, "diaryvault_pdf_export");

return "";
return generatedPdfFile.path;
} catch (e) {
log.e(e);
rethrow;
Expand Down
6 changes: 4 additions & 2 deletions lib/features/notes/data/repositories/notes_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ class NotesRepository with NoteHelperMixin implements INotesRepository {
});

@override
Future<Either<NotesFailure, List<NoteModel>>> fetchNotes() async {
Future<Either<NotesFailure, List<NoteModel>>> fetchNotes(
{List<String>? noteIds}) async {
try {
// since userId is fetched asynchronously, it will be null first time
final userId = authSessionBloc.state.user?.id ?? "";
var notesList = await notesLocalDataSource.fetchNotes(userId);
var notesList = await notesLocalDataSource.fetchNotes(
authorId: userId, noteIds: noteIds);
return Right(notesList);
} catch (e) {
log.e(e);
Expand Down
3 changes: 2 additions & 1 deletion lib/features/notes/domain/repositories/notes_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ abstract class INotesRepository {
bool dontModifyAnyParameters = false,
});

Future<Either<NotesFailure, List<NoteModel>>> fetchNotes();
Future<Either<NotesFailure, List<NoteModel>>> fetchNotes(
{List<String>? noteIds});

Future<Either<NotesFailure, NoteModel>> getNote(String id);

Expand Down

0 comments on commit 3a22902

Please sign in to comment.