mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: optimize calendar for mobile (#3979)
* feat: calendar mobile ui - Resolves double border on Calendar Cells - Adds Jump to year quick action for Mobile - Reduces Cell height in Calendar - Change out EventList with EventIndicator in Cell * chore: push card details screen * fix: navigation to card details * feat: day events screen update on new event * fix: changes after merging main * fix: missing argument * fix: amend test and remove stack
This commit is contained in:
parent
b3dd5fb8bd
commit
7fb1b4f43f
@ -74,10 +74,10 @@ void main() {
|
|||||||
await tester.scrollToToday();
|
await tester.scrollToToday();
|
||||||
|
|
||||||
// Hover over today's calendar cell
|
// Hover over today's calendar cell
|
||||||
await tester.hoverOnTodayCalendarCell();
|
await tester.hoverOnTodayCalendarCell(
|
||||||
|
// Tap on create new event button
|
||||||
// Tap on create new event button
|
onHover: () async => await tester.tapAddCalendarEventButton(),
|
||||||
await tester.tapAddCalendarEventButton();
|
);
|
||||||
|
|
||||||
// Make sure that the event editor popup is shown
|
// Make sure that the event editor popup is shown
|
||||||
tester.assertEventEditorOpen();
|
tester.assertEventEditorOpen();
|
||||||
@ -90,15 +90,9 @@ void main() {
|
|||||||
// Double click on today's calendar cell to create a new event
|
// Double click on today's calendar cell to create a new event
|
||||||
await tester.doubleClickCalendarCell(DateTime.now());
|
await tester.doubleClickCalendarCell(DateTime.now());
|
||||||
|
|
||||||
// Make sure that the event editor popup is shown
|
|
||||||
tester.assertEventEditorOpen();
|
|
||||||
|
|
||||||
// Make sure that the event is inserted in the cell
|
// Make sure that the event is inserted in the cell
|
||||||
tester.assertNumberOfEventsInCalendar(2);
|
tester.assertNumberOfEventsInCalendar(2);
|
||||||
|
|
||||||
// Dismiss the event editor popup
|
|
||||||
await tester.dismissEventEditor();
|
|
||||||
|
|
||||||
// Click on the event
|
// Click on the event
|
||||||
await tester.openCalendarEvent(index: 0);
|
await tester.openCalendarEvent(index: 0);
|
||||||
tester.assertEventEditorOpen();
|
tester.assertEventEditorOpen();
|
||||||
@ -112,7 +106,7 @@ void main() {
|
|||||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||||
|
|
||||||
// Click on the event
|
// Click on the event
|
||||||
await tester.openCalendarEvent(index: 1);
|
await tester.openCalendarEvent(index: 0);
|
||||||
tester.assertEventEditorOpen();
|
tester.assertEventEditorOpen();
|
||||||
|
|
||||||
// Click on the open icon
|
// Click on the open icon
|
||||||
@ -137,7 +131,7 @@ void main() {
|
|||||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||||
|
|
||||||
// Delete event from row detail page
|
// Delete event from row detail page
|
||||||
await tester.openCalendarEvent(index: 1);
|
await tester.openCalendarEvent(index: 0);
|
||||||
await tester.openEventToRowDetailPage();
|
await tester.openEventToRowDetailPage();
|
||||||
tester.assertRowDetailPageOpened();
|
tester.assertRowDetailPageOpened();
|
||||||
|
|
||||||
@ -163,7 +157,7 @@ void main() {
|
|||||||
await tester.dismissEventEditor();
|
await tester.dismissEventEditor();
|
||||||
|
|
||||||
// Drag and drop the event onto the next week, same day
|
// Drag and drop the event onto the next week, same day
|
||||||
await tester.dragDropRescheduleCalendarEvent(firstOfThisMonth);
|
await tester.dragDropRescheduleCalendarEvent();
|
||||||
|
|
||||||
// Make sure that the event has been rescheduled to the new date
|
// Make sure that the event has been rescheduled to the new date
|
||||||
final sameDayNextWeek = firstOfThisMonth.add(const Duration(days: 7));
|
final sameDayNextWeek = firstOfThisMonth.add(const Duration(days: 7));
|
||||||
|
@ -1249,12 +1249,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
|||||||
await pumpAndSettle(const Duration(milliseconds: 300));
|
await pumpAndSettle(const Duration(milliseconds: 300));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> hoverOnTodayCalendarCell() async {
|
Future<void> hoverOnTodayCalendarCell({
|
||||||
|
Future<void> Function()? onHover,
|
||||||
|
}) async {
|
||||||
final todayCell = find.byWidgetPredicate(
|
final todayCell = find.byWidgetPredicate(
|
||||||
(widget) => widget is CalendarDayCard && widget.isToday,
|
(widget) => widget is CalendarDayCard && widget.isToday,
|
||||||
);
|
);
|
||||||
|
|
||||||
await hoverOnWidget(todayCell);
|
await hoverOnWidget(todayCell, onHover: onHover);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> tapAddCalendarEventButton() async {
|
Future<void> tapAddCalendarEventButton() async {
|
||||||
@ -1362,7 +1364,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
|||||||
await tapButton(button);
|
await tapButton(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dragDropRescheduleCalendarEvent(DateTime startDate) async {
|
Future<void> dragDropRescheduleCalendarEvent() async {
|
||||||
final findEventCard = find.byType(EventCard);
|
final findEventCard = find.byType(EventCard);
|
||||||
await drag(findEventCard.first, const Offset(0, 300));
|
await drag(findEventCard.first, const Offset(0, 300));
|
||||||
await pumpAndSettle();
|
await pumpAndSettle();
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||||
|
import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_event_card.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
class MobileCalendarEventsScreen extends StatefulWidget {
|
||||||
|
static const routeName = "/calendar-events";
|
||||||
|
|
||||||
|
// GoRouter Arguments
|
||||||
|
static const calendarBlocKey = "calendar_bloc";
|
||||||
|
static const calendarDateKey = "date";
|
||||||
|
static const calendarEventsKey = "events";
|
||||||
|
static const calendarRowCacheKey = "row_cache";
|
||||||
|
static const calendarViewIdKey = "view_id";
|
||||||
|
|
||||||
|
const MobileCalendarEventsScreen({
|
||||||
|
super.key,
|
||||||
|
required this.calendarBloc,
|
||||||
|
required this.date,
|
||||||
|
required this.events,
|
||||||
|
required this.rowCache,
|
||||||
|
required this.viewId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final CalendarBloc calendarBloc;
|
||||||
|
final DateTime date;
|
||||||
|
final List<CalendarDayEvent> events;
|
||||||
|
final RowCache rowCache;
|
||||||
|
final String viewId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileCalendarEventsScreen> createState() =>
|
||||||
|
_MobileCalendarEventsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileCalendarEventsScreenState
|
||||||
|
extends State<MobileCalendarEventsScreen> {
|
||||||
|
late final List<CalendarDayEvent> _events = widget.events;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider.value(
|
||||||
|
value: widget.calendarBloc,
|
||||||
|
child: BlocBuilder<CalendarBloc, CalendarState>(
|
||||||
|
buildWhen: (p, c) => p.newEvent != c.newEvent,
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state.newEvent?.event != null) {
|
||||||
|
_events.add(state.newEvent!.event!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
key: const Key('add_event_fab'),
|
||||||
|
elevation: 6,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
onPressed: () => widget.calendarBloc
|
||||||
|
.add(CalendarEvent.createEvent(widget.date)),
|
||||||
|
child: const Text('+'),
|
||||||
|
),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
DateFormat.yMMMMd(context.locale.toLanguageTag())
|
||||||
|
.format(widget.date),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const VSpace(10),
|
||||||
|
...widget.events.map((event) {
|
||||||
|
return ListTile(
|
||||||
|
dense: true,
|
||||||
|
title: EventCard(
|
||||||
|
fieldController: widget.calendarBloc.fieldController,
|
||||||
|
event: event,
|
||||||
|
viewId: widget.viewId,
|
||||||
|
rowCache: widget.rowCache,
|
||||||
|
constraints: const BoxConstraints.expand(),
|
||||||
|
autoEdit: false,
|
||||||
|
isDraggable: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
const VSpace(24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -146,19 +146,19 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
(settings) async {
|
(settings) async {
|
||||||
final dateField = _getCalendarFieldInfo(settings.fieldId);
|
final dateField = _getCalendarFieldInfo(settings.fieldId);
|
||||||
if (dateField != null) {
|
if (dateField != null) {
|
||||||
final newRow = await databaseController.createRow(
|
final newRow = await databaseController
|
||||||
withCells: (builder) {
|
.createRow(
|
||||||
builder.insertDate(dateField, date);
|
withCells: (builder) => builder.insertDate(dateField, date),
|
||||||
},
|
)
|
||||||
).then(
|
.then(
|
||||||
(result) => result.fold(
|
(result) => result.fold(
|
||||||
(newRow) => newRow,
|
(newRow) => newRow,
|
||||||
(err) {
|
(err) {
|
||||||
Log.error(err);
|
Log.error(err);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newRow != null) {
|
if (newRow != null) {
|
||||||
final event = await _loadEvent(newRow.id);
|
final event = await _loadEvent(newRow.id);
|
||||||
@ -207,10 +207,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
final payload = RowIdPB(viewId: viewId, rowId: rowId);
|
final payload = RowIdPB(viewId: viewId, rowId: rowId);
|
||||||
return DatabaseEventGetCalendarEvent(payload).send().then((result) {
|
return DatabaseEventGetCalendarEvent(payload).send().then((result) {
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(eventPB) {
|
(eventPB) => _calendarEventDataFromEventPB(eventPB),
|
||||||
final calendarEvent = _calendarEventDataFromEventPB(eventPB);
|
|
||||||
return calendarEvent;
|
|
||||||
},
|
|
||||||
(r) {
|
(r) {
|
||||||
Log.error(r);
|
Log.error(r);
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_events_screen.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||||
|
import 'package:appflowy/util/platform_extension.dart';
|
||||||
|
import 'package:calendar_view/calendar_view.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
|
|
||||||
@ -8,6 +11,7 @@ import 'package:flowy_infra/theme_extension.dart';
|
|||||||
import 'package:flowy_infra/time/duration.dart';
|
import 'package:flowy_infra/time/duration.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../grid/presentation/layout/sizes.dart';
|
import '../../grid/presentation/layout/sizes.dart';
|
||||||
@ -15,6 +19,18 @@ import '../application/calendar_bloc.dart';
|
|||||||
import 'calendar_event_card.dart';
|
import 'calendar_event_card.dart';
|
||||||
|
|
||||||
class CalendarDayCard extends StatelessWidget {
|
class CalendarDayCard extends StatelessWidget {
|
||||||
|
const CalendarDayCard({
|
||||||
|
super.key,
|
||||||
|
required this.viewId,
|
||||||
|
required this.isToday,
|
||||||
|
required this.isInMonth,
|
||||||
|
required this.date,
|
||||||
|
required this.rowCache,
|
||||||
|
required this.events,
|
||||||
|
required this.onCreateEvent,
|
||||||
|
required this.position,
|
||||||
|
});
|
||||||
|
|
||||||
final String viewId;
|
final String viewId;
|
||||||
final bool isToday;
|
final bool isToday;
|
||||||
final bool isInMonth;
|
final bool isInMonth;
|
||||||
@ -22,24 +38,10 @@ class CalendarDayCard extends StatelessWidget {
|
|||||||
final RowCache rowCache;
|
final RowCache rowCache;
|
||||||
final List<CalendarDayEvent> events;
|
final List<CalendarDayEvent> events;
|
||||||
final void Function(DateTime) onCreateEvent;
|
final void Function(DateTime) onCreateEvent;
|
||||||
|
final CellPosition position;
|
||||||
const CalendarDayCard({
|
|
||||||
required this.viewId,
|
|
||||||
required this.isToday,
|
|
||||||
required this.isInMonth,
|
|
||||||
required this.date,
|
|
||||||
required this.onCreateEvent,
|
|
||||||
required this.rowCache,
|
|
||||||
required this.events,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color backgroundColor = Colors.transparent;
|
|
||||||
if (date.isWeekend) {
|
|
||||||
backgroundColor = AFThemeExtension.of(context).calendarWeekendBGColor;
|
|
||||||
}
|
|
||||||
final hoverBackgroundColor =
|
final hoverBackgroundColor =
|
||||||
Theme.of(context).brightness == Brightness.light
|
Theme.of(context).brightness == Brightness.light
|
||||||
? Theme.of(context).colorScheme.secondaryContainer
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
@ -64,55 +66,67 @@ class CalendarDayCard extends StatelessWidget {
|
|||||||
const VSpace(6.0),
|
const VSpace(6.0),
|
||||||
|
|
||||||
// List of cards or empty space
|
// List of cards or empty space
|
||||||
if (events.isNotEmpty)
|
if (events.isNotEmpty && !PlatformExtension.isMobile) ...[
|
||||||
_EventList(
|
_EventList(
|
||||||
events: events,
|
events: events,
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
rowCache: rowCache,
|
rowCache: rowCache,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
),
|
),
|
||||||
|
] else if (events.isNotEmpty && PlatformExtension.isMobile) ...[
|
||||||
|
const _EventIndicator(),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return Stack(
|
return MouseRegion(
|
||||||
children: <Widget>[
|
onEnter: (p) => notifyEnter(context, true),
|
||||||
GestureDetector(
|
onExit: (p) => notifyEnter(context, false),
|
||||||
onDoubleTap: () => onCreateEvent(date),
|
opaque: false,
|
||||||
child: Container(color: backgroundColor),
|
hitTestBehavior: HitTestBehavior.translucent,
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: () => onCreateEvent(date),
|
||||||
|
onTap: PlatformExtension.isMobile
|
||||||
|
? () => _mobileOnTap(context)
|
||||||
|
: null,
|
||||||
|
behavior: HitTestBehavior.deferToChild,
|
||||||
|
child: Container(
|
||||||
|
color: date.isWeekend
|
||||||
|
? AFThemeExtension.of(context).calendarWeekendBGColor
|
||||||
|
: Colors.transparent,
|
||||||
|
child: DragTarget<CalendarDayEvent>(
|
||||||
|
builder: (context, candidate, __) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: candidate.isEmpty
|
||||||
|
? null
|
||||||
|
: hoverBackgroundColor,
|
||||||
|
border: _borderFromPosition(context, position),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(top: 5.0),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
if (candidate.isEmpty && !PlatformExtension.isMobile)
|
||||||
|
NewEventButton(onCreate: () => onCreateEvent(date)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onAccept: (CalendarDayEvent event) {
|
||||||
|
if (event.date == date) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context
|
||||||
|
.read<CalendarBloc>()
|
||||||
|
.add(CalendarEvent.moveEvent(event, date));
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
DragTarget<CalendarDayEvent>(
|
),
|
||||||
builder: (context, candidate, __) {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
color:
|
|
||||||
candidate.isEmpty ? null : hoverBackgroundColor,
|
|
||||||
padding: const EdgeInsets.only(top: 5.0),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
if (candidate.isEmpty)
|
|
||||||
NewEventButton(onCreate: () => onCreateEvent(date)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onAccept: (CalendarDayEvent event) {
|
|
||||||
if (event.date == date) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context
|
|
||||||
.read<CalendarBloc>()
|
|
||||||
.add(CalendarEvent.moveEvent(event, date));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
MouseRegion(
|
|
||||||
onEnter: (p) => notifyEnter(context, true),
|
|
||||||
onExit: (p) => notifyEnter(context, false),
|
|
||||||
opaque: false,
|
|
||||||
hitTestBehavior: HitTestBehavior.translucent,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -120,42 +134,94 @@ class CalendarDayCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyEnter(BuildContext context, bool isEnter) {
|
void _mobileOnTap(BuildContext context) {
|
||||||
Provider.of<_CardEnterNotifier>(
|
context.push(
|
||||||
context,
|
MobileCalendarEventsScreen.routeName,
|
||||||
listen: false,
|
extra: {
|
||||||
).onEnter = isEnter;
|
MobileCalendarEventsScreen.calendarBlocKey:
|
||||||
|
context.read<CalendarBloc>(),
|
||||||
|
MobileCalendarEventsScreen.calendarDateKey: date,
|
||||||
|
MobileCalendarEventsScreen.calendarEventsKey: events,
|
||||||
|
MobileCalendarEventsScreen.calendarRowCacheKey: rowCache,
|
||||||
|
MobileCalendarEventsScreen.calendarViewIdKey: viewId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyEnter(BuildContext context, bool isEnter) =>
|
||||||
|
Provider.of<_CardEnterNotifier>(context, listen: false).onEnter = isEnter;
|
||||||
|
|
||||||
|
Border _borderFromPosition(BuildContext context, CellPosition position) {
|
||||||
|
final BorderSide borderSide =
|
||||||
|
BorderSide(color: Theme.of(context).dividerColor);
|
||||||
|
|
||||||
|
return Border(
|
||||||
|
top: borderSide,
|
||||||
|
left: borderSide,
|
||||||
|
bottom: [
|
||||||
|
CellPosition.bottom,
|
||||||
|
CellPosition.bottomLeft,
|
||||||
|
CellPosition.bottomRight,
|
||||||
|
].contains(position)
|
||||||
|
? borderSide
|
||||||
|
: BorderSide.none,
|
||||||
|
right: [
|
||||||
|
CellPosition.topRight,
|
||||||
|
CellPosition.bottomRight,
|
||||||
|
CellPosition.right,
|
||||||
|
].contains(position)
|
||||||
|
? borderSide
|
||||||
|
: BorderSide.none,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EventIndicator extends StatelessWidget {
|
||||||
|
const _EventIndicator();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Header extends StatelessWidget {
|
class _Header extends StatelessWidget {
|
||||||
final bool isToday;
|
|
||||||
final bool isInMonth;
|
|
||||||
final DateTime date;
|
|
||||||
const _Header({
|
const _Header({
|
||||||
required this.isToday,
|
required this.isToday,
|
||||||
required this.isInMonth,
|
required this.isInMonth,
|
||||||
required this.date,
|
required this.date,
|
||||||
Key? key,
|
});
|
||||||
}) : super(key: key);
|
|
||||||
|
final bool isToday;
|
||||||
|
final bool isInMonth;
|
||||||
|
final DateTime date;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
child: _DayBadge(
|
child: _DayBadge(isToday: isToday, isInMonth: isInMonth, date: date),
|
||||||
isToday: isToday,
|
|
||||||
isInMonth: isInMonth,
|
|
||||||
date: date,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
class NewEventButton extends StatelessWidget {
|
class NewEventButton extends StatelessWidget {
|
||||||
|
const NewEventButton({super.key, required this.onCreate});
|
||||||
|
|
||||||
final VoidCallback onCreate;
|
final VoidCallback onCreate;
|
||||||
const NewEventButton({required this.onCreate, Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -164,6 +230,7 @@ class NewEventButton extends StatelessWidget {
|
|||||||
if (!notifier.onEnter) {
|
if (!notifier.onEnter) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(4.0),
|
padding: const EdgeInsets.all(4.0),
|
||||||
child: FlowyIconButton(
|
child: FlowyIconButton(
|
||||||
@ -210,15 +277,15 @@ class NewEventButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DayBadge extends StatelessWidget {
|
class _DayBadge extends StatelessWidget {
|
||||||
final bool isToday;
|
|
||||||
final bool isInMonth;
|
|
||||||
final DateTime date;
|
|
||||||
const _DayBadge({
|
const _DayBadge({
|
||||||
required this.isToday,
|
required this.isToday,
|
||||||
required this.isInMonth,
|
required this.isInMonth,
|
||||||
required this.date,
|
required this.date,
|
||||||
Key? key,
|
});
|
||||||
}) : super(key: key);
|
|
||||||
|
final bool isToday;
|
||||||
|
final bool isInMonth;
|
||||||
|
final DateTime date;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -239,10 +306,12 @@ class _DayBadge extends StatelessWidget {
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 18,
|
height: 18,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: PlatformExtension.isMobile
|
||||||
|
? MainAxisAlignment.center
|
||||||
|
: MainAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (date.day == 1)
|
if (date.day == 1 && !PlatformExtension.isMobile)
|
||||||
FlowyText.medium(
|
FlowyText.medium(
|
||||||
monthString,
|
monthString,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
@ -255,7 +324,6 @@ class _DayBadge extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
width: isToday ? 18 : null,
|
width: isToday ? 18 : null,
|
||||||
height: isToday ? 18 : null,
|
height: isToday ? 18 : null,
|
||||||
// padding: GridSize.typeOptionContentInsets,
|
|
||||||
child: Center(
|
child: Center(
|
||||||
child: FlowyText.medium(
|
child: FlowyText.medium(
|
||||||
dayString,
|
dayString,
|
||||||
@ -271,27 +339,25 @@ class _DayBadge extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _EventList extends StatelessWidget {
|
class _EventList extends StatelessWidget {
|
||||||
final List<CalendarDayEvent> events;
|
|
||||||
final String viewId;
|
|
||||||
final RowCache rowCache;
|
|
||||||
final BoxConstraints constraints;
|
|
||||||
|
|
||||||
const _EventList({
|
const _EventList({
|
||||||
required this.events,
|
required this.events,
|
||||||
required this.viewId,
|
required this.viewId,
|
||||||
required this.rowCache,
|
required this.rowCache,
|
||||||
required this.constraints,
|
required this.constraints,
|
||||||
Key? key,
|
});
|
||||||
}) : super(key: key);
|
|
||||||
|
final List<CalendarDayEvent> events;
|
||||||
|
final String viewId;
|
||||||
|
final RowCache rowCache;
|
||||||
|
final BoxConstraints constraints;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final editingEvent = context.watch<CalendarBloc>().state.editingEvent;
|
final editingEvent = context.watch<CalendarBloc>().state.editingEvent;
|
||||||
|
|
||||||
return Flexible(
|
return Flexible(
|
||||||
child: ScrollConfiguration(
|
child: ScrollConfiguration(
|
||||||
behavior: ScrollConfiguration.of(context).copyWith(
|
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: true),
|
||||||
scrollbars: true,
|
|
||||||
),
|
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
final autoEdit =
|
final autoEdit =
|
||||||
@ -307,7 +373,7 @@ class _EventList extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
itemCount: events.length,
|
itemCount: events.length,
|
||||||
padding: const EdgeInsets.fromLTRB(4.0, 0, 4.0, 4.0),
|
padding: const EdgeInsets.fromLTRB(4.0, 0, 4.0, 4.0),
|
||||||
separatorBuilder: (BuildContext context, int index) =>
|
separatorBuilder: (_, __) =>
|
||||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||||
|
import 'package:appflowy/plugins/database_view/application/row/row_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/card/card.dart';
|
import 'package:appflowy/plugins/database_view/widgets/card/card.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/card/card_cell_builder.dart';
|
import 'package:appflowy/plugins/database_view/widgets/card/card_cell_builder.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/card/cells/card_cell.dart';
|
import 'package:appflowy/plugins/database_view/widgets/card/cells/card_cell.dart';
|
||||||
@ -8,6 +10,7 @@ import 'package:appflowy/plugins/database_view/widgets/card/cells/number_card_ce
|
|||||||
import 'package:appflowy/plugins/database_view/widgets/card/cells/url_card_cell.dart';
|
import 'package:appflowy/plugins/database_view/widgets/card/cells/url_card_cell.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart';
|
import 'package:appflowy/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart';
|
||||||
|
import 'package:appflowy/util/platform_extension.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -16,27 +19,30 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../application/calendar_bloc.dart';
|
import '../application/calendar_bloc.dart';
|
||||||
import 'calendar_event_editor.dart';
|
import 'calendar_event_editor.dart';
|
||||||
|
|
||||||
class EventCard extends StatefulWidget {
|
class EventCard extends StatefulWidget {
|
||||||
|
const EventCard({
|
||||||
|
super.key,
|
||||||
|
required this.fieldController,
|
||||||
|
required this.event,
|
||||||
|
required this.viewId,
|
||||||
|
required this.rowCache,
|
||||||
|
required this.constraints,
|
||||||
|
required this.autoEdit,
|
||||||
|
this.isDraggable = true,
|
||||||
|
});
|
||||||
|
|
||||||
final FieldController fieldController;
|
final FieldController fieldController;
|
||||||
final CalendarDayEvent event;
|
final CalendarDayEvent event;
|
||||||
final String viewId;
|
final String viewId;
|
||||||
final RowCache rowCache;
|
final RowCache rowCache;
|
||||||
final BoxConstraints constraints;
|
final BoxConstraints constraints;
|
||||||
final bool autoEdit;
|
final bool autoEdit;
|
||||||
|
final bool isDraggable;
|
||||||
const EventCard({
|
|
||||||
super.key,
|
|
||||||
required this.event,
|
|
||||||
required this.viewId,
|
|
||||||
required this.rowCache,
|
|
||||||
required this.constraints,
|
|
||||||
required this.autoEdit,
|
|
||||||
required this.fieldController,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EventCard> createState() => _EventCardState();
|
State<EventCard> createState() => _EventCardState();
|
||||||
@ -75,7 +81,7 @@ class _EventCardState extends State<EventCard> {
|
|||||||
);
|
);
|
||||||
final renderHook = _calendarEventCardRenderHook(context);
|
final renderHook = _calendarEventCardRenderHook(context);
|
||||||
|
|
||||||
final card = RowCard<CalendarDayEvent>(
|
Widget card = RowCard<CalendarDayEvent>(
|
||||||
// Add the key here to make sure the card is rebuilt when the cells
|
// Add the key here to make sure the card is rebuilt when the cells
|
||||||
// in this row are updated.
|
// in this row are updated.
|
||||||
key: ValueKey(widget.event.eventId),
|
key: ValueKey(widget.event.eventId),
|
||||||
@ -85,7 +91,25 @@ class _EventCardState extends State<EventCard> {
|
|||||||
cardData: widget.event,
|
cardData: widget.event,
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
cellBuilder: cellBuilder,
|
cellBuilder: cellBuilder,
|
||||||
openCard: (_) => _popoverController.show(),
|
openCard: (context) {
|
||||||
|
if (PlatformExtension.isMobile) {
|
||||||
|
final dataController = RowController(
|
||||||
|
rowMeta: rowInfo.rowMeta,
|
||||||
|
viewId: widget.viewId,
|
||||||
|
rowCache: widget.rowCache,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.push(
|
||||||
|
MobileCardDetailScreen.routeName,
|
||||||
|
extra: {
|
||||||
|
MobileCardDetailScreen.argRowController: dataController,
|
||||||
|
MobileCardDetailScreen.argFieldController: widget.fieldController,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_popoverController.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
styleConfiguration: RowCardStyleConfiguration(
|
styleConfiguration: RowCardStyleConfiguration(
|
||||||
showAccessory: false,
|
showAccessory: false,
|
||||||
cellPadding: EdgeInsets.zero,
|
cellPadding: EdgeInsets.zero,
|
||||||
@ -132,50 +156,56 @@ class _EventCardState extends State<EventCard> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return Draggable<CalendarDayEvent>(
|
card = AppFlowyPopover(
|
||||||
data: widget.event,
|
triggerActions: PopoverTriggerFlags.none,
|
||||||
feedback: ConstrainedBox(
|
direction: PopoverDirection.rightWithCenterAligned,
|
||||||
constraints: BoxConstraints(
|
controller: _popoverController,
|
||||||
maxWidth: widget.constraints.maxWidth - 8.0,
|
constraints: const BoxConstraints(maxWidth: 360, maxHeight: 348),
|
||||||
),
|
asBarrier: true,
|
||||||
child: Opacity(
|
margin: EdgeInsets.zero,
|
||||||
opacity: 0.6,
|
offset: const Offset(10.0, 0),
|
||||||
child: DecoratedBox(
|
popupBuilder: (BuildContext popoverContext) {
|
||||||
decoration: decoration,
|
final settings = context.watch<CalendarBloc>().state.settings.fold(
|
||||||
child: card,
|
() => null,
|
||||||
),
|
(layoutSettings) => layoutSettings,
|
||||||
),
|
);
|
||||||
),
|
if (settings == null) {
|
||||||
child: AppFlowyPopover(
|
return const SizedBox.shrink();
|
||||||
triggerActions: PopoverTriggerFlags.none,
|
}
|
||||||
direction: PopoverDirection.rightWithCenterAligned,
|
return CalendarEventEditor(
|
||||||
controller: _popoverController,
|
fieldController: widget.fieldController,
|
||||||
constraints: const BoxConstraints(maxWidth: 360, maxHeight: 348),
|
rowCache: widget.rowCache,
|
||||||
asBarrier: true,
|
rowMeta: widget.event.event.rowMeta,
|
||||||
margin: EdgeInsets.zero,
|
viewId: widget.viewId,
|
||||||
offset: const Offset(10.0, 0),
|
layoutSettings: settings,
|
||||||
popupBuilder: (BuildContext popoverContext) {
|
);
|
||||||
final settings = context.watch<CalendarBloc>().state.settings.fold(
|
},
|
||||||
() => null,
|
child: DecoratedBox(
|
||||||
(layoutSettings) => layoutSettings,
|
decoration: decoration,
|
||||||
);
|
child: card,
|
||||||
if (settings == null) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return CalendarEventEditor(
|
|
||||||
rowCache: widget.rowCache,
|
|
||||||
rowMeta: widget.event.event.rowMeta,
|
|
||||||
viewId: widget.viewId,
|
|
||||||
layoutSettings: settings,
|
|
||||||
fieldController: widget.fieldController,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: decoration,
|
|
||||||
child: card,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (widget.isDraggable) {
|
||||||
|
return Draggable<CalendarDayEvent>(
|
||||||
|
data: widget.event,
|
||||||
|
feedback: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: widget.constraints.maxWidth - 8.0,
|
||||||
|
),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: 0.6,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: decoration,
|
||||||
|
child: card,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: card,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
RowCardRenderHook<CalendarDayEvent> _calendarEventCardRenderHook(
|
RowCardRenderHook<CalendarDayEvent> _calendarEventCardRenderHook(
|
||||||
|
@ -65,14 +65,15 @@ class CalendarEventEditor extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EventEditorControls extends StatelessWidget {
|
class EventEditorControls extends StatelessWidget {
|
||||||
final RowController rowController;
|
|
||||||
final FieldController fieldController;
|
|
||||||
const EventEditorControls({
|
const EventEditorControls({
|
||||||
super.key,
|
super.key,
|
||||||
required this.rowController,
|
required this.rowController,
|
||||||
required this.fieldController,
|
required this.fieldController,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final RowController rowController;
|
||||||
|
final FieldController fieldController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
|
import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/calendar/application/unschedule_event_bloc.dart';
|
import 'package:appflowy/plugins/database_view/calendar/application/unschedule_event_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||||
import 'package:appflowy/plugins/database_view/tab_bar/tab_bar_view.dart';
|
import 'package:appflowy/plugins/database_view/tab_bar/tab_bar_view.dart';
|
||||||
|
import 'package:appflowy/util/platform_extension.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
@ -18,6 +20,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../../application/row/row_cache.dart';
|
import '../../application/row/row_cache.dart';
|
||||||
import '../../application/row/row_controller.dart';
|
import '../../application/row/row_controller.dart';
|
||||||
@ -65,24 +68,25 @@ class CalendarPageTabBarBuilderImpl implements DatabaseTabBarItemBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CalendarPage extends StatefulWidget {
|
class CalendarPage extends StatefulWidget {
|
||||||
final ViewPB view;
|
|
||||||
final DatabaseController databaseController;
|
|
||||||
final bool shrinkWrap;
|
|
||||||
const CalendarPage({
|
const CalendarPage({
|
||||||
|
super.key,
|
||||||
required this.view,
|
required this.view,
|
||||||
required this.databaseController,
|
required this.databaseController,
|
||||||
this.shrinkWrap = false,
|
this.shrinkWrap = false,
|
||||||
super.key,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final DatabaseController databaseController;
|
||||||
|
final bool shrinkWrap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CalendarPage> createState() => _CalendarPageState();
|
State<CalendarPage> createState() => _CalendarPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CalendarPageState extends State<CalendarPage> {
|
class _CalendarPageState extends State<CalendarPage> {
|
||||||
final _eventController = EventController<CalendarDayEvent>();
|
final _eventController = EventController<CalendarDayEvent>();
|
||||||
|
late final CalendarBloc _calendarBloc;
|
||||||
GlobalKey<MonthViewState>? _calendarState;
|
GlobalKey<MonthViewState>? _calendarState;
|
||||||
late CalendarBloc _calendarBloc;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -181,18 +185,21 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
int firstDayOfWeek,
|
int firstDayOfWeek,
|
||||||
) {
|
) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: CalendarSize.contentInsets,
|
padding: PlatformExtension.isMobile
|
||||||
|
? CalendarSize.contentInsetsMobile
|
||||||
|
: CalendarSize.contentInsets,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
// must specify MonthView width for useAvailableVerticalSpace to work properly
|
// must specify MonthView width for useAvailableVerticalSpace to work properly
|
||||||
builder: (context, constraints) => ScrollConfiguration(
|
builder: (context, constraints) => ScrollConfiguration(
|
||||||
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||||
child: MonthView(
|
child: MonthView(
|
||||||
key: _calendarState,
|
key: _calendarState,
|
||||||
|
// TODO(Xazin): Border Color on Mobile
|
||||||
controller: _eventController,
|
controller: _eventController,
|
||||||
width: constraints.maxWidth,
|
width: constraints.maxWidth,
|
||||||
cellAspectRatio: 0.6,
|
cellAspectRatio: PlatformExtension.isMobile ? 1 : 0.6,
|
||||||
startDay: _weekdayFromInt(firstDayOfWeek),
|
startDay: _weekdayFromInt(firstDayOfWeek),
|
||||||
borderColor: Theme.of(context).dividerColor,
|
showBorder: false,
|
||||||
headerBuilder: _headerNavigatorBuilder,
|
headerBuilder: _headerNavigatorBuilder,
|
||||||
weekDayBuilder: _headerWeekDayBuilder,
|
weekDayBuilder: _headerWeekDayBuilder,
|
||||||
cellBuilder: _calendarDayBuilder,
|
cellBuilder: _calendarDayBuilder,
|
||||||
@ -209,9 +216,39 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
FlowyText.medium(
|
GestureDetector(
|
||||||
DateFormat('MMMM y', context.locale.toLanguageTag())
|
onTap: PlatformExtension.isMobile
|
||||||
.format(currentMonth),
|
? () => showFlowyMobileBottomSheet(
|
||||||
|
context,
|
||||||
|
title: LocaleKeys.calendar_quickJumpYear.tr(),
|
||||||
|
builder: (_) => SizedBox(
|
||||||
|
height: 200,
|
||||||
|
child: YearPicker(
|
||||||
|
firstDate: CalendarConstants.epochDate.withoutTime,
|
||||||
|
lastDate: CalendarConstants.maxDate.withoutTime,
|
||||||
|
selectedDate: currentMonth,
|
||||||
|
initialDate: currentMonth,
|
||||||
|
currentDate: DateTime.now(),
|
||||||
|
onChanged: (newDate) {
|
||||||
|
_calendarState?.currentState?.jumpToMonth(newDate);
|
||||||
|
context.pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
FlowyText.medium(
|
||||||
|
DateFormat('MMMM y', context.locale.toLanguageTag())
|
||||||
|
.format(currentMonth),
|
||||||
|
),
|
||||||
|
if (PlatformExtension.isMobile) ...[
|
||||||
|
const HSpace(6),
|
||||||
|
const FlowySvg(FlowySvgs.arrow_down_s),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
FlowyIconButton(
|
FlowyIconButton(
|
||||||
@ -253,7 +290,12 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
Widget _headerWeekDayBuilder(day) {
|
Widget _headerWeekDayBuilder(day) {
|
||||||
// incoming day starts from Monday, the symbols start from Sunday
|
// incoming day starts from Monday, the symbols start from Sunday
|
||||||
final symbols = DateFormat.EEEE(context.locale.toLanguageTag()).dateSymbols;
|
final symbols = DateFormat.EEEE(context.locale.toLanguageTag()).dateSymbols;
|
||||||
final weekDayString = symbols.WEEKDAYS[(day + 1) % 7];
|
String weekDayString = symbols.WEEKDAYS[(day + 1) % 7];
|
||||||
|
|
||||||
|
if (PlatformExtension.isMobile) {
|
||||||
|
weekDayString = weekDayString.substring(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: CalendarSize.daysOfWeekInsets,
|
padding: CalendarSize.daysOfWeekInsets,
|
||||||
@ -271,14 +313,14 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
List<CalendarEventData<CalendarDayEvent>> calenderEvents,
|
List<CalendarEventData<CalendarDayEvent>> calenderEvents,
|
||||||
isToday,
|
isToday,
|
||||||
isInMonth,
|
isInMonth,
|
||||||
|
position,
|
||||||
) {
|
) {
|
||||||
final events = calenderEvents.map((value) => value.event!).toList();
|
|
||||||
// Sort the events by timestamp. Because the database view is not
|
// Sort the events by timestamp. Because the database view is not
|
||||||
// reserving the order of the events. Reserving the order of the rows/events
|
// reserving the order of the events. Reserving the order of the rows/events
|
||||||
// is implemnted in the develop branch(WIP). Will be replaced with that.
|
// is implemnted in the develop branch(WIP). Will be replaced with that.
|
||||||
events.sort(
|
final events = calenderEvents.map((value) => value.event!).toList()
|
||||||
(a, b) => a.event.timestamp.compareTo(b.event.timestamp),
|
..sort((a, b) => a.event.timestamp.compareTo(b.event.timestamp));
|
||||||
);
|
|
||||||
return CalendarDayCard(
|
return CalendarDayCard(
|
||||||
viewId: widget.view.id,
|
viewId: widget.view.id,
|
||||||
isToday: isToday,
|
isToday: isToday,
|
||||||
@ -286,11 +328,9 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
events: events,
|
events: events,
|
||||||
date: date,
|
date: date,
|
||||||
rowCache: _calendarBloc.rowCache,
|
rowCache: _calendarBloc.rowCache,
|
||||||
onCreateEvent: (date) {
|
onCreateEvent: (date) =>
|
||||||
_calendarBloc.add(
|
_calendarBloc.add(CalendarEvent.createEvent(date)),
|
||||||
CalendarEvent.createEvent(date),
|
position: position,
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,13 @@ class CalendarSize {
|
|||||||
CalendarSize.headerContainerPadding,
|
CalendarSize.headerContainerPadding,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static EdgeInsets get contentInsetsMobile => EdgeInsets.fromLTRB(
|
||||||
|
GridSize.leadingHeaderPadding / 2,
|
||||||
|
CalendarSize.headerContainerPadding / 2,
|
||||||
|
GridSize.leadingHeaderPadding / 2,
|
||||||
|
CalendarSize.headerContainerPadding / 2,
|
||||||
|
);
|
||||||
|
|
||||||
static double get scrollBarSize => 8 * scale;
|
static double get scrollBarSize => 8 * scale;
|
||||||
static double get navigatorButtonWidth => 20 * scale;
|
static double get navigatorButtonWidth => 20 * scale;
|
||||||
static double get navigatorButtonHeight => 24 * scale;
|
static double get navigatorButtonHeight => 24 * scale;
|
||||||
|
@ -7,14 +7,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class MobileGridSettingButton extends StatelessWidget {
|
class MobileGridSettingButton extends StatelessWidget {
|
||||||
final DatabaseController controller;
|
|
||||||
final ToggleExtensionNotifier toggleExtension;
|
|
||||||
const MobileGridSettingButton({
|
const MobileGridSettingButton({
|
||||||
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.toggleExtension,
|
required this.toggleExtension,
|
||||||
super.key,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final DatabaseController controller;
|
||||||
|
final ToggleExtensionNotifier toggleExtension;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
@ -49,10 +50,18 @@ class MobileGridSettingButton extends StatelessWidget {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return IconButton(
|
|
||||||
onPressed: () {},
|
return SizedBox(
|
||||||
icon: const FlowySvg(
|
height: 24,
|
||||||
FlowySvgs.m_setting_m,
|
width: 24,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
// TODO(Xazin): Database Settings
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const FlowySvg(
|
||||||
|
FlowySvgs.m_setting_m,
|
||||||
|
size: Size.square(24),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -24,7 +24,7 @@ class MobileTabBarHeader extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -4,11 +4,12 @@ import 'package:appflowy/plugins/database_view/tab_bar/mobile/mobile_tab_bar_hea
|
|||||||
import 'package:appflowy/plugins/database_view/widgets/share_button.dart';
|
import 'package:appflowy/plugins/database_view/widgets/share_button.dart';
|
||||||
import 'package:appflowy/plugins/util.dart';
|
import 'package:appflowy/plugins/util.dart';
|
||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
|
import 'package:appflowy/util/platform_extension.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart';
|
import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -85,6 +86,7 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
|
|||||||
],
|
],
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
if (PlatformExtension.isMobile) const VSpace(12),
|
||||||
BlocBuilder<DatabaseTabBarBloc, DatabaseTabBarState>(
|
BlocBuilder<DatabaseTabBarBloc, DatabaseTabBarState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return ValueListenableBuilder<bool>(
|
return ValueListenableBuilder<bool>(
|
||||||
@ -96,11 +98,14 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
|
|||||||
if (value) {
|
if (value) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
padding: EdgeInsets.symmetric(
|
||||||
child: PlatformExtension.isDesktop
|
horizontal: PlatformExtension.isMobile ? 20 : 40,
|
||||||
? const TabBarHeader()
|
),
|
||||||
: const MobileTabBarHeader(),
|
child: PlatformExtension.isMobile
|
||||||
|
? const MobileTabBarHeader()
|
||||||
|
: const TabBarHeader(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -95,11 +95,11 @@ class _DatabaseSettingListPopoverState
|
|||||||
|
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
controller: ScrollController(),
|
controller: ScrollController(),
|
||||||
itemCount: cells.length,
|
itemCount: cells.length,
|
||||||
separatorBuilder: (context, index) {
|
separatorBuilder: (context, index) =>
|
||||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||||
},
|
|
||||||
physics: StyledScrollPhysics(),
|
physics: StyledScrollPhysics(),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return cells[index];
|
return cells[index];
|
||||||
|
@ -3,6 +3,7 @@ import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_cr
|
|||||||
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/card_property_edit_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/card/card_property_edit/card_property_edit_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/card/row/cells/cells.dart';
|
import 'package:appflowy/mobile/presentation/database/card/row/cells/cells.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_board_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_board_screen.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_events_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/favorite/mobile_favorite_page.dart';
|
import 'package:appflowy/mobile/presentation/favorite/mobile_favorite_page.dart';
|
||||||
@ -77,6 +78,10 @@ GoRouter generateRouter(Widget child) {
|
|||||||
_mobileCodeLanguagePickerPageRoute(),
|
_mobileCodeLanguagePickerPageRoute(),
|
||||||
_mobileLanguagePickerPageRoute(),
|
_mobileLanguagePickerPageRoute(),
|
||||||
_mobileFontPickerPageRoute(),
|
_mobileFontPickerPageRoute(),
|
||||||
|
|
||||||
|
// calendar related
|
||||||
|
_mobileCalendarEventsPageRoute(),
|
||||||
|
|
||||||
_mobileBlockSettingsPageRoute(),
|
_mobileBlockSettingsPageRoute(),
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -331,6 +336,25 @@ GoRoute _mobileFontPickerPageRoute() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GoRoute _mobileCalendarEventsPageRoute() {
|
||||||
|
return GoRoute(
|
||||||
|
path: MobileCalendarEventsScreen.routeName,
|
||||||
|
pageBuilder: (context, state) {
|
||||||
|
final args = state.extra as Map<String, dynamic>;
|
||||||
|
|
||||||
|
return MaterialPage(
|
||||||
|
child: MobileCalendarEventsScreen(
|
||||||
|
calendarBloc: args[MobileCalendarEventsScreen.calendarBlocKey],
|
||||||
|
date: args[MobileCalendarEventsScreen.calendarDateKey],
|
||||||
|
events: args[MobileCalendarEventsScreen.calendarEventsKey],
|
||||||
|
rowCache: args[MobileCalendarEventsScreen.calendarRowCacheKey],
|
||||||
|
viewId: args[MobileCalendarEventsScreen.calendarViewIdKey],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
GoRoute _desktopHomeScreenRoute() {
|
GoRoute _desktopHomeScreenRoute() {
|
||||||
return GoRoute(
|
return GoRoute(
|
||||||
path: DesktopHomeScreen.routeName,
|
path: DesktopHomeScreen.routeName,
|
||||||
|
@ -8,6 +8,7 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
|
|||||||
import 'package:appflowy/user/application/historical_user_bloc.dart';
|
import 'package:appflowy/user/application/historical_user_bloc.dart';
|
||||||
import 'package:appflowy/user/presentation/router.dart';
|
import 'package:appflowy/user/presentation/router.dart';
|
||||||
import 'package:appflowy/user/presentation/widgets/widgets.dart';
|
import 'package:appflowy/user/presentation/widgets/widgets.dart';
|
||||||
|
import 'package:appflowy/util/platform_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
@ -53,7 +54,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
FlowyLogoTitle(
|
FlowyLogoTitle(
|
||||||
title: LocaleKeys.welcomeText.tr(),
|
title: LocaleKeys.welcomeText.tr(),
|
||||||
logoSize: const Size.square(40),
|
logoSize: Size.square(PlatformExtension.isMobile ? 80 : 40),
|
||||||
),
|
),
|
||||||
const VSpace(32),
|
const VSpace(32),
|
||||||
GoButton(
|
GoButton(
|
||||||
@ -113,14 +114,14 @@ class SkipLoginPageFooter extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// The placeholderWidth should be greater than the longest width of the LanguageSelectorOnWelcomePage
|
// The placeholderWidth should be greater than the longest width of the LanguageSelectorOnWelcomePage
|
||||||
const double placeholderWidth = 180;
|
const double placeholderWidth = 180;
|
||||||
return const Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
HSpace(placeholderWidth),
|
if (!PlatformExtension.isMobile) const HSpace(placeholderWidth),
|
||||||
Expanded(child: SubscribeButtons()),
|
const Expanded(child: SubscribeButtons()),
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
width: placeholderWidth,
|
width: placeholderWidth,
|
||||||
height: 28,
|
height: 28,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -5,14 +5,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
class FlowyLogoTitle extends StatelessWidget {
|
class FlowyLogoTitle extends StatelessWidget {
|
||||||
final String title;
|
|
||||||
final Size logoSize;
|
|
||||||
const FlowyLogoTitle({
|
const FlowyLogoTitle({
|
||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.logoSize = const Size.square(40),
|
this.logoSize = const Size.square(40),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final Size logoSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
|
@ -205,11 +205,12 @@ packages:
|
|||||||
calendar_view:
|
calendar_view:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: calendar_view
|
path: "."
|
||||||
sha256: "58a8b851ac0a2d62770fd06ad30f06683bd40848a5dd1a1eca332f5a6064bd82"
|
ref: "6fe0c98"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "6fe0c989289b077569858d5472f3f7ec05b7746f"
|
||||||
source: hosted
|
url: "https://github.com/Xazin/flutter_calendar_view"
|
||||||
version: "1.0.3"
|
source: git
|
||||||
|
version: "1.0.5"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -47,7 +47,7 @@ dependencies:
|
|||||||
appflowy_editor:
|
appflowy_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||||
ref: '31acaff'
|
ref: "31acaff"
|
||||||
|
|
||||||
appflowy_popover:
|
appflowy_popover:
|
||||||
path: packages/appflowy_popover
|
path: packages/appflowy_popover
|
||||||
@ -94,7 +94,10 @@ dependencies:
|
|||||||
shared_preferences: ^2.1.1
|
shared_preferences: ^2.1.1
|
||||||
google_fonts: ^4.0.5
|
google_fonts: ^4.0.5
|
||||||
percent_indicator: ^4.2.3
|
percent_indicator: ^4.2.3
|
||||||
calendar_view: ^1.0.3
|
calendar_view:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Xazin/flutter_calendar_view
|
||||||
|
ref: "6fe0c98"
|
||||||
window_manager: ^0.3.4
|
window_manager: ^0.3.4
|
||||||
http: ^1.0.0
|
http: ^1.0.0
|
||||||
path: ^1.8.3
|
path: ^1.8.3
|
||||||
|
@ -854,7 +854,8 @@
|
|||||||
"clickToAdd": "Click to add to the calendar",
|
"clickToAdd": "Click to add to the calendar",
|
||||||
"name": "Calendar layout"
|
"name": "Calendar layout"
|
||||||
},
|
},
|
||||||
"referencedCalendarPrefix": "View of"
|
"referencedCalendarPrefix": "View of",
|
||||||
|
"quickJumpYear": "Jump to"
|
||||||
},
|
},
|
||||||
"errorDialog": {
|
"errorDialog": {
|
||||||
"title": "AppFlowy Error",
|
"title": "AppFlowy Error",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user