Skip to content
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
19 changes: 18 additions & 1 deletion lib/database/app_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class AppDatabase {

Future<void> open() async {
_database = await openDatabase(_internalPath!,
version: 4, onCreate: _createDatabase, onUpgrade: _onUpgrade);
version: 5, onCreate: _createDatabase, onUpgrade: _onUpgrade);

await EntriesProvider.instance.load();
await EntryImagesProvider.instance.load();
Expand Down Expand Up @@ -331,6 +331,7 @@ CREATE TABLE $imagesTable (
''');

await _createTagTables(db);
await _createTemplateTagTable(db);
await TagsProvider.instance.createDefaultTags();

await _createWelcomeEntry();
Expand Down Expand Up @@ -446,6 +447,19 @@ CREATE TABLE $entryTagsTable (
''');
}

Future<void> _createTemplateTagTable(Database db) async {
await db.execute('''
CREATE TABLE $templateTagsTable (
${TemplateTagFields.id} INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
${TemplateTagFields.templateId} INTEGER NOT NULL,
${TemplateTagFields.tagId} INTEGER NOT NULL,
${TemplateTagFields.timeCreate} DATETIME NOT NULL DEFAULT (DATETIME('now')),
FOREIGN KEY (${TemplateTagFields.templateId}) REFERENCES $templatesTable (id),
FOREIGN KEY (${TemplateTagFields.tagId}) REFERENCES $tagsTable (id)
)
''');
}

void _onUpgrade(Database db, int oldVersion, int newVersion) async {
_database = db;
// In this case, oldVersion is 1, newVersion is 2
Expand Down Expand Up @@ -515,5 +529,8 @@ DROP TABLE old_entries;
await _createTagTables(db);
await TagsProvider.instance.createDefaultTags();
}
if (oldVersion <= 4) {
await _createTemplateTagTable(db);
}
}
}
43 changes: 43 additions & 0 deletions lib/database/template_tag_dao.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:daily_you/database/app_database.dart';
import 'package:daily_you/models/tag.dart';

class TemplateTagDao {
static Future<List<TemplateTag>> getAll() async {
final db = AppDatabase.instance.database!;
final result = await db.query(templateTagsTable);
return result.map((json) => TemplateTag.fromJson(json)).toList();
}

static Future<TemplateTag> add(TemplateTag templateTag) async {
final db = AppDatabase.instance.database!;
final id = await db.insert(templateTagsTable, templateTag.toJson());
return templateTag.copy(id: id);
}

static Future<void> remove(int id) async {
final db = AppDatabase.instance.database!;
await db.delete(
templateTagsTable,
where: '${TemplateTagFields.id} = ?',
whereArgs: [id],
);
}

static Future<void> removeAllForTemplate(int templateId) async {
final db = AppDatabase.instance.database!;
await db.delete(
templateTagsTable,
where: '${TemplateTagFields.templateId} = ?',
whereArgs: [templateId],
);
}

static Future<void> removeAllForTag(int tagId) async {
final db = AppDatabase.instance.database!;
await db.delete(
templateTagsTable,
where: '${TemplateTagFields.tagId} = ?',
whereArgs: [tagId],
);
}
}
51 changes: 51 additions & 0 deletions lib/models/tag.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:daily_you/models/tag_icon_type.dart';

const String tagsTable = 'tags';
const String entryTagsTable = 'entry_tags';
const String templateTagsTable = 'template_tags';

enum TagType {
label,
Expand Down Expand Up @@ -169,3 +170,53 @@ class EntryTag {
EntryTagFields.timeCreate: timeCreate.toIso8601String(),
};
}

class TemplateTagFields {
static const List<String> values = [id, templateId, tagId, timeCreate];
static const String id = 'id';
static const String templateId = 'template_id';
static const String tagId = 'tag_id';
static const String timeCreate = 'time_create';
}

class TemplateTag {
final int? id;
final int templateId;
final int tagId;
final DateTime timeCreate;

const TemplateTag({
this.id,
required this.templateId,
required this.tagId,
required this.timeCreate,
});

TemplateTag copy({
int? id,
int? templateId,
int? tagId,
DateTime? timeCreate,
}) =>
TemplateTag(
id: id ?? this.id,
templateId: templateId ?? this.templateId,
tagId: tagId ?? this.tagId,
timeCreate: timeCreate ?? this.timeCreate,
);

static TemplateTag fromJson(Map<String, Object?> json) => TemplateTag(
id: json[TemplateTagFields.id] as int?,
templateId: json[TemplateTagFields.templateId] as int,
tagId: json[TemplateTagFields.tagId] as int,
timeCreate:
DateTime.parse(json[TemplateTagFields.timeCreate] as String),
);

Map<String, Object?> toJson() => {
TemplateTagFields.id: id,
TemplateTagFields.templateId: templateId,
TemplateTagFields.tagId: tagId,
TemplateTagFields.timeCreate: timeCreate.toIso8601String(),
};
}
122 changes: 88 additions & 34 deletions lib/pages/edit_entry_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import 'package:daily_you/notification_manager.dart';
import 'package:daily_you/models/tag.dart';
import 'package:daily_you/providers/entries_provider.dart';
import 'package:daily_you/providers/entry_images_provider.dart';
import 'package:daily_you/models/template.dart';
import 'package:daily_you/providers/tags_provider.dart';
import 'package:daily_you/providers/templates_provider.dart';
import 'package:daily_you/widgets/entry_tag_chips.dart';
import 'package:daily_you/widgets/tag_attachment_source.dart';
import 'package:daily_you/widgets/tag_grouped_chip_list.dart';
import 'package:daily_you/widgets/tag_picker_dialog.dart';
import 'package:daily_you/widgets/tag_chip.dart';
import 'package:daily_you/widgets/tracker_value_dialog.dart';
import 'package:daily_you/time_manager.dart';
import 'package:provider/provider.dart';
import 'package:daily_you/pages/full_screen_text_editor_page.dart';
import 'package:daily_you/widgets/edit_toolbar.dart';
import 'package:daily_you/widgets/entry_image_editable_list.dart';
Expand Down Expand Up @@ -64,6 +66,9 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
bool _newEntry = false;
bool _creatingNewEntry = false;
Timer? _debounceTimer;
late TagAttachmentSource _tagSource;
// Tag ids seeded from the default template, or already on the entry
late Set<int> _seededTagIds;

Future<void> _initEntry() async {
if (widget.entry == null) {
Expand All @@ -73,9 +78,15 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
: (widget.overrideCreateDate ?? DateTime.now());
var text = "";
final defaultTemplate = TemplatesProvider.instance.getDefaultTemplate();
final defaultTagIds = <int>[];
if (defaultTemplate != null) {
text = defaultTemplate.text ?? "";
defaultTagIds.addAll(TagsProvider.instance
.getTemplateTagsForTemplate(defaultTemplate.id!)
.map((templateTag) => templateTag.tagId));
}
_tagSource = TagAttachmentSource(
supportsValues: true, initialTagIds: defaultTagIds);
_entry = Entry(
text: text,
mood: null,
Expand All @@ -88,7 +99,11 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
} else {
_entry = widget.entry!;
id = _entry.id ?? -1;
_tagSource = TagAttachmentSource.fromEntryTags(
TagsProvider.instance.getEntryTagsForEntry(id));
}
_seededTagIds = _tagSource.attachedTagIds.toSet();
_tagSource.addListener(_onTagsChanged);
_lastMood = _entry.mood;
mood = _entry.mood;
_lastEntryDate = _entry.timeCreate;
Expand Down Expand Up @@ -126,6 +141,8 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
_textEditingController.dispose();
_undoController.dispose();
_debounceTimer?.cancel();
_tagSource.removeListener(_onTagsChanged);
_tagSource.dispose();
super.dispose();
}

Expand Down Expand Up @@ -261,6 +278,7 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
controller: _textEditingController,
undoController: _undoController,
focusNode: _focusNode,
onTemplateInserted: _applyInsertedTemplateTags,
trailer: _buildTagsButton(context, theme),
),
),
Expand Down Expand Up @@ -306,28 +324,40 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
"save-entry", Duration(seconds: 5), () => _saveEntry());
}),
),
EntryTagChips(
entryId: id,
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
chipBuilder: (tag, entryTag) => TagChip(
_buildTagChips(),
],
),
);
}

Widget _buildTagChips() {
const padding = EdgeInsets.only(left: 8, right: 8, bottom: 8);
return ListenableBuilder(
listenable: _tagSource,
builder: (context, _) {
final attachedIds = _tagSource.attachedTagIds;
if (attachedIds.isEmpty) return const SizedBox.shrink();
final tagsProvider = Provider.of<TagsProvider>(context);
final attachedSet = attachedIds.toSet();
final tagPool = tagsProvider.tags
.where((tag) => attachedSet.contains(tag.id))
.toList();
final sections = tagsProvider.buildSections('', tagPool: tagPool);
return Padding(
padding: padding,
child: TagGroupedChipList(
sections: sections,
chipBuilder: (tag) => TagChip(
tag: tag,
value: entryTag.value,
value: _tagSource.valueFor(tag.id!),
onTap: tag.tagType == TagType.tracker
? () async {
final newValue = await showTrackerValueDialog(
context, tag,
initialValue: entryTag.value);
if (newValue != null) {
await TagsProvider.instance
.updateEntryTag(entryTag.copy(value: newValue));
}
}
? () => _tagSource.editValue(context, tag)
: null,
onRemove: () => TagsProvider.instance.removeEntryTag(entryTag),
onRemove: () => _tagSource.detach(tag.id!),
),
)
],
),
),
);
},
);
}

Expand Down Expand Up @@ -396,17 +426,11 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
Widget _buildTagsButton(BuildContext context, ThemeData theme) {
return IconButton(
onPressed: () async {
if (id == -1) {
await _saveEntry(forceCreate: true);
}
if (id == -1) return;
if (context.mounted) {
await showDialog(
context: context,
builder: (_) =>
TagPickerDialog(mode: TagPickerMode.addToEntry, entryId: id),
);
}
await showDialog(
context: context,
builder: (_) =>
TagPickerDialog(mode: TagPickerMode.attach, source: _tagSource),
);
},
icon: Icon(
Icons.local_offer_rounded,
Expand Down Expand Up @@ -498,7 +522,7 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
await _saveEntry();
}

Future<void> _saveEntry({bool forceCreate = false}) async {
Future<void> _saveEntry() async {
// Saving is guarded since quickly entering and exiting the app could trigger
// multiple async saves.
if (_savingEntry == false) {
Expand All @@ -515,11 +539,13 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
final hasMoodChange = updatedEntry.mood != _lastMood;
final hasDateChange = updatedEntry.timeCreate != _lastEntryDate;

final hasTagChange = _tagsExceedSeed();

if (_newEntry) {
if (forceCreate ||
hasTextChange ||
if (hasTextChange ||
hasMoodChange ||
hasDateChange ||
hasTagChange ||
_currentImages.isNotEmpty) {
if (Platform.isAndroid &&
TimeManager.isSameDay(DateTime.now(), updatedEntry.timeCreate)) {
Expand All @@ -531,6 +557,8 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
_lastText = _entry.text;
_lastMood = _entry.mood;
_lastEntryDate = _entry.timeCreate;
await TagsProvider.instance
.setEntryTags(id, _tagSource.toEntryTags(id));
}
} else {
if (hasTextChange || hasMoodChange || hasDateChange) {
Expand All @@ -543,6 +571,8 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
_lastEntryDate = updatedEntry.timeCreate;
await EntriesProvider.instance.update(updatedEntry);
}
await TagsProvider.instance
.setEntryTags(id, _tagSource.toEntryTags(id));
}
// Images will update if they changed
await _saveOrUpdateImage(id);
Expand Down Expand Up @@ -595,6 +625,30 @@ class _AddEditEntryPageState extends State<AddEditEntryPage>
}
}

void _onTagsChanged() {
EasyDebounce.debounce(
"save-entry", const Duration(seconds: 5), () => _saveEntry());
}

// Whether the working set holds anything beyond the seeded default tags
bool _tagsExceedSeed() {
for (final tagId in _tagSource.attachedTagIds) {
if (!_seededTagIds.contains(tagId)) return true;
if (_tagSource.valueFor(tagId) != null) return true;
}
return false;
}

void _applyInsertedTemplateTags(Template template) {
if (template.id == null) return;
final templateTagIds = TagsProvider.instance
.getTemplateTagsForTemplate(template.id!)
.map((templateTag) => templateTag.tagId);
for (final tagId in templateTagIds) {
_tagSource.addTagId(tagId);
}
}

Future<void> _addImage(List<String> imgPaths) async {
for (var imgPath in imgPaths) {
// Add image to the end by giving it the lowest rank
Expand Down
Loading
Loading