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

Allow tags#58 #130

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3b7b3c1
Updated task model
sommye-ctr Dec 28, 2024
bc82ba7
Organized task form screen
sommye-ctr Dec 28, 2024
3b12ecf
Added changes to task form screen
sommye-ctr Dec 28, 2024
819d14b
Merge remote-tracking branch 'origin/main' into recurring-task#25
sommye-ctr Dec 28, 2024
6169934
Merge
sommye-ctr Dec 28, 2024
d466035
Merge
sommye-ctr Dec 28, 2024
f427ae5
Added color card in form screen
sommye-ctr Dec 28, 2024
05bd01b
Made task list have rounded corners
sommye-ctr Dec 28, 2024
9c65bc2
Added management of repeated tasks
sommye-ctr Dec 28, 2024
82d71a5
Fixed bugs
sommye-ctr Dec 28, 2024
e9e7ca6
Merge remote-tracking branch 'origin/main' into recurring-task#25
sommye-ctr Dec 29, 2024
d6805c0
Merge remote-tracking branch 'origin/main' into recurring-task#25
sommye-ctr Jan 4, 2025
825dd59
merge
sommye-ctr Jan 4, 2025
e34e32c
Handled repeat interval constraints
sommye-ctr Jan 4, 2025
1e05296
Added reset repeat interval button
sommye-ctr Jan 4, 2025
d63c26e
Made isRecurring field computed
sommye-ctr Jan 4, 2025
d3ef0be
Merge remote-tracking branch 'origin/main' into recurring-task#25
sommye-ctr Jan 4, 2025
12582d9
Merge
sommye-ctr Jan 4, 2025
fe529f6
Added tag model
sommye-ctr Jan 4, 2025
85345ed
Added ui & tag storage
sommye-ctr Jan 4, 2025
ed0f8c6
Changed model config
sommye-ctr Jan 4, 2025
4507233
Added a tag storage method
sommye-ctr Jan 4, 2025
fdf4bae
Added task addition
sommye-ctr Jan 4, 2025
02c456f
Added tag provider & home screen ui change
sommye-ctr Jan 4, 2025
62c6022
Added way to remove all tags
sommye-ctr Jan 4, 2025
78fb6c1
Changed dialog to bottom sheet
sommye-ctr Jan 4, 2025
10fb2e0
Fixed overflow issue on many tags
sommye-ctr Jan 4, 2025
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
14 changes: 7 additions & 7 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/speech_to_text/darwin"

SPEC CHECKSUMS:
audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
speech_to_text: 627d3fd2194770b51abb324ba45c2d39398f24a8
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
speech_to_text: 9dc43a5df3cbc2813f8c7cc9bd0fbf94268ed7ac
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96

PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
14 changes: 14 additions & 0 deletions lib/enums/repeat_interval.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
enum RepeatInterval {
daily(1),
weekly(7),
monthly(30);

const RepeatInterval(this.days);
final int days;
}

extension ToString on RepeatInterval {
String getString() {
return "${name.toString()[0].toUpperCase()}${name.toString().substring(1)}";
}
}
22 changes: 16 additions & 6 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:taskly/providers/tags_provider.dart';
import 'package:taskly/providers/theme_provider.dart';
import 'package:taskly/screens/home_screen.dart';
import 'package:taskly/screens/intro_screen.dart';
import 'package:taskly/screens/splash_screen.dart';
import 'package:taskly/service/local_db_service.dart';
import 'package:taskly/service/speech_service.dart';
import 'package:taskly/storage/tags_storage.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -28,20 +30,27 @@ class TasklyApp extends StatefulWidget {

class _TasklyAppState extends State<TasklyApp> {
late ThemeProvider themeProvider;

TagsProvider tagsProvider = TagsProvider();

@override
void initState() {
super.initState();
themeProvider =
ThemeProvider();
themeProvider = ThemeProvider();
SpeechService.intialize();
_loadTags();
}

void _loadTags() async {
tagsProvider.updateTags(await TagsStorage.loadTags());
}

@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => themeProvider,
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => themeProvider),
ChangeNotifierProvider(create: (_) => tagsProvider),
],
child: Consumer<ThemeProvider>(
builder: (context, value, child) => MaterialApp(
debugShowCheckedModeBanner: false,
Expand All @@ -55,7 +64,8 @@ class _TasklyAppState extends State<TasklyApp> {
// home: widget.isFirstTime ? const IntroScreen() : const HomeScreen(),
initialRoute: '/',
routes: {
'/': (context) => SplashScreen(), // Add Splash Screen as the initial route
'/': (context) =>
SplashScreen(), // Add Splash Screen as the initial route
'/main': (context) =>
widget.isFirstTime ? const IntroScreen() : const HomeScreen(),
},
Expand Down
61 changes: 61 additions & 0 deletions lib/models/tag.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'dart:convert';
import 'dart:math';

import 'package:flutter/material.dart';

class Tag {
String id;
String title;
Color color;

Tag({
Color? color,
String? id,
required this.title,
}) : color = color ??
Colors.primaries[Random().nextInt(Colors.primaries.length)],
id = id ?? DateTime.now().millisecondsSinceEpoch.toString();

Tag copyWith({
String? id,
String? title,
Color? color,
}) {
return Tag(
id: id ?? this.id,
title: title ?? this.title,
color: color ?? this.color,
);
}

Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'color': color.value,
};
}

factory Tag.fromMap(Map<String, dynamic> map) {
return Tag(
id: map['id'] ?? '',
title: map['title'] ?? '',
color: Color(map['color']),
);
}

String toJson() => json.encode(toMap());

factory Tag.fromJson(String source) => Tag.fromMap(json.decode(source));

@override
String toString() => 'Tag(id: $id, title: $title, color: $color)';

@override
bool operator ==(Object other) {
return other is Tag && other.id == id;
}

@override
int get hashCode => id.hashCode ^ title.hashCode ^ color.hashCode;
}
33 changes: 31 additions & 2 deletions lib/models/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,22 @@ class Task {
DateTime deadline;
bool hasDeadline;
Color color;
int? recurringDays;
List<String> tags; // the tag ids

Task({required this.title, this.description = '', this.isCompleted = false, DateTime? deadline, this.hasDeadline = false,this.color = Colors.blue })
: deadline = deadline ?? DateTime.now();
bool get isRecurring => recurringDays != null;

Task({
required this.title,
this.description = '',
this.isCompleted = false,
DateTime? deadline,
this.hasDeadline = false,
this.color = Colors.blue,
this.recurringDays,
List<String>? tags,
}) : deadline = deadline ?? DateTime.now(),
tags = tags ?? [];

// Convert a Task object to JSON
Map<String, dynamic> toJson() {
Expand All @@ -19,7 +32,9 @@ class Task {
'isCompleted': isCompleted,
'deadline': deadline.toIso8601String(),
'hasDeadline': hasDeadline,
'recurringDays': recurringDays,
'color': color.value,
'tags': tags,
};
}

Expand All @@ -31,7 +46,21 @@ class Task {
isCompleted: json['isCompleted'],
deadline: DateTime.parse(json['deadline']),
hasDeadline: json['hasDeadline'],
recurringDays: json['recurringDays'],
color: Color(json['color']),
tags: List.from(json['tags']),
);
}

void toggleCompletion() {
if (!isRecurring) {
isCompleted = !isCompleted;
return;
}

if (hasDeadline) {
deadline = deadline.add(Duration(days: recurringDays!));
return;
}
}
}
11 changes: 11 additions & 0 deletions lib/providers/tags_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
import 'package:taskly/models/tag.dart';

class TagsProvider extends ChangeNotifier {
List<Tag> allTags = [];

void updateTags(List<Tag> tags) {
allTags = tags;
notifyListeners();
}
}
64 changes: 35 additions & 29 deletions lib/screens/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class _HomeScreenState extends State<HomeScreen> {

void _toggleTaskCompletion(int index, bool? value) async {
setState(() {
tasks[index].isCompleted = value ?? false;
tasks[index].toggleCompletion();

if (tasks[index].isCompleted) {
if (tasks[index].hasDeadline) {
Expand Down Expand Up @@ -135,11 +135,9 @@ class _HomeScreenState extends State<HomeScreen> {
} else if (option == TaskOption.launchMeditationScreen) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const MeditationScreen()));
}
else if (option == TaskOption.exportToCSV) {
} else if (option == TaskOption.exportToCSV) {
exportToCSV(tasks);
}

});
}

Expand All @@ -157,39 +155,47 @@ class _HomeScreenState extends State<HomeScreen> {
await TaskStorage.saveTasks(tasks);
}
}
void exportToCSV(List<Task> tasks) async {
// Prepare CSV data
List<List<dynamic>> rows = [];

// Add header
rows.add(["Title", "Description", "Is Completed","Has Deadline","Deadline"]);
void exportToCSV(List<Task> tasks) async {
// Prepare CSV data
List<List<dynamic>> rows = [];

// Add data rows
for (var task in tasks) {
rows.add([task.title, task.description, task.isCompleted,task.hasDeadline,'${task.deadline.day}/${task.deadline.month}/${task.deadline.year}']);
}
// Add header
rows.add(
["Title", "Description", "Is Completed", "Has Deadline", "Deadline"]);

// Convert to CSV string
String csv = const ListToCsvConverter().convert(rows);
// Add data rows
for (var task in tasks) {
rows.add([
task.title,
task.description,
task.isCompleted,
task.hasDeadline,
'${task.deadline.day}/${task.deadline.month}/${task.deadline.year}'
]);
}

// Open directory picker
String? directory = await FilePicker.platform.getDirectoryPath();
// Convert to CSV string
String csv = const ListToCsvConverter().convert(rows);

if (directory == null) {
// User canceled the picker
print("Export canceled.");
return;
}
// Open directory picker
String? directory = await FilePicker.platform.getDirectoryPath();

// Create the file path
final path = "$directory/tasks.csv";
if (directory == null) {
// User canceled the picker
print("Export canceled.");
return;
}

// Write the CSV file
final file = File(path);
await file.writeAsString(csv);
// Create the file path
final path = "$directory/tasks.csv";

print("File saved at: $path");
}
// Write the CSV file
final file = File(path);
await file.writeAsString(csv);

print("File saved at: $path");
}

void _loadKudos() async {
Kudos loadedKudos = await KudosStorage.loadKudos();
Expand Down
Loading