Skip to content

Commit 6faafe1

Browse files
author
davidwei
committed
Add privacy control next to save button with visual feedback
- Create PrivacySaveFab component combining privacy toggle and save button - Replace AppBar save button with FAB in note_detail.dart - Add lock icon next to FAB in both new_note and note_detail pages - Remove lock icon from bottom action buttons in note_edit.dart - Add subtle text color for visual feedback: blue for private, green for public - Disable privacy toggle during save to prevent race conditions This brings the privacy control closer to the save action and provides clear visual feedback (icon color, border color, and text color) for private vs public notes.
1 parent fa55eb4 commit 6faafe1

4 files changed

Lines changed: 80 additions & 33 deletions

File tree

lib/screens/components/note_edit.dart

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,11 @@ class NoteEditState extends State<NoteEdit> {
8484
focusNode: noteModel.focusNode,
8585
keyboardType: TextInputType.multiline,
8686
maxLines: null,
87-
expands: true,
8887
textAlignVertical: TextAlignVertical.top,
88+
expands: true,
89+
style: TextStyle(
90+
color: noteModel.isPrivate ? Colors.blue.shade300 : Colors.green.shade300,
91+
),
8992
decoration: InputDecoration(
9093
hintText: prompt,
9194
border: OutlineInputBorder(
@@ -119,14 +122,6 @@ class NoteEditState extends State<NoteEdit> {
119122
Row(
120123
mainAxisAlignment: MainAxisAlignment.start,
121124
children: [
122-
_buildActionButton(
123-
context,
124-
noteModel,
125-
icon: noteModel.isPrivate ? Icons.lock : Icons.lock_open,
126-
color: noteModel.isPrivate ? Colors.blue : Colors.grey,
127-
onTap: () => noteModel.togglePrivate(),
128-
isSmallScreen: isSmallScreen,
129-
),
130125
_buildActionButton(
131126
context,
132127
noteModel,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:provider/provider.dart';
3+
import '../../models/note_model.dart';
4+
import 'shared_fab.dart';
5+
6+
/// Privacy toggle + Save FAB combination widget
7+
class PrivacySaveFab extends StatelessWidget {
8+
final bool isSaving;
9+
final VoidCallback? onSave;
10+
final bool mini;
11+
final String? heroTag;
12+
13+
const PrivacySaveFab({
14+
Key? key,
15+
required this.isSaving,
16+
this.onSave,
17+
this.mini = false,
18+
this.heroTag,
19+
}) : super(key: key);
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
return Consumer<NoteModel>(
24+
builder: (context, noteModel, child) {
25+
return Opacity(
26+
opacity: 0.85,
27+
child: Row(
28+
mainAxisAlignment: MainAxisAlignment.end,
29+
crossAxisAlignment: CrossAxisAlignment.center,
30+
children: [
31+
Padding(
32+
padding: const EdgeInsets.only(right: 12.0),
33+
child: IconButton(
34+
icon: Icon(
35+
noteModel.isPrivate ? Icons.lock : Icons.lock_open,
36+
color: noteModel.isPrivate ? Colors.blue : Colors.grey,
37+
),
38+
onPressed: isSaving ? null : () {
39+
noteModel.togglePrivate();
40+
},
41+
tooltip: noteModel.isPrivate ? 'Private' : 'Public',
42+
),
43+
),
44+
SharedFab(
45+
icon: isSaving ? Icons.hourglass_top : Icons.save,
46+
isPrivate: noteModel.isPrivate,
47+
busy: isSaving,
48+
mini: mini,
49+
onPressed: isSaving ? null : onSave,
50+
heroTag: heroTag,
51+
),
52+
],
53+
),
54+
);
55+
},
56+
);
57+
}
58+
}

lib/screens/new_note/new_note.dart

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import '../../services/dialog_services.dart';
1111
import '../../services/draft_service.dart';
1212
import '../../utils/util.dart';
1313
import '../components/note_edit.dart';
14-
import 'package:happy_notes/screens/components/shared_fab.dart';
14+
import '../components/privacy_save_fab.dart';
1515
import '../components/hour_picker_dialog.dart';
1616

1717
class NewNote extends StatefulWidget {
@@ -243,7 +243,7 @@ class NewNoteState extends State<NewNote> {
243243
return Text(
244244
_getNoteTitle(noteModel),
245245
style: TextStyle(
246-
color: noteModel.isPrivate ? Colors.red : Colors.green, // Change colors accordingly
246+
color: noteModel.isPrivate ? Colors.blue : Colors.green, // Change colors accordingly
247247
),
248248
);
249249
},
@@ -255,20 +255,11 @@ class NewNoteState extends State<NewNote> {
255255
onSubmit: _floatingActionButtonOnPressed,
256256
),
257257
),
258-
floatingActionButton: Opacity(
259-
opacity: 0.85,
260-
child: Consumer<NoteModel>(
261-
builder: (context, nm, child) {
262-
return SharedFab(
263-
icon: isSaving ? Icons.hourglass_top : Icons.save,
264-
isPrivate: nm.isPrivate,
265-
busy: isSaving,
266-
mini: true,
267-
onPressed: _floatingActionButtonOnPressed,
268-
heroTag: 'new_note_save_fab',
269-
);
270-
},
271-
),
258+
floatingActionButton: PrivacySaveFab(
259+
isSaving: isSaving,
260+
onSave: isSaving ? null : _floatingActionButtonOnPressed,
261+
mini: true,
262+
heroTag: 'new_note_save_fab',
272263
),
273264
),
274265
);

lib/screens/note_detail/note_detail.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import '../account/user_session.dart';
99
import '../components/note_view.dart';
1010
import '../trash_bin/trash_bin_page.dart';
1111
import '../../providers/notes_provider.dart';
12-
import '../components/shared_fab.dart';
12+
import '../components/privacy_save_fab.dart';
1313
import '../../utils/util.dart';
1414
import '../../services/seq_logger.dart';
1515
import '../../services/note_update_coordinator.dart';
@@ -226,6 +226,7 @@ class NoteDetailState extends State<NoteDetail> with RouteAware {
226226
return true;
227227
}
228228

229+
229230
@override
230231
Widget build(BuildContext context) {
231232
if (_isLoading) {
@@ -256,17 +257,12 @@ class NoteDetailState extends State<NoteDetail> with RouteAware {
256257
title: Text(
257258
'${note?.id} - ${noteModel.isPrivate ? 'Private' : 'Public'}${noteModel.isMarkdown ? ' with M↓' : ''}',
258259
style: TextStyle(
259-
color: noteModel.isPrivate ? Colors.red : Colors.green, // Change colors accordingly
260+
color: noteModel.isPrivate ? Colors.blue : Colors.green, // Change colors accordingly
260261
),
261262
),
262263
actions: [
263264
if (note?.userId == UserSession().id) ...[
264-
if (_isEditing)
265-
IconButton(
266-
icon: _isSaving ? const CircularProgressIndicator() : const Icon(Icons.check),
267-
onPressed: _saveNoteHandler,
268-
)
269-
else
265+
if (!_isEditing)
270266
IconButton(
271267
icon: const Icon(Icons.edit),
272268
onPressed: _enterEditingMode,
@@ -366,6 +362,13 @@ class NoteDetailState extends State<NoteDetail> with RouteAware {
366362
],
367363
),
368364
),
365+
floatingActionButton: _isEditing
366+
? PrivacySaveFab(
367+
isSaving: _isSaving,
368+
onSave: _isSaving ? null : _saveNoteHandler,
369+
heroTag: 'note_detail_save_fab',
370+
)
371+
: null,
369372
),
370373
);
371374
});

0 commit comments

Comments
 (0)