2024-05-30 20:44:06 -06:00
|
|
|
import 'package:colorful_print/colorful_print.dart';
|
2023-07-27 01:40:26 -06:00
|
|
|
import 'package:dio/dio.dart';
|
2023-07-22 21:29:32 -06:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
2023-07-27 01:40:26 -06:00
|
|
|
import 'package:rluv/global/utils.dart';
|
|
|
|
import 'package:rluv/models/shared_note.dart';
|
2023-07-22 21:29:32 -06:00
|
|
|
|
2023-07-27 01:40:26 -06:00
|
|
|
import '../../../global/api.dart';
|
2023-07-22 21:29:32 -06:00
|
|
|
import '../../../global/store.dart';
|
2023-07-27 01:40:26 -06:00
|
|
|
import '../../../global/styles.dart';
|
|
|
|
import '../../../global/widgets/ui_button.dart';
|
2023-07-22 21:29:32 -06:00
|
|
|
|
|
|
|
class SharedNotesScreen extends ConsumerStatefulWidget {
|
|
|
|
const SharedNotesScreen({super.key, required this.initialData});
|
|
|
|
|
|
|
|
final Map<String, dynamic> initialData;
|
|
|
|
@override
|
|
|
|
ConsumerState<SharedNotesScreen> createState() => _SharedNotesScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SharedNotesScreenState extends ConsumerState<SharedNotesScreen> {
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-05-30 20:44:06 -06:00
|
|
|
final screen = MediaQuery.of(context).size;
|
2023-07-27 01:40:26 -06:00
|
|
|
final sharedNotes = ref.watch(sharedNotesProvider);
|
2023-07-22 21:29:32 -06:00
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
const Padding(
|
|
|
|
padding: EdgeInsets.all(18.0),
|
|
|
|
child: Text(
|
|
|
|
'Notes:',
|
|
|
|
style: TextStyle(fontSize: 20),
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
),
|
2023-07-27 01:40:26 -06:00
|
|
|
if (sharedNotes.isEmpty) ...[
|
|
|
|
const Text('Add notes to get started:'),
|
|
|
|
],
|
2023-07-22 21:29:32 -06:00
|
|
|
if (sharedNotes.isNotEmpty)
|
2023-07-27 01:40:26 -06:00
|
|
|
SizedBox(
|
|
|
|
height: screen.height * 0.65,
|
|
|
|
child: GridView.builder(
|
|
|
|
itemCount: sharedNotes.length,
|
|
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
|
|
crossAxisCount: 2,
|
|
|
|
),
|
|
|
|
itemBuilder: (BuildContext context, int index) {
|
|
|
|
final note = sharedNotes[index];
|
2024-05-30 20:44:06 -06:00
|
|
|
final colorBrightness = note.color == null ? Brightness.light : getBrightness(note.color!);
|
2023-07-27 01:40:26 -06:00
|
|
|
final textStyle = TextStyle(
|
|
|
|
fontSize: 16.0,
|
2024-05-30 20:44:06 -06:00
|
|
|
color: colorBrightness == Brightness.light ? Colors.black54 : Colors.white70,
|
2023-07-27 01:40:26 -06:00
|
|
|
);
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.all(8.0),
|
|
|
|
child: InkWell(
|
|
|
|
child: Card(
|
|
|
|
color: note.color ?? Styles.washedStone,
|
|
|
|
child: SizedBox(
|
|
|
|
width: screen.width * 0.4,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(8.0),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
2024-05-30 20:44:06 -06:00
|
|
|
FittedBox(child: Text(note.title, style: const TextStyle(fontSize: 20))),
|
2023-07-27 01:40:26 -06:00
|
|
|
Flexible(
|
|
|
|
child: Text(
|
2024-05-30 20:44:06 -06:00
|
|
|
note.content.length > 80 ? "${note.content.substring(0, 75)}..." : note.content,
|
2023-07-27 01:40:26 -06:00
|
|
|
style: textStyle,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
showModalBottomSheet(
|
|
|
|
isScrollControlled: true,
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.circular(20.0),
|
|
|
|
),
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
context: context,
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
return Padding(
|
2024-05-30 20:44:06 -06:00
|
|
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
2023-07-27 01:40:26 -06:00
|
|
|
child: NoteBottomSheet(
|
|
|
|
index: index,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
2023-07-22 21:29:32 -06:00
|
|
|
),
|
2023-07-27 01:40:26 -06:00
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
2023-07-22 21:29:32 -06:00
|
|
|
),
|
2023-07-27 01:40:26 -06:00
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 12.0),
|
|
|
|
child: UiButton(
|
|
|
|
text: 'Add Note',
|
|
|
|
onPressed: () {
|
|
|
|
showModalBottomSheet(
|
|
|
|
isScrollControlled: true,
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.circular(20.0),
|
|
|
|
),
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
context: context,
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
return Padding(
|
2024-05-30 20:44:06 -06:00
|
|
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
2023-07-27 01:40:26 -06:00
|
|
|
child: const NoteBottomSheet(
|
|
|
|
index: null,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
),
|
2023-07-22 21:29:32 -06:00
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-07-27 01:40:26 -06:00
|
|
|
|
|
|
|
class NoteBottomSheet extends ConsumerStatefulWidget {
|
|
|
|
const NoteBottomSheet({super.key, required this.index});
|
|
|
|
|
|
|
|
final int? index;
|
|
|
|
|
|
|
|
@override
|
|
|
|
ConsumerState<NoteBottomSheet> createState() => _NoteBottomSheetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _NoteBottomSheetState extends ConsumerState<NoteBottomSheet> {
|
|
|
|
late final SharedNote? oldNote;
|
|
|
|
late SharedNote newNote;
|
|
|
|
|
|
|
|
final titleFocusNode = FocusNode();
|
|
|
|
final contentFocusNode = FocusNode();
|
|
|
|
late final TextEditingController titleController;
|
|
|
|
late final TextEditingController contentController;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
if (widget.index == null) {
|
|
|
|
oldNote = null;
|
|
|
|
final family = ref.read(familyProvider);
|
|
|
|
final user = ref.read(userProvider);
|
|
|
|
newNote = SharedNote(
|
|
|
|
familyId: family!.id,
|
|
|
|
createdByUserId: user!.id!,
|
|
|
|
title: 'Title',
|
|
|
|
content: '',
|
|
|
|
tagIds: [],
|
|
|
|
color: Styles.washedStone,
|
|
|
|
isMarkdown: false,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
oldNote = ref.read(sharedNotesProvider).elementAt(widget.index!);
|
|
|
|
newNote = SharedNote.copy(oldNote!);
|
|
|
|
}
|
|
|
|
titleController = TextEditingController(text: newNote.title);
|
|
|
|
contentController = TextEditingController(text: newNote.content);
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback(
|
|
|
|
(timeStamp) => titleFocusNode.requestFocus(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-05-30 20:44:06 -06:00
|
|
|
final screen = MediaQuery.of(context).size;
|
2023-07-27 01:40:26 -06:00
|
|
|
return Container(
|
|
|
|
width: screen.width,
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: newNote.color ?? Styles.washedStone,
|
2024-05-30 20:44:06 -06:00
|
|
|
borderRadius: const BorderRadius.only(topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
|
2023-07-27 01:40:26 -06:00
|
|
|
boxShadow: const [
|
2024-05-30 20:44:06 -06:00
|
|
|
BoxShadow(color: Colors.black26, spreadRadius: 5.0, blurRadius: 2.0, offset: Offset(0, -2))
|
2023-07-27 01:40:26 -06:00
|
|
|
]),
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(16.0),
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
TextField(
|
|
|
|
style: const TextStyle(fontSize: 18.0),
|
|
|
|
maxLines: 1,
|
|
|
|
focusNode: titleFocusNode,
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
controller: titleController,
|
|
|
|
// decoration: Styles.inputLavenderBubble(),
|
|
|
|
),
|
|
|
|
TextField(
|
|
|
|
style: const TextStyle(fontSize: 18.0),
|
|
|
|
maxLines: 6,
|
|
|
|
focusNode: contentFocusNode,
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
controller: contentController,
|
|
|
|
// decoration: Styles.inputLavenderBubble(),
|
|
|
|
),
|
|
|
|
Padding(
|
2024-05-30 20:44:06 -06:00
|
|
|
padding: const EdgeInsets.symmetric(vertical: 28.0, horizontal: 36.0),
|
2023-07-27 01:40:26 -06:00
|
|
|
child: UiButton(
|
|
|
|
onPressed: !noteChanged(oldNote, newNote)
|
|
|
|
? null
|
|
|
|
: () async {
|
|
|
|
newNote.content = contentController.text;
|
|
|
|
newNote.title = titleController.text;
|
|
|
|
try {
|
|
|
|
Response? res;
|
|
|
|
if (widget.index == null) {
|
2024-05-30 20:44:06 -06:00
|
|
|
res = await ref.read(apiProvider).post('shared_note', data: newNote.toJson());
|
2023-07-27 01:40:26 -06:00
|
|
|
} else {
|
2024-05-30 20:44:06 -06:00
|
|
|
res = await ref.read(apiProvider).put('shared_note', data: newNote.toJson());
|
2023-07-27 01:40:26 -06:00
|
|
|
}
|
|
|
|
if (res.data != null && res.data['success']) {
|
|
|
|
if (widget.index == null) {
|
2024-05-30 20:44:06 -06:00
|
|
|
ref.read(dashboardProvider.notifier).add({'shared_notes': res.data});
|
2023-07-27 01:40:26 -06:00
|
|
|
} else {
|
2024-05-30 20:44:06 -06:00
|
|
|
ref.read(dashboardProvider.notifier).update({'shared_notes': res.data});
|
2023-07-27 01:40:26 -06:00
|
|
|
}
|
2024-05-30 20:44:06 -06:00
|
|
|
showSnack(ref: ref, text: 'Added note', type: SnackType.success);
|
2023-07-27 01:40:26 -06:00
|
|
|
} else {
|
|
|
|
showSnack(
|
|
|
|
ref: ref,
|
2024-05-30 20:44:06 -06:00
|
|
|
text: res.data['message'] ?? 'Unexpected error occurred',
|
2023-07-27 01:40:26 -06:00
|
|
|
type: SnackType.error);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2024-05-30 20:44:06 -06:00
|
|
|
showSnack(ref: ref, text: 'Unexpected error occurred', type: SnackType.error);
|
|
|
|
printColor(err, textColor: TextColor.red);
|
2023-07-27 01:40:26 -06:00
|
|
|
}
|
2023-08-17 15:16:08 -06:00
|
|
|
// ignore: use_build_context_synchronously
|
2023-07-27 01:40:26 -06:00
|
|
|
Navigator.pop(context);
|
|
|
|
},
|
|
|
|
text: 'Save',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool noteChanged(SharedNote? n, SharedNote m) {
|
|
|
|
if (n == null) return true;
|
|
|
|
if (n.content != m.content) return true;
|
|
|
|
if (n.title != m.content) return true;
|
|
|
|
if (n.tagIds != m.tagIds) return true;
|
|
|
|
if (n.color != m.color) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|