Skip to content

Commit

Permalink
Merge pull request #5 from synyx/material3
Browse files Browse the repository at this point in the history
Material 3
  • Loading branch information
enoy19 authored May 22, 2023
2 parents 6c74d77 + bf03a81 commit 9541f27
Show file tree
Hide file tree
Showing 31 changed files with 1,023 additions and 305 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
43 changes: 43 additions & 0 deletions lib/cubit/theme_mode_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';

class ThemeModeCubit extends HydratedCubit<ThemeMode> {
ThemeModeCubit() : super(ThemeMode.system);

bool? get isDark => switch (state) {
ThemeMode.dark => true,
ThemeMode.light => false,
ThemeMode.system => null,
};

set dark(bool? dark) => emit(switch (dark) {
true => ThemeMode.dark,
false => ThemeMode.light,
null => ThemeMode.system,
});

@override
ThemeMode fromJson(Map<String, dynamic> json) => switch (json['version']) {
<= 1 => switch (json['themeMode']) {
'dark' => ThemeMode.dark,
'light' => ThemeMode.light,
_ => ThemeMode.system,
},
_ => ThemeMode.system,
};

@override
Map<String, dynamic>? toJson(ThemeMode state) {
final themeMode = switch (state) {
ThemeMode.dark => 'dark',
ThemeMode.light => 'light',
_ => 'system',
};

return {
'version': 1,
'themeMode': themeMode,
};
}
}
25 changes: 25 additions & 0 deletions lib/cubit/time_entries_filter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
library time_entries_filter;

import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/json_object.dart';
import 'package:built_value/serializer.dart';
import 'package:syntrack/model/common/task.dart';

part 'time_entries_filter.g.dart';

abstract class TimeEntriesFilter implements Built<TimeEntriesFilter, TimeEntriesFilterBuilder> {
String? get query;
DateTime? get filterStart;
DateTime? get filterEnd;
Duration? get filterDuration;
bool? get filterBooked;
Set<int?> get filterWeekday;
Task? get filterTask;
Set<String?> get filterWorkInterfaceId;
Set<String?> get filterActivityNames;

TimeEntriesFilter._();

factory TimeEntriesFilter([void Function(TimeEntriesFilterBuilder) updates]) = _$TimeEntriesFilter;
}
114 changes: 114 additions & 0 deletions lib/cubit/time_entries_filter_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syntrack/cubit/time_entries_filter.dart';
import 'package:syntrack/model/common/time_entry.dart';
import 'package:syntrack/util/date_time_extension.dart';

class TimeEntriesFilterCubit extends Cubit<TimeEntriesFilter> {
TimeEntriesFilterCubit()
: super((TimeEntriesFilterBuilder()
..filterActivityNames = {}
..filterWorkInterfaceId = {}
..filterWeekday = {})
.build());

Iterable<TimeEntry> filter(List<TimeEntry> timeEntries) {
final TimeEntriesFilter(
query: query,
filterActivityNames: filterActivityNames,
filterBooked: filterBooked,
filterTask: filterTask,
filterDuration: filterDuration,
filterWeekday: filterWeekday,
filterWorkInterfaceId: filterWorkInterfaceId,
filterStart: filterStart,
filterEnd: filterEnd,
) = state;

if (query == null &&
filterActivityNames.isEmpty &&
filterBooked == null &&
filterTask == null &&
filterDuration == null &&
filterWeekday.isEmpty &&
filterWorkInterfaceId.isEmpty &&
filterStart == null &&
filterEnd == null) {
return timeEntries;
}

return timeEntries
.where(
(element) => query != null
? '${element.comment.trim()}${element.task?.name ?? '<NO TASK>'}${element.activity?.name ?? '<NO ACTIVITY>'}'
.toLowerCase()
.contains(query.trim().toLowerCase())
: true,
)
.where(
(element) => filterActivityNames.isNotEmpty ? filterActivityNames.contains(element.activity?.name) : true,
)
.where(
(element) => filterBooked != null
? filterBooked
? element.bookingId != null
: element.bookingId == null
: true,
)
.where(
(element) => filterTask != null ? element.task?.id == filterTask.id : true,
)
.where(
(element) => filterWeekday.isNotEmpty ? filterWeekday.contains(element.start.weekday) : true,
)
.where(
(element) =>
filterWorkInterfaceId.isNotEmpty ? filterWorkInterfaceId.contains(element.task?.workInterfaceId) : true,
)
.where(
(element) => filterStart != null ? element.start.startOfDay == filterStart.startOfDay : true,
)
.where(
(element) => filterEnd != null ? element.end.startOfDay == filterEnd.startOfDay : true,
)
.where(
(element) => filterDuration != null ? filterDuration.inSeconds <= element.duration.inSeconds : true,
);
}

void debouncedFilters(Function(TimeEntriesFilterBuilder) updates) {
EasyDebounce.debounce('time_entries_filter_cubit.debouncedFilters', const Duration(milliseconds: 500), () {
setFilters(updates);
});
}

void setFilters(Function(TimeEntriesFilterBuilder) updates) {
emit(state.rebuild(updates));
}

void clearFilters() {
emit(state.rebuild((p0) => p0
..filterActivityNames = const {}
..filterBooked = null
..filterTask = null
..filterDuration = null
..filterWeekday = const {}
..filterWorkInterfaceId = const {}
..filterStart = null
..filterEnd = null));
}

void debouncedQuery(String? query) {
EasyDebounce.debounce(
'time_entries_filter_cubit.debouncedQuery',
const Duration(milliseconds: 750),
() {
immediateQuery(query);
},
);
}

void immediateQuery(String? query) {
emit(state.rebuild((state) => state..query = query));
}
}
24 changes: 24 additions & 0 deletions lib/cubit/work_interface_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:convert';

import 'package:built_collection/built_collection.dart';
import 'package:collection/collection.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:syntrack/model/common/task_search_origin.dart';
import 'package:syntrack/model/work/erpnext/erpnext_config.dart';
import 'package:syntrack/model/work/redmine/redmine_config.dart';
import 'package:syntrack/model/work/work_interface_configs.dart';
Expand Down Expand Up @@ -98,4 +100,26 @@ class WorkInterfaceCubit extends HydratedCubit<WorkInterfaceConfigs> {
'data': jsonDecode(state.toJson()),
};
}

TaskSearchOrigin? getOriginFor(String workInterfaceId) {
return state.combinedConfigs
.map((element) => switch (element) {
ErpNextConfig() => element.id == workInterfaceId ? TaskSearchOrigin.erpNext : null,
RedmineConfig() => element.id == workInterfaceId ? TaskSearchOrigin.redmine : null,
_ => null,
})
.firstWhereOrNull((element) => element != null);
}

String getNameFor(dynamic workInterface) => switch (workInterface) {
ErpNextConfig() => workInterface.name,
RedmineConfig() => workInterface.name,
_ => '',
};

String getIdFor(dynamic workInterface) => switch (workInterface) {
ErpNextConfig() => workInterface.id,
RedmineConfig() => workInterface.id,
_ => '',
};
}
20 changes: 17 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sizer/sizer.dart';
import 'package:syntrack/cubit/booking_cubit.dart';
import 'package:syntrack/cubit/theme_mode_cubit.dart';
import 'package:syntrack/cubit/task_search_cubit.dart';
import 'package:syntrack/cubit/time_entries_cubit.dart';
import 'package:syntrack/cubit/time_entries_filter_cubit.dart';
import 'package:syntrack/cubit/time_tracking_cubit.dart';
import 'package:syntrack/cubit/work_interface_cubit.dart';
import 'package:syntrack/repository/data/latest_bookings_data_provider.dart';
Expand Down Expand Up @@ -59,7 +61,6 @@ Future<void> createHydratedBoxBackup(Directory appDir, {int maxBackups = 10}) as
}

class SynTrack extends StatelessWidget {
static const primarySwatch = Colors.blue;
final _appRouter = AppRouter();

SynTrack({super.key});
Expand All @@ -71,9 +72,15 @@ class SynTrack extends StatelessWidget {
create: (context) => WorkRepository(),
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => ThemeModeCubit(),
),
BlocProvider(
create: (context) => TimeEntriesCubit(),
),
BlocProvider(
create: (context) => TimeEntriesFilterCubit(),
),
BlocProvider(
lazy: false,
create: (context) {
Expand Down Expand Up @@ -112,9 +119,16 @@ class SynTrack extends StatelessWidget {
],
title: 'synTrack',
theme: ThemeData(
primarySwatch: primarySwatch,
useMaterial3: false,
brightness: Brightness.light,
useMaterial3: true,
colorSchemeSeed: const Color(0xFFFF5250),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
useMaterial3: true,
colorSchemeSeed: const Color(0xFF1923DC),
),
themeMode: context.watch<ThemeModeCubit>().state,
routerDelegate: _appRouter.delegate(),
routeInformationParser: _appRouter.defaultRouteParser(),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/model/common/task_search_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import 'package:syntrack/model/serializer/serializers.dart';
part 'task_search_result.g.dart';

abstract class TaskSearchResult implements Built<TaskSearchResult, TaskSearchResultBuilder> {
Task get task;
Task? get task;
TaskSearchOrigin get origin;
Activity? get activity;
String? get comment;
Expand Down
18 changes: 10 additions & 8 deletions lib/repository/data/latest_bookings_data_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,24 @@ class LatestBookingsDataProvider extends WorkDataProvider<void> {
Stream<TaskSearchResult> search(config, String query) async* {
final tasks = <Task>{};

query = query.toLowerCase();

yield* Stream.fromIterable(timeEntriesCubit.state).where(
(timeEntry) {
return timeEntry.comment.toLowerCase().contains(query) ||
(timeEntry.task != null && timeEntry.task!.name.toLowerCase().contains(query));
return timeEntry.comment.trim().toLowerCase().contains(query) ||
(timeEntry.task != null && timeEntry.task!.name.trim().toLowerCase().contains(query));
},
).where((event) {
).where((timeEntry) {
// filter out tasks that are already in the list
final contains = !tasks.contains(event.task);
if (event.task != null) {
tasks.add(event.task!);
final contains = !tasks.contains(timeEntry.task);
if (timeEntry.task != null) {
tasks.add(timeEntry.task!);
}
return event.task == null || contains;
return timeEntry.task == null || contains;
}).map(
(e) => TaskSearchResult(
(b) => b
..displayText = '${e.comment}${e.task != null ? ' - ' : ''}${e.task?.name}'
..displayText = e.comment
..origin = TaskSearchOrigin.latestBookings
..activity = e.activity?.toBuilder()
..comment = e.comment
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/erpnext_edit_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class _ErpNextEditPageState extends State<ErpNextEditPage> {
appBar: AppBar(
title: const Text('ERPNext'),
centerTitle: true,
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.onSecondary,
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.save),
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/redmine_edit_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class _RedmineEditPageState extends State<RedmineEditPage> {
appBar: AppBar(
title: const Text('Redmine'),
centerTitle: true,
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.onSecondary,
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.save),
Expand Down
Loading

0 comments on commit 9541f27

Please sign in to comment.