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

Add password protected notes, closes #771 #772

Merged
merged 8 commits into from
Dec 27, 2024
Merged
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
16 changes: 16 additions & 0 deletions api/lib/src/converter/color.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:dart_leap/dart_leap.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

class ColorJsonConverter extends JsonConverter<SRGBColor, int> {
const ColorJsonConverter();

@override
SRGBColor fromJson(int json) {
return SRGBColor(json);
}

@override
int toJson(SRGBColor object) {
return object.value;
}
}
14 changes: 7 additions & 7 deletions api/lib/src/converter/note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ import '../../butterfly_api.dart';

const kArchiveSignature = 0x50;

NoteData noteDataMigrator(Uint8List data) {
NoteData noteDataMigrator(Uint8List data, {String? password}) {
Archive archive;
if (data.isNotEmpty && data[0] != kArchiveSignature) {
final map = json.decode(utf8.decode(data)) as Map<String, dynamic>;
archive = convertLegacyDataToArchive(map);
} else {
archive = ZipDecoder().decodeBytes(data);
archive = ZipDecoder().decodeBytes(data, password: password);
}
return archiveNoteDataMigrator(archive);
return archiveNoteDataMigrator(archive, password: password);
}

NoteData archiveNoteDataMigrator(Archive archive) {
var noteData = NoteData(archive);
NoteData archiveNoteDataMigrator(Archive archive, {String? password}) {
var noteData = NoteData.build(archive, password: password);
var metadata = noteData.getMetadata();
if (metadata != null &&
(metadata.fileVersion ?? kFileVersion) < kFileVersion) {
Expand Down Expand Up @@ -52,7 +52,7 @@ NoteData _migrate(NoteData noteData, FileMetadata metadata) {
}
}
if (version < 10) {
for (final page in noteData.getAssets(kPagesArchiveDirectory)) {
for (final page in noteData.getAssets('$kPagesArchiveDirectory/')) {
final data = noteData.getAsset('$kPagesArchiveDirectory/$page');
if (data == null) continue;
final pageData = json.decode(utf8.decode(data)) as Map<String, dynamic>;
Expand Down Expand Up @@ -89,7 +89,7 @@ NoteData _migrate(NoteData noteData, FileMetadata metadata) {
}
}
if (version < 11) {
for (final page in noteData.getAssets(kPagesArchiveDirectory)) {
for (final page in noteData.getAssets('$kPagesArchiveDirectory/')) {
final data = noteData.getAsset('$kPagesArchiveDirectory/$page');
if (data == null) continue;
final pageData = json.decode(utf8.decode(data)) as Map<String, dynamic>;
Expand Down
17 changes: 8 additions & 9 deletions api/lib/src/converter/xopp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:typed_data';
import 'package:archive/archive.dart';
import 'package:butterfly_api/butterfly_api.dart';
import 'package:butterfly_api/butterfly_text.dart' as text;
import 'package:dart_leap/dart_leap.dart';
import 'package:xml/xml.dart';

List<PathPoint> toPoints(List<double> data) {
Expand All @@ -20,14 +21,12 @@ List<PathPoint> toPoints(List<double> data) {
return points;
}

int _importColor(String value) {
final number = int.parse(value.substring(1), radix: 16);
return (number >> 8 | number << 24);
SRGBColor _importColor(String value) {
return SRGBColor.parse(value);
}

String _exportColor(int value) {
final number = (value >> 8 | value << 24);
return '#${number.toRadixString(16)}';
String _exportColor(SRGBColor value) {
return value.toHexString();
}

(NoteData, PadElement?) getElement(
Expand Down Expand Up @@ -161,8 +160,8 @@ Uint8List xoppExporter(NoteData document) {
builder.element('page', nest: () {
builder.element('background', attributes: {
'type': 'solid',
'color':
_exportColor(page.backgrounds.firstOrNull?.defaultColor ?? 0),
'color': _exportColor(
page.backgrounds.firstOrNull?.defaultColor ?? SRGBColor.white),
'style': 'plain',
});
builder.element('layer', nest: () {
Expand All @@ -185,7 +184,7 @@ Uint8List xoppExporter(NoteData document) {
?.span
: styleSheet?.getParagraphProperty('p')?.span;
builder.element('text', attributes: {
'color': _exportColor(style?.color ?? 0),
'color': _exportColor(style?.color ?? SRGBColor.black),
'size': (style?.size ?? 12).toString(),
'x': e.position.x.toString(),
'y': e.position.y.toString(),
Expand Down
8 changes: 0 additions & 8 deletions api/lib/src/models/asset.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import 'dart:typed_data';

import 'package:butterfly_api/butterfly_api.dart';
import 'package:lw_file_system_api/lw_file_system_api.dart';

enum AssetFileType { note, page, image, markdown, pdf, svg, xopp, archive }

extension AppDocumentLoadExtension on FileSystemFile {
NoteData load({bool disableMigrations = false}) =>
NoteData.fromData(Uint8List.fromList(data),
disableMigrations: disableMigrations);
}

extension AssetLocationFileTypeExtension on AssetLocation {
AssetFileType? get fileType =>
AssetFileTypeHelper.fromFileExtension(fileExtension);
Expand Down
6 changes: 3 additions & 3 deletions api/lib/src/models/background.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:dart_leap/dart_leap.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

import 'colors.dart';
import 'texture.dart';

part 'background.g.dart';
Expand Down Expand Up @@ -36,8 +36,8 @@ sealed class Background with _$Background {
factory Background.fromJson(Map<String, dynamic> json) =>
_$BackgroundFromJson(json);

int get defaultColor => switch (this) {
SRGBColor get defaultColor => switch (this) {
TextureBackground e => e.texture.boxColor,
_ => BasicColors.white
_ => SRGBColor.white
};
}
13 changes: 6 additions & 7 deletions api/lib/src/models/colors.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import 'package:dart_leap/dart_leap.dart';

class BasicColors {
static const white = 0xFFFFFFFF;
static const light = 0xFFD9D9D9;
static const black = 0xFF000000;
static const dark = 0xFF1A1A1A;
static const red = 0xFFF44336;
static const blue = 0xFF2196F3;
static const transparent = 0x00000000;
static const light = SRGBColor(0xFFD9D9D9);
static const dark = SRGBColor(0xFF1A1A1A);
static const red = SRGBColor(0xFFF44336);
static const blue = SRGBColor(0xFF2196F3);

const BasicColors._();
}
59 changes: 45 additions & 14 deletions api/lib/src/models/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,56 @@ import 'palette.dart';

final Set<String> validAssetPaths = {kImagesArchiveDirectory};

@immutable
final class NoteFile {
final Uint8List data;

NoteFile(this.data);

bool isEncrypted() => isZipEncrypted(data);

(String?, NoteData)? _data;

NoteData? load({String? password}) {
if (_data != null && _data?.$1 == password) {
return _data?.$2;
}
try {
final data = NoteData.fromData(this.data, password: password);
_data = (password, data);
return data;
} catch (_) {
return null;
}
}
}

final class NoteData extends ArchiveData<NoteData> {
NoteData(super.archive, {super.state});
NoteData.build(super.archive, {super.password}) : super.build();

factory NoteData.fromData(Uint8List data, {bool disableMigrations = false}) {
factory NoteData.fromData(Uint8List data,
{bool disableMigrations = false, String? password}) {
if (disableMigrations) {
final archive = ZipDecoder().decodeBytes(data);
return NoteData(archive);
final archive = ZipDecoder().decodeBytes(data, password: password);
return NoteData.build(archive, password: password);
}
return noteDataMigrator(data);
return noteDataMigrator(data, password: password);
}

factory NoteData.fromArchive(Archive archive,
{bool disableMigrations = false}) {
{bool disableMigrations = false, String? password}) {
if (disableMigrations) {
return NoteData(archive);
return NoteData.build(archive, password: password);
}
return archiveNoteDataMigrator(archive);
}

factory NoteData.fromJson(dynamic json) => NoteData.fromData(
factory NoteData.fromJson(dynamic json,
{bool disableMigrations = false, String? password}) =>
NoteData.fromData(
base64Decode(json as String),
disableMigrations: disableMigrations,
password: password,
);

NoteFileType? get type => getMetadata()?.type;
Expand Down Expand Up @@ -71,7 +99,7 @@ final class NoteData extends ArchiveData<NoteData> {
'$name${fileExtension.isNotEmpty ? '.$fileExtension' : ''}';

String findUniqueName(String path, String fileExtension, [String name = '']) {
final assets = getAssets(path);
final assets = getAssets('$path/');
if (!assets.contains(_getFileName(name, fileExtension)) &&
name.trim().isNotEmpty) {
return _getFileName(name, fileExtension);
Expand Down Expand Up @@ -238,7 +266,7 @@ final class NoteData extends ArchiveData<NoteData> {

@useResult
List<(int, String, String)> _getPagesOrder() =>
getAssets(kPagesArchiveDirectory, true).map((e) {
getAssets('$kPagesArchiveDirectory/', true).map((e) {
if (e.contains('.')) {
final split = e.split('.');
return (
Expand Down Expand Up @@ -319,13 +347,13 @@ final class NoteData extends ArchiveData<NoteData> {
removeAsset('$kPacksArchiveDirectory/$name.bfly');

@useResult
Iterable<String> getPacks() => getAssets(kPacksArchiveDirectory, true);
Iterable<String> getPacks() => getAssets('$kPacksArchiveDirectory/', true);

// Pack specific

@useResult
Iterable<String> getComponents() =>
getAssets(kComponentsArchiveDirectory, true);
getAssets('$kComponentsArchiveDirectory/', true);

@useResult
ButterflyComponent? getComponent(String componentName) {
Expand All @@ -348,7 +376,7 @@ final class NoteData extends ArchiveData<NoteData> {
removeAsset('$kComponentsArchiveDirectory/$name.json');

@useResult
Iterable<String> getStyles() => getAssets(kStylesArchiveDirectory, true);
Iterable<String> getStyles() => getAssets('$kStylesArchiveDirectory/', true);

@useResult
TextStyleSheet? getStyle(String styleName) {
Expand Down Expand Up @@ -383,7 +411,8 @@ final class NoteData extends ArchiveData<NoteData> {
}

@useResult
Iterable<String> getPalettes() => getAssets(kPalettesArchiveDirectory, true);
Iterable<String> getPalettes() =>
getAssets('$kPalettesArchiveDirectory/', true);

@useResult
ColorPalette? getPalette(String paletteName) {
Expand Down Expand Up @@ -425,4 +454,6 @@ final class NoteData extends ArchiveData<NoteData> {
final removed = Set<String>.from(state.removed)..remove(path);
return updateState(state.copyWith(removed: removed));
}

NoteFile toFile() => NoteFile(exportAsBytes());
}
10 changes: 6 additions & 4 deletions api/lib/src/models/element.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'dart:math';

import 'package:butterfly_api/src/converter/color.dart';
import 'package:dart_leap/dart_leap.dart';

import '../converter/core.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

import '../converter/id.dart';
import 'colors.dart';
import 'pack.dart';
import 'point.dart';
import 'property.dart';
Expand Down Expand Up @@ -60,7 +62,7 @@ mixin LabelElement {
double get scale;
PackAssetLocation get styleSheet;
ElementConstraint get constraint;
int get foreground;
SRGBColor get foreground;

AreaProperty get areaProperty => switch (this) {
MarkdownElement e => e.areaProperty,
Expand Down Expand Up @@ -102,7 +104,7 @@ sealed class PadElement with _$PadElement {
@Default(PackAssetLocation()) PackAssetLocation styleSheet,
required TextArea area,
@Default(ElementConstraint(size: 1000)) ElementConstraint constraint,
@Default(BasicColors.black) int foreground,
@Default(SRGBColor.black) @ColorJsonConverter() SRGBColor foreground,
@Default({}) Map<String, dynamic> extra,
}) = TextElement;

Expand All @@ -119,7 +121,7 @@ sealed class PadElement with _$PadElement {
@Default(AreaProperty()) AreaProperty areaProperty,
required String text,
@Default(ElementConstraint(size: 1000)) ElementConstraint constraint,
@Default(BasicColors.black) int foreground,
@Default(SRGBColor.black) @ColorJsonConverter() SRGBColor foreground,
@Default({}) Map<String, dynamic> extra,
}) = MarkdownElement;

Expand Down
Loading
Loading