Skip to content
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added .vs/AppFlowy/v17/.wsuo
Binary file not shown.
12 changes: 12 additions & 0 deletions .vs/AppFlowy/v17/DocumentLayout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Chithra\\AppFlowy\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}
Binary file added .vs/AppFlowy/v17/workspaceFileList.bin
Binary file not shown.
8 changes: 8 additions & 0 deletions .vs/VSWorkspaceState.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"ExpandedNodes": [
"",
"\\frontend"
],
"SelectedNode": "\\frontend",
"PreviewInSolutionExplorer": false
}
Binary file added .vs/slnx.sqlite
Binary file not shown.
11 changes: 11 additions & 0 deletions frontend/appflowy_flutter/lib/workspace/application/tag/tag.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Tag {
final String id;
final String name;
final int color;

const Tag({
required this.id,
required this.name,
required this.color,
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'tag.dart';

class TagFilter {
static bool match({
required List<Tag> tags,
required String query,
}) {
if (query.isEmpty) return true;

final lower = query.toLowerCase();
return tags.any((tag) => tag.name.toLowerCase().contains(lower));
Comment on lines +4 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Trimming and normalizing the query would make matching more robust.

Because the emptiness check runs before lowercasing, a query like ' ' is treated as non-empty and never matches anything. Trim and lowercase once (e.g. final normalized = query.trim().toLowerCase();) and use normalized.isEmpty and normalized for matching to handle whitespace-only queries correctly.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ import 'package:provider/provider.dart';
import 'package:time/time.dart';
import 'package:universal_platform/universal_platform.dart';
import 'package:window_manager/window_manager.dart';

import 'home_layout.dart';

import '../widgets/tags/tag_search_bar.dart';


typedef NavigationCallback = void Function(String id);

abstract class HomeStackDelegate {
Expand Down Expand Up @@ -80,6 +82,17 @@ class _HomeStackState extends State<HomeStack> with WindowListener {
},
),
),

//tag feature
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TagSearchBar(
onChanged: (query) {
// TEMP: just prints text
debugPrint('Search query: $query');
},
),
),
Expanded(
child: IndexedStack(
index: selectedIndex,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import '../../../application/tag/tag.dart';

class TagEditor extends StatefulWidget {
final List<Tag> tags;
final ValueChanged<List<Tag>> onChanged;

const TagEditor({
super.key,
required this.tags,
required this.onChanged,
});

@override
State<TagEditor> createState() => _TagEditorState();
}

class _TagEditorState extends State<TagEditor> {
final controller = TextEditingController();

void _addTag(String value) {
if (value.trim().isEmpty) return;

widget.onChanged([
...widget.tags,
Tag(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: value.trim(),
color: Colors.blue.value,
),
]);

controller.clear();
}

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 6,
children: widget.tags.map((tag) {
return Chip(
label: Text(tag.name),
onDeleted: () {
widget.onChanged(
widget.tags.where((t) => t.id != tag.id).toList(),
);
},
);
}).toList(),
),
const SizedBox(height: 6),
TextField(
controller: controller,
onSubmitted: _addTag,
decoration: const InputDecoration(
hintText: 'Add tag',
isDense: true,
),
),
],
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';

class TagSearchBar extends StatelessWidget {
final ValueChanged<String> onChanged;

const TagSearchBar({
super.key,
required this.onChanged,
});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8),
child: TextField(
decoration: const InputDecoration(
hintText: 'Search by tag...',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
),
onChanged: onChanged,
),
);
}
}