mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: update data & time format
This commit is contained in:
parent
efbac6c92d
commit
c6edd1a6da
@ -27,7 +27,7 @@ class GridCellContextBuilder {
|
|||||||
gridCell: _gridCell,
|
gridCell: _gridCell,
|
||||||
cellCache: _cellCache,
|
cellCache: _cellCache,
|
||||||
cellDataLoader: DateCellDataLoader(gridCell: _gridCell),
|
cellDataLoader: DateCellDataLoader(gridCell: _gridCell),
|
||||||
cellDataPersistence: NumberCellDataPersistence(gridCell: _gridCell),
|
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
||||||
);
|
);
|
||||||
case FieldType.Number:
|
case FieldType.Number:
|
||||||
return GridCellContext(
|
return GridCellContext(
|
||||||
@ -120,7 +120,7 @@ class _GridCellContext<T, D> extends Equatable {
|
|||||||
_onFieldChangedFn = () {
|
_onFieldChangedFn = () {
|
||||||
_loadData();
|
_loadData();
|
||||||
};
|
};
|
||||||
cellCache.addListener(_cacheKey, _onFieldChangedFn!);
|
cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCellChangedFn() {
|
onCellChangedFn() {
|
||||||
@ -172,7 +172,7 @@ class _GridCellContext<T, D> extends Equatable {
|
|||||||
_delayOperation?.cancel();
|
_delayOperation?.cancel();
|
||||||
|
|
||||||
if (_onFieldChangedFn != null) {
|
if (_onFieldChangedFn != null) {
|
||||||
cellCache.removeListener(_cacheKey, _onFieldChangedFn!);
|
cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||||
_onFieldChangedFn = null;
|
_onFieldChangedFn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class GridCellCache {
|
|||||||
final GridCellFieldDelegate fieldDelegate;
|
final GridCellFieldDelegate fieldDelegate;
|
||||||
|
|
||||||
/// fieldId: {objectId: callback}
|
/// fieldId: {objectId: callback}
|
||||||
final Map<String, Map<String, List<VoidCallback>>> _listenerByFieldId = {};
|
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
|
||||||
|
|
||||||
/// fieldId: {cacheKey: cacheData}
|
/// fieldId: {cacheKey: cacheData}
|
||||||
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
|
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
|
||||||
@ -40,7 +40,7 @@ class GridCellCache {
|
|||||||
}) {
|
}) {
|
||||||
fieldDelegate.onFieldChanged((fieldId) {
|
fieldDelegate.onFieldChanged((fieldId) {
|
||||||
_cellDataByFieldId.remove(fieldId);
|
_cellDataByFieldId.remove(fieldId);
|
||||||
final map = _listenerByFieldId[fieldId];
|
final map = _fieldListenerByFieldId[fieldId];
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
for (final callbacks in map.values) {
|
for (final callbacks in map.values) {
|
||||||
for (final callback in callbacks) {
|
for (final callback in callbacks) {
|
||||||
@ -51,24 +51,24 @@ class GridCellCache {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addListener(GridCellCacheKey cacheKey, VoidCallback callback) {
|
void addFieldListener(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
|
||||||
var map = _listenerByFieldId[cacheKey.fieldId];
|
var map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
_listenerByFieldId[cacheKey.fieldId] = {};
|
_fieldListenerByFieldId[cacheKey.fieldId] = {};
|
||||||
map = _listenerByFieldId[cacheKey.fieldId];
|
map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||||
map![cacheKey.objectId] = [callback];
|
map![cacheKey.objectId] = [onFieldChanged];
|
||||||
} else {
|
} else {
|
||||||
var objects = map[cacheKey.objectId];
|
var objects = map[cacheKey.objectId];
|
||||||
if (objects == null) {
|
if (objects == null) {
|
||||||
map[cacheKey.objectId] = [callback];
|
map[cacheKey.objectId] = [onFieldChanged];
|
||||||
} else {
|
} else {
|
||||||
objects.add(callback);
|
objects.add(onFieldChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeListener(GridCellCacheKey cacheKey, VoidCallback fn) {
|
void removeFieldListener(GridCellCacheKey cacheKey, VoidCallback fn) {
|
||||||
var callbacks = _listenerByFieldId[cacheKey.fieldId]?[cacheKey.objectId];
|
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.objectId];
|
||||||
final index = callbacks?.indexWhere((callback) => callback == fn);
|
final index = callbacks?.indexWhere((callback) => callback == fn);
|
||||||
if (index != null && index != -1) {
|
if (index != null && index != -1) {
|
||||||
callbacks?.removeAt(index);
|
callbacks?.removeAt(index);
|
||||||
|
@ -61,12 +61,13 @@ class CellDataLoader extends _GridCellDataLoader<Cell> {
|
|||||||
|
|
||||||
class DateCellDataLoader extends _GridCellDataLoader<DateCellData> {
|
class DateCellDataLoader extends _GridCellDataLoader<DateCellData> {
|
||||||
final GridCell gridCell;
|
final GridCell gridCell;
|
||||||
|
final GridCellDataConfig _config;
|
||||||
DateCellDataLoader({
|
DateCellDataLoader({
|
||||||
required this.gridCell,
|
required this.gridCell,
|
||||||
});
|
}) : _config = DefaultCellDataConfig(reloadOnFieldChanged: true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GridCellDataConfig get config => DefaultCellDataConfig();
|
GridCellDataConfig get config => _config;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<DateCellData?> loadData() {
|
Future<DateCellData?> loadData() {
|
||||||
|
@ -35,9 +35,9 @@ class DateCellPersistenceData with _$DateCellPersistenceData {
|
|||||||
const factory DateCellPersistenceData({required DateTime date, String? time}) = _DateCellPersistenceData;
|
const factory DateCellPersistenceData({required DateTime date, String? time}) = _DateCellPersistenceData;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NumberCellDataPersistence implements _GridCellDataPersistence<DateCellPersistenceData> {
|
class DateCellDataPersistence implements _GridCellDataPersistence<DateCellPersistenceData> {
|
||||||
final GridCell gridCell;
|
final GridCell gridCell;
|
||||||
NumberCellDataPersistence({
|
DateCellDataPersistence({
|
||||||
required this.gridCell,
|
required this.gridCell,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,10 +47,7 @@ class NumberCellDataPersistence implements _GridCellDataPersistence<DateCellPers
|
|||||||
|
|
||||||
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
|
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
|
||||||
payload.date = date;
|
payload.date = date;
|
||||||
|
payload.time = data.time ?? "";
|
||||||
if (data.time != null) {
|
|
||||||
payload.time = data.time!;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GridEventUpdateDateCell(payload).send().then((result) {
|
return GridEventUpdateDateCell(payload).send().then((result) {
|
||||||
return result.fold(
|
return result.fold(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@ -25,8 +25,8 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
|||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
await event.when(
|
await event.when(
|
||||||
initial: () async => _startListening(),
|
initial: () async => _startListening(),
|
||||||
selectDay: (date) {
|
selectDay: (date) async {
|
||||||
_updateDateData(emit, date: date);
|
await _updateDateData(emit, date: date, time: state.time);
|
||||||
},
|
},
|
||||||
setCalFormat: (format) {
|
setCalFormat: (format) {
|
||||||
emit(state.copyWith(format: format));
|
emit(state.copyWith(format: format));
|
||||||
@ -44,22 +44,19 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
|||||||
setTimeFormat: (timeFormat) async {
|
setTimeFormat: (timeFormat) async {
|
||||||
await _updateTypeOption(emit, timeFormat: timeFormat);
|
await _updateTypeOption(emit, timeFormat: timeFormat);
|
||||||
},
|
},
|
||||||
setTime: (time) {
|
setTime: (time) async {
|
||||||
_updateDateData(emit, time: time);
|
await _updateDateData(emit, time: time);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateDateData(Emitter<DateCalState> emit, {DateTime? date, String? time}) {
|
Future<void> _updateDateData(Emitter<DateCalState> emit, {DateTime? date, String? time}) {
|
||||||
state.dateData.fold(
|
final DateCellPersistenceData newDateData = state.dateData.fold(
|
||||||
() {
|
() {
|
||||||
var newDateData = DateCellPersistenceData(date: date ?? DateTime.now());
|
var newDateData = DateCellPersistenceData(date: date ?? DateTime.now());
|
||||||
if (time != null) {
|
return newDateData.copyWith(time: time);
|
||||||
newDateData = newDateData.copyWith(time: time);
|
|
||||||
}
|
|
||||||
emit(state.copyWith(dateData: Some(newDateData)));
|
|
||||||
},
|
},
|
||||||
(dateData) {
|
(dateData) {
|
||||||
var newDateData = dateData;
|
var newDateData = dateData;
|
||||||
@ -70,9 +67,34 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
|||||||
if (newDateData.time != time) {
|
if (newDateData.time != time) {
|
||||||
newDateData = newDateData.copyWith(time: time);
|
newDateData = newDateData.copyWith(time: time);
|
||||||
}
|
}
|
||||||
|
return newDateData;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (newDateData != dateData) {
|
return _saveDateData(emit, newDateData);
|
||||||
emit(state.copyWith(dateData: Some(newDateData)));
|
}
|
||||||
|
|
||||||
|
Future<void> _saveDateData(Emitter<DateCalState> emit, DateCellPersistenceData newDateData) async {
|
||||||
|
if (state.dateData == Some(newDateData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await cellContext.saveCellData(newDateData);
|
||||||
|
result.fold(
|
||||||
|
() => emit(state.copyWith(
|
||||||
|
dateData: Some(newDateData),
|
||||||
|
timeFormatError: none(),
|
||||||
|
)),
|
||||||
|
(err) {
|
||||||
|
switch (ErrorCode.valueOf(err.code)!) {
|
||||||
|
case ErrorCode.InvalidDateTimeFormat:
|
||||||
|
emit(state.copyWith(
|
||||||
|
dateData: Some(newDateData),
|
||||||
|
timeFormatError: Some(err.toString()),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.error(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -152,7 +174,7 @@ class DateCalState with _$DateCalState {
|
|||||||
required CalendarFormat format,
|
required CalendarFormat format,
|
||||||
required DateTime focusedDay,
|
required DateTime focusedDay,
|
||||||
required String time,
|
required String time,
|
||||||
required Option<FlowyError> inputTimeError,
|
required Option<String> timeFormatError,
|
||||||
required Option<DateCellPersistenceData> dateData,
|
required Option<DateCellPersistenceData> dateData,
|
||||||
}) = _DateCalState;
|
}) = _DateCalState;
|
||||||
|
|
||||||
@ -175,7 +197,7 @@ class DateCalState with _$DateCalState {
|
|||||||
focusedDay: DateTime.now(),
|
focusedDay: DateTime.now(),
|
||||||
dateData: dateData,
|
dateData: dateData,
|
||||||
time: time,
|
time: time,
|
||||||
inputTimeError: none(),
|
timeFormatError: none(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
|||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
event.when(
|
event.when(
|
||||||
initial: () => _startListening(),
|
initial: () => _startListening(),
|
||||||
selectDate: (DateCellPersistenceData value) => cellContext.saveCellData(value),
|
|
||||||
didReceiveCellUpdate: (DateCellData value) => emit(state.copyWith(data: Some(value))),
|
didReceiveCellUpdate: (DateCellData value) => emit(state.copyWith(data: Some(value))),
|
||||||
didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)),
|
didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)),
|
||||||
);
|
);
|
||||||
@ -48,7 +47,6 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
|||||||
@freezed
|
@freezed
|
||||||
class DateCellEvent with _$DateCellEvent {
|
class DateCellEvent with _$DateCellEvent {
|
||||||
const factory DateCellEvent.initial() = _InitialCell;
|
const factory DateCellEvent.initial() = _InitialCell;
|
||||||
const factory DateCellEvent.selectDate(DateCellPersistenceData data) = _SelectDay;
|
|
||||||
const factory DateCellEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate;
|
const factory DateCellEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate;
|
||||||
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ class CellCalendar with FlowyOverlayDelegate {
|
|||||||
Future<void> show(
|
Future<void> show(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required GridDateCellContext cellContext,
|
required GridDateCellContext cellContext,
|
||||||
required void Function(DateCellPersistenceData) onSelected,
|
|
||||||
}) async {
|
}) async {
|
||||||
CellCalendar.remove(context);
|
CellCalendar.remove(context);
|
||||||
|
|
||||||
@ -49,7 +48,6 @@ class CellCalendar with FlowyOverlayDelegate {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
final calendar = _CellCalendarWidget(
|
final calendar = _CellCalendarWidget(
|
||||||
onSelected: onSelected,
|
|
||||||
cellContext: cellContext,
|
cellContext: cellContext,
|
||||||
dateTypeOption: typeOptionData,
|
dateTypeOption: typeOptionData,
|
||||||
);
|
);
|
||||||
@ -88,10 +86,8 @@ class CellCalendar with FlowyOverlayDelegate {
|
|||||||
class _CellCalendarWidget extends StatelessWidget {
|
class _CellCalendarWidget extends StatelessWidget {
|
||||||
final GridDateCellContext cellContext;
|
final GridDateCellContext cellContext;
|
||||||
final DateTypeOption dateTypeOption;
|
final DateTypeOption dateTypeOption;
|
||||||
final void Function(DateCellPersistenceData) onSelected;
|
|
||||||
|
|
||||||
const _CellCalendarWidget({
|
const _CellCalendarWidget({
|
||||||
required this.onSelected,
|
|
||||||
required this.cellContext,
|
required this.cellContext,
|
||||||
required this.dateTypeOption,
|
required this.dateTypeOption,
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -108,40 +104,16 @@ class _CellCalendarWidget extends StatelessWidget {
|
|||||||
cellContext: cellContext,
|
cellContext: cellContext,
|
||||||
)..add(const DateCalEvent.initial());
|
)..add(const DateCalEvent.initial());
|
||||||
},
|
},
|
||||||
child: BlocConsumer<DateCalBloc, DateCalState>(
|
child: BlocBuilder<DateCalBloc, DateCalState>(
|
||||||
listener: (context, state) {
|
buildWhen: (p, c) => false,
|
||||||
state.dateData.fold(
|
|
||||||
() => null,
|
|
||||||
(dateData) => onSelected(dateData),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
listenWhen: (p, c) => p.dateData != c.dateData,
|
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
List<Widget> children = [];
|
List<Widget> children = [
|
||||||
|
_buildCalendar(theme, context),
|
||||||
children.addAll([
|
_TimeTextField(bloc: context.read<DateCalBloc>()),
|
||||||
_buildCalendar(state, theme, context),
|
|
||||||
const VSpace(10),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (state.dateTypeOption.includeTime) {
|
|
||||||
children.addAll([
|
|
||||||
_TimeTextField(
|
|
||||||
text: state.time,
|
|
||||||
errorText: state.inputTimeError.fold(() => "", (error) => error.toString()),
|
|
||||||
onEditingComplete: (text) {
|
|
||||||
context.read<DateCalBloc>().add(DateCalEvent.setTime(text));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
children.addAll([
|
|
||||||
Divider(height: 1, color: theme.shader5),
|
Divider(height: 1, color: theme.shader5),
|
||||||
const _IncludeTimeButton(),
|
const _IncludeTimeButton(),
|
||||||
]);
|
const _DateTypeOptionButton()
|
||||||
|
];
|
||||||
children.add(const _DateTypeOptionButton());
|
|
||||||
|
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@ -159,56 +131,60 @@ class _CellCalendarWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TableCalendar<dynamic> _buildCalendar(DateCalState state, AppTheme theme, BuildContext context) {
|
Widget _buildCalendar(AppTheme theme, BuildContext context) {
|
||||||
return TableCalendar(
|
return BlocBuilder<DateCalBloc, DateCalState>(
|
||||||
firstDay: kFirstDay,
|
builder: (context, state) {
|
||||||
lastDay: kLastDay,
|
return TableCalendar(
|
||||||
focusedDay: state.focusedDay,
|
firstDay: kFirstDay,
|
||||||
rowHeight: 40,
|
lastDay: kLastDay,
|
||||||
calendarFormat: state.format,
|
focusedDay: state.focusedDay,
|
||||||
headerStyle: HeaderStyle(
|
rowHeight: 40,
|
||||||
formatButtonVisible: false,
|
calendarFormat: state.format,
|
||||||
titleCentered: true,
|
headerStyle: HeaderStyle(
|
||||||
leftChevronMargin: EdgeInsets.zero,
|
formatButtonVisible: false,
|
||||||
leftChevronPadding: EdgeInsets.zero,
|
titleCentered: true,
|
||||||
leftChevronIcon: svgWidget("home/arrow_left"),
|
leftChevronMargin: EdgeInsets.zero,
|
||||||
rightChevronPadding: EdgeInsets.zero,
|
leftChevronPadding: EdgeInsets.zero,
|
||||||
rightChevronMargin: EdgeInsets.zero,
|
leftChevronIcon: svgWidget("home/arrow_left"),
|
||||||
rightChevronIcon: svgWidget("home/arrow_right"),
|
rightChevronPadding: EdgeInsets.zero,
|
||||||
),
|
rightChevronMargin: EdgeInsets.zero,
|
||||||
calendarStyle: CalendarStyle(
|
rightChevronIcon: svgWidget("home/arrow_right"),
|
||||||
selectedDecoration: BoxDecoration(
|
),
|
||||||
color: theme.main1,
|
calendarStyle: CalendarStyle(
|
||||||
shape: BoxShape.circle,
|
selectedDecoration: BoxDecoration(
|
||||||
),
|
color: theme.main1,
|
||||||
todayDecoration: BoxDecoration(
|
shape: BoxShape.circle,
|
||||||
color: theme.shader4,
|
),
|
||||||
shape: BoxShape.circle,
|
todayDecoration: BoxDecoration(
|
||||||
),
|
color: theme.shader4,
|
||||||
selectedTextStyle: TextStyle(
|
shape: BoxShape.circle,
|
||||||
color: theme.surface,
|
),
|
||||||
fontSize: 14.0,
|
selectedTextStyle: TextStyle(
|
||||||
),
|
color: theme.surface,
|
||||||
todayTextStyle: TextStyle(
|
fontSize: 14.0,
|
||||||
color: theme.surface,
|
),
|
||||||
fontSize: 14.0,
|
todayTextStyle: TextStyle(
|
||||||
),
|
color: theme.surface,
|
||||||
),
|
fontSize: 14.0,
|
||||||
selectedDayPredicate: (day) {
|
),
|
||||||
return state.dateData.fold(
|
),
|
||||||
() => false,
|
selectedDayPredicate: (day) {
|
||||||
(dateData) => isSameDay(dateData.date, day),
|
return state.dateData.fold(
|
||||||
|
() => false,
|
||||||
|
(dateData) => isSameDay(dateData.date, day),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onDaySelected: (selectedDay, focusedDay) {
|
||||||
|
context.read<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay));
|
||||||
|
},
|
||||||
|
onFormatChanged: (format) {
|
||||||
|
context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format));
|
||||||
|
},
|
||||||
|
onPageChanged: (focusedDay) {
|
||||||
|
context.read<DateCalBloc>().add(DateCalEvent.setFocusedDay(focusedDay));
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onDaySelected: (selectedDay, focusedDay) {
|
|
||||||
context.read<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay));
|
|
||||||
},
|
|
||||||
onFormatChanged: (format) {
|
|
||||||
context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format));
|
|
||||||
},
|
|
||||||
onPageChanged: (focusedDay) {
|
|
||||||
context.read<DateCalBloc>().add(DateCalEvent.setFocusedDay(focusedDay));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,14 +222,10 @@ class _IncludeTimeButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _TimeTextField extends StatefulWidget {
|
class _TimeTextField extends StatefulWidget {
|
||||||
final String errorText;
|
final DateCalBloc bloc;
|
||||||
final String text;
|
|
||||||
final void Function(String) onEditingComplete;
|
|
||||||
const _TimeTextField({
|
const _TimeTextField({
|
||||||
|
required this.bloc,
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.text,
|
|
||||||
required this.errorText,
|
|
||||||
required this.onEditingComplete,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -267,33 +239,45 @@ class _TimeTextFieldState extends State<_TimeTextField> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_focusNode = FocusNode();
|
_focusNode = FocusNode();
|
||||||
_controller = TextEditingController(text: widget.text);
|
_controller = TextEditingController(text: widget.bloc.state.time);
|
||||||
_focusNode.addListener(() {
|
if (widget.bloc.state.dateTypeOption.includeTime) {
|
||||||
if (mounted) {
|
_focusNode.addListener(() {
|
||||||
widget.onEditingComplete(_controller.text);
|
if (mounted) {
|
||||||
}
|
widget.bloc.add(DateCalEvent.setTime(_controller.text));
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return Padding(
|
return BlocBuilder<DateCalBloc, DateCalState>(
|
||||||
padding: kMargin,
|
builder: (context, state) {
|
||||||
child: RoundedInputField(
|
if (state.dateTypeOption.includeTime) {
|
||||||
height: 40,
|
return Padding(
|
||||||
focusNode: _focusNode,
|
padding: kMargin,
|
||||||
controller: _controller,
|
child: RoundedInputField(
|
||||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
height: 40,
|
||||||
normalBorderColor: theme.shader4,
|
focusNode: _focusNode,
|
||||||
errorBorderColor: theme.red,
|
controller: _controller,
|
||||||
cursorColor: theme.main1,
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||||
errorText: widget.errorText,
|
normalBorderColor: theme.shader4,
|
||||||
onEditingComplete: (value) {
|
errorBorderColor: theme.red,
|
||||||
widget.onEditingComplete(value);
|
focusBorderColor: theme.main1,
|
||||||
},
|
cursorColor: theme.main1,
|
||||||
),
|
errorText: state.timeFormatError.fold(() => "", (error) => error),
|
||||||
|
onEditingComplete: (value) {
|
||||||
|
widget.bloc.add(DateCalEvent.setTime(value));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,6 @@ class _DateCellState extends State<DateCell> {
|
|||||||
calendar.show(
|
calendar.show(
|
||||||
context,
|
context,
|
||||||
cellContext: bloc.cellContext.clone(),
|
cellContext: bloc.cellContext.clone(),
|
||||||
onSelected: (data) => bloc.add(DateCellEvent.selectDate(data)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ class ErrorCode extends $pb.ProtobufEnum {
|
|||||||
static const ErrorCode FieldNotExists = ErrorCode._(443, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldNotExists');
|
static const ErrorCode FieldNotExists = ErrorCode._(443, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldNotExists');
|
||||||
static const ErrorCode FieldInvalidOperation = ErrorCode._(444, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldInvalidOperation');
|
static const ErrorCode FieldInvalidOperation = ErrorCode._(444, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldInvalidOperation');
|
||||||
static const ErrorCode TypeOptionDataIsEmpty = ErrorCode._(450, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TypeOptionDataIsEmpty');
|
static const ErrorCode TypeOptionDataIsEmpty = ErrorCode._(450, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TypeOptionDataIsEmpty');
|
||||||
static const ErrorCode InvalidData = ErrorCode._(500, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidData');
|
static const ErrorCode InvalidDateTimeFormat = ErrorCode._(500, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidDateTimeFormat');
|
||||||
|
static const ErrorCode InvalidData = ErrorCode._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidData');
|
||||||
|
|
||||||
static const $core.List<ErrorCode> values = <ErrorCode> [
|
static const $core.List<ErrorCode> values = <ErrorCode> [
|
||||||
Internal,
|
Internal,
|
||||||
@ -97,6 +98,7 @@ class ErrorCode extends $pb.ProtobufEnum {
|
|||||||
FieldNotExists,
|
FieldNotExists,
|
||||||
FieldInvalidOperation,
|
FieldInvalidOperation,
|
||||||
TypeOptionDataIsEmpty,
|
TypeOptionDataIsEmpty,
|
||||||
|
InvalidDateTimeFormat,
|
||||||
InvalidData,
|
InvalidData,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -54,9 +54,10 @@ const ErrorCode$json = const {
|
|||||||
const {'1': 'FieldNotExists', '2': 443},
|
const {'1': 'FieldNotExists', '2': 443},
|
||||||
const {'1': 'FieldInvalidOperation', '2': 444},
|
const {'1': 'FieldInvalidOperation', '2': 444},
|
||||||
const {'1': 'TypeOptionDataIsEmpty', '2': 450},
|
const {'1': 'TypeOptionDataIsEmpty', '2': 450},
|
||||||
const {'1': 'InvalidData', '2': 500},
|
const {'1': 'InvalidDateTimeFormat', '2': 500},
|
||||||
|
const {'1': 'InvalidData', '2': 1000},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
/// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||||
final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIRCg1Vc2VySWRJc0VtcHR5EAQSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQZBIWChJXb3Jrc3BhY2VJZEludmFsaWQQZRIYChRBcHBDb2xvclN0eWxlSW52YWxpZBBmEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEGcSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQaBIQCgxBcHBJZEludmFsaWQQbhISCg5BcHBOYW1lSW52YWxpZBBvEhMKD1ZpZXdOYW1lSW52YWxpZBB4EhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEHkSEQoNVmlld0lkSW52YWxpZBB6EhMKD1ZpZXdEZXNjVG9vTG9uZxB7EhMKD1ZpZXdEYXRhSW52YWxpZBB8EhMKD1ZpZXdOYW1lVG9vTG9uZxB9EhEKDENvbm5lY3RFcnJvchDIARIRCgxFbWFpbElzRW1wdHkQrAISFwoSRW1haWxGb3JtYXRJbnZhbGlkEK0CEhcKEkVtYWlsQWxyZWFkeUV4aXN0cxCuAhIUCg9QYXNzd29yZElzRW1wdHkQrwISFAoPUGFzc3dvcmRUb29Mb25nELACEiUKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzELECEhoKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBCyAhIVChBQYXNzd29yZE5vdE1hdGNoELMCEhQKD1VzZXJOYW1lVG9vTG9uZxC0AhInCiJVc2VyTmFtZUNvbnRhaW5Gb3JiaWRkZW5DaGFyYWN0ZXJzELUCEhQKD1VzZXJOYW1lSXNFbXB0eRC2AhISCg1Vc2VySWRJbnZhbGlkELcCEhEKDFVzZXJOb3RFeGlzdBC4AhIQCgtUZXh0VG9vTG9uZxCQAxISCg1HcmlkSWRJc0VtcHR5EJoDEhMKDkJsb2NrSWRJc0VtcHR5EKQDEhEKDFJvd0lkSXNFbXB0eRCuAxIUCg9PcHRpb25JZElzRW1wdHkQrwMSEwoORmllbGRJZElzRW1wdHkQuAMSFgoRRmllbGREb2VzTm90RXhpc3QQuQMSHAoXU2VsZWN0T3B0aW9uTmFtZUlzRW1wdHkQugMSEwoORmllbGROb3RFeGlzdHMQuwMSGgoVRmllbGRJbnZhbGlkT3BlcmF0aW9uELwDEhoKFVR5cGVPcHRpb25EYXRhSXNFbXB0eRDCAxIQCgtJbnZhbGlkRGF0YRD0Aw==');
|
final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIRCg1Vc2VySWRJc0VtcHR5EAQSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQZBIWChJXb3Jrc3BhY2VJZEludmFsaWQQZRIYChRBcHBDb2xvclN0eWxlSW52YWxpZBBmEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEGcSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQaBIQCgxBcHBJZEludmFsaWQQbhISCg5BcHBOYW1lSW52YWxpZBBvEhMKD1ZpZXdOYW1lSW52YWxpZBB4EhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEHkSEQoNVmlld0lkSW52YWxpZBB6EhMKD1ZpZXdEZXNjVG9vTG9uZxB7EhMKD1ZpZXdEYXRhSW52YWxpZBB8EhMKD1ZpZXdOYW1lVG9vTG9uZxB9EhEKDENvbm5lY3RFcnJvchDIARIRCgxFbWFpbElzRW1wdHkQrAISFwoSRW1haWxGb3JtYXRJbnZhbGlkEK0CEhcKEkVtYWlsQWxyZWFkeUV4aXN0cxCuAhIUCg9QYXNzd29yZElzRW1wdHkQrwISFAoPUGFzc3dvcmRUb29Mb25nELACEiUKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzELECEhoKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBCyAhIVChBQYXNzd29yZE5vdE1hdGNoELMCEhQKD1VzZXJOYW1lVG9vTG9uZxC0AhInCiJVc2VyTmFtZUNvbnRhaW5Gb3JiaWRkZW5DaGFyYWN0ZXJzELUCEhQKD1VzZXJOYW1lSXNFbXB0eRC2AhISCg1Vc2VySWRJbnZhbGlkELcCEhEKDFVzZXJOb3RFeGlzdBC4AhIQCgtUZXh0VG9vTG9uZxCQAxISCg1HcmlkSWRJc0VtcHR5EJoDEhMKDkJsb2NrSWRJc0VtcHR5EKQDEhEKDFJvd0lkSXNFbXB0eRCuAxIUCg9PcHRpb25JZElzRW1wdHkQrwMSEwoORmllbGRJZElzRW1wdHkQuAMSFgoRRmllbGREb2VzTm90RXhpc3QQuQMSHAoXU2VsZWN0T3B0aW9uTmFtZUlzRW1wdHkQugMSEwoORmllbGROb3RFeGlzdHMQuwMSGgoVRmllbGRJbnZhbGlkT3BlcmF0aW9uELwDEhoKFVR5cGVPcHRpb25EYXRhSXNFbXB0eRDCAxIaChVJbnZhbGlkRGF0ZVRpbWVGb3JtYXQQ9AMSEAoLSW52YWxpZERhdGEQ6Ac=');
|
||||||
|
@ -5,6 +5,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use chrono::format::strftime::StrftimeItems;
|
use chrono::format::strftime::StrftimeItems;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
use diesel::types::Time;
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
||||||
use flowy_grid_data_model::entities::{
|
use flowy_grid_data_model::entities::{
|
||||||
@ -30,37 +31,28 @@ pub struct DateTypeOption {
|
|||||||
impl_type_option!(DateTypeOption, FieldType::DateTime);
|
impl_type_option!(DateTypeOption, FieldType::DateTime);
|
||||||
|
|
||||||
impl DateTypeOption {
|
impl DateTypeOption {
|
||||||
fn today_desc_from_timestamp(&self, timestamp: i64) -> String {
|
fn today_desc_from_timestamp(&self, timestamp: i64, time: &Option<String>) -> String {
|
||||||
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
|
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
|
||||||
self.today_desc_from_native(native)
|
self.today_desc_from_native(native, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn today_desc_from_str(&self, s: String) -> String {
|
fn today_desc_from_str(&self, s: String, time: &Option<String>) -> String {
|
||||||
match NaiveDateTime::parse_from_str(&s, &self.fmt_str()) {
|
match NaiveDateTime::parse_from_str(&s, &self.date_fmt(time)) {
|
||||||
Ok(native) => self.today_desc_from_native(native),
|
Ok(native) => self.today_desc_from_native(native, time),
|
||||||
Err(_) => "".to_owned(),
|
Err(_) => "".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn today_desc_from_native(&self, native: chrono::NaiveDateTime) -> String {
|
fn today_desc_from_native(&self, native: chrono::NaiveDateTime, time: &Option<String>) -> String {
|
||||||
let utc = self.utc_date_time_from_native(native);
|
let utc = self.utc_date_time_from_native(native);
|
||||||
// let china_timezone = FixedOffset::east(8 * 3600);
|
// let china_timezone = FixedOffset::east(8 * 3600);
|
||||||
// let a = utc.with_timezone(&china_timezone);
|
// let a = utc.with_timezone(&china_timezone);
|
||||||
let output = format!("{}", utc.format_with_items(StrftimeItems::new(&self.fmt_str())));
|
let fmt = self.date_fmt(time);
|
||||||
|
let output = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt)));
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timestamp_from_str(&self, s: &str) -> FlowyResult<i64> {
|
|
||||||
match NaiveDateTime::parse_from_str(s, &self.fmt_str()) {
|
|
||||||
Ok(native) => {
|
|
||||||
let utc = self.utc_date_time_from_native(native);
|
|
||||||
Ok(utc.timestamp())
|
|
||||||
}
|
|
||||||
Err(_) => Err(ErrorCode::InvalidData.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> {
|
fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> {
|
||||||
let native = NaiveDateTime::from_timestamp(timestamp, 0);
|
let native = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||||
self.utc_date_time_from_native(native)
|
self.utc_date_time_from_native(native)
|
||||||
@ -70,9 +62,18 @@ impl DateTypeOption {
|
|||||||
chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
|
chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_str(&self) -> String {
|
fn date_fmt(&self, time: &Option<String>) -> String {
|
||||||
if self.include_time {
|
if self.include_time {
|
||||||
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
|
match time.as_ref() {
|
||||||
|
None => self.date_format.format_str().to_string(),
|
||||||
|
Some(time_str) => {
|
||||||
|
if time_str.is_empty() {
|
||||||
|
self.date_format.format_str().to_string()
|
||||||
|
} else {
|
||||||
|
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.date_format.format_str().to_string()
|
self.date_format.format_str().to_string()
|
||||||
}
|
}
|
||||||
@ -90,29 +91,48 @@ impl DateTypeOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?;
|
let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?;
|
||||||
let time = serde_cell_data.time;
|
let date = self.decode_cell_data_from_timestamp(&serde_cell_data).content;
|
||||||
|
let time = serde_cell_data.time.unwrap_or("".to_owned());
|
||||||
let timestamp = serde_cell_data.timestamp;
|
let timestamp = serde_cell_data.timestamp;
|
||||||
let date = self.decode_cell_data_from_timestamp(serde_cell_data.timestamp).content;
|
|
||||||
|
|
||||||
return Ok(DateCellData { date, time, timestamp });
|
return Ok(DateCellData { date, time, timestamp });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_cell_data_from_timestamp(&self, timestamp: i64) -> DecodedCellData {
|
fn decode_cell_data_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> DecodedCellData {
|
||||||
if timestamp == 0 {
|
if serde_cell_data.timestamp == 0 {
|
||||||
return DecodedCellData::default();
|
return DecodedCellData::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cell_content = self.today_desc_from_timestamp(timestamp);
|
let cell_content = self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time);
|
||||||
return DecodedCellData::new(timestamp.to_string(), cell_content);
|
return DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timestamp_from_utc_with_time(&self, utc: &chrono::DateTime<chrono::Utc>, time: &str) -> FlowyResult<i64> {
|
fn timestamp_from_utc_with_time(
|
||||||
|
&self,
|
||||||
|
utc: &chrono::DateTime<chrono::Utc>,
|
||||||
|
time: &Option<String>,
|
||||||
|
) -> FlowyResult<i64> {
|
||||||
let mut date_str = format!(
|
let mut date_str = format!(
|
||||||
"{}",
|
"{}",
|
||||||
utc.format_with_items(StrftimeItems::new(self.date_format.format_str()))
|
utc.format_with_items(StrftimeItems::new(self.date_format.format_str()))
|
||||||
);
|
);
|
||||||
date_str = date_str.add(&time);
|
|
||||||
self.timestamp_from_str(&date_str)
|
if let Some(time_str) = time.as_ref() {
|
||||||
|
if !time_str.is_empty() {
|
||||||
|
date_str = date_str.add(&time_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fmt = self.date_fmt(time);
|
||||||
|
match NaiveDateTime::parse_from_str(&date_str, &fmt) {
|
||||||
|
Ok(native) => {
|
||||||
|
let utc = self.utc_date_time_from_native(native);
|
||||||
|
Ok(utc.timestamp())
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
let msg = format!("Parse {} with format: {} failed", date_str, fmt);
|
||||||
|
Err(FlowyError::new(ErrorCode::InvalidDateTimeFormat, &msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +147,7 @@ impl CellDataOperation for DateTypeOption {
|
|||||||
return DecodedCellData::default();
|
return DecodedCellData::default();
|
||||||
}
|
}
|
||||||
return match DateCellDataSerde::from_str(&type_option_cell_data.data) {
|
return match DateCellDataSerde::from_str(&type_option_cell_data.data) {
|
||||||
Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(serde_cell_data.timestamp),
|
Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(&serde_cell_data),
|
||||||
Err(_) => DecodedCellData::default(),
|
Err(_) => DecodedCellData::default(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -145,12 +165,12 @@ impl CellDataOperation for DateTypeOption {
|
|||||||
None => DateCellDataSerde::default(),
|
None => DateCellDataSerde::default(),
|
||||||
Some(date_timestamp) => match (self.include_time, content_changeset.time) {
|
Some(date_timestamp) => match (self.include_time, content_changeset.time) {
|
||||||
(true, Some(time)) => {
|
(true, Some(time)) => {
|
||||||
let time = time.to_uppercase();
|
let time = Some(time.trim().to_uppercase());
|
||||||
let utc = self.utc_date_time_from_timestamp(date_timestamp);
|
let utc = self.utc_date_time_from_timestamp(date_timestamp);
|
||||||
let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?;
|
let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?;
|
||||||
DateCellDataSerde { timestamp, time }
|
DateCellDataSerde::new(timestamp, time, &self.time_format)
|
||||||
}
|
}
|
||||||
_ => DateCellDataSerde::from_timestamp(date_timestamp),
|
_ => DateCellDataSerde::from_timestamp(date_timestamp, &self.time_format),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -281,14 +301,21 @@ pub struct DateCellData {
|
|||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
pub struct DateCellDataSerde {
|
pub struct DateCellDataSerde {
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub time: String,
|
pub time: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DateCellDataSerde {
|
impl DateCellDataSerde {
|
||||||
fn from_timestamp(timestamp: i64) -> Self {
|
fn new(timestamp: i64, time: Option<String>, time_format: &TimeFormat) -> Self {
|
||||||
Self {
|
Self {
|
||||||
timestamp,
|
timestamp,
|
||||||
time: "".to_string(),
|
time: Some(time.unwrap_or(default_time_str(time_format))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_timestamp(timestamp: i64, time_format: &TimeFormat) -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp,
|
||||||
|
time: Some(default_time_str(time_format)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +328,13 @@ impl DateCellDataSerde {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_time_str(time_format: &TimeFormat) -> String {
|
||||||
|
match time_format {
|
||||||
|
TimeFormat::TwelveHour => "12:00 AM".to_string(),
|
||||||
|
TimeFormat::TwentyFourHour => "00:00".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, ProtoBuf)]
|
#[derive(Clone, Debug, Default, ProtoBuf)]
|
||||||
pub struct DateChangesetPayload {
|
pub struct DateChangesetPayload {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
@ -439,7 +473,7 @@ mod tests {
|
|||||||
TimeFormat::TwentyFourHour => {
|
TimeFormat::TwentyFourHour => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Mar 14,2022".to_owned(),
|
"Mar 14,2022".to_owned(),
|
||||||
type_option.today_desc_from_timestamp(1647251762)
|
type_option.today_desc_from_timestamp(1647251762, &None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Mar 14,2022".to_owned(),
|
"Mar 14,2022".to_owned(),
|
||||||
@ -449,7 +483,7 @@ mod tests {
|
|||||||
TimeFormat::TwelveHour => {
|
TimeFormat::TwelveHour => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Mar 14,2022".to_owned(),
|
"Mar 14,2022".to_owned(),
|
||||||
type_option.today_desc_from_timestamp(1647251762)
|
type_option.today_desc_from_timestamp(1647251762, &None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Mar 14,2022".to_owned(),
|
"Mar 14,2022".to_owned(),
|
||||||
@ -469,24 +503,47 @@ mod tests {
|
|||||||
type_option.include_time = true;
|
type_option.include_time = true;
|
||||||
match time_format {
|
match time_format {
|
||||||
TimeFormat::TwentyFourHour => {
|
TimeFormat::TwentyFourHour => {
|
||||||
assert_eq!(
|
let changeset = DateCellContentChangeset {
|
||||||
"May 27,2022 00:00".to_owned(),
|
date: Some(1653609600.to_string()),
|
||||||
type_option.today_desc_from_timestamp(1653609600)
|
time: None,
|
||||||
);
|
};
|
||||||
assert_eq!(
|
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||||
"May 27,2022 00:00".to_owned(),
|
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||||
type_option.decode_cell_data(data(1653609600), &field_meta).content
|
assert_eq!("May 27,2022 00:00".to_owned(), content);
|
||||||
);
|
|
||||||
|
let changeset = DateCellContentChangeset {
|
||||||
|
date: Some(1653609600.to_string()),
|
||||||
|
time: Some("23:00".to_owned()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||||
|
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||||
|
assert_eq!("May 27,2022 23:00".to_owned(), content);
|
||||||
}
|
}
|
||||||
TimeFormat::TwelveHour => {
|
TimeFormat::TwelveHour => {
|
||||||
assert_eq!(
|
let changeset = DateCellContentChangeset {
|
||||||
"May 27,2022 12:00 AM".to_owned(),
|
date: Some(1653609600.to_string()),
|
||||||
type_option.today_desc_from_timestamp(1653609600)
|
time: None,
|
||||||
);
|
};
|
||||||
assert_eq!(
|
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||||
"May 27,2022 12:00 AM".to_owned(),
|
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||||
type_option.decode_cell_data(data(1653609600), &field_meta).content
|
assert_eq!("May 27,2022 12:00 AM".to_owned(), content);
|
||||||
);
|
|
||||||
|
let changeset = DateCellContentChangeset {
|
||||||
|
date: Some(1653609600.to_string()),
|
||||||
|
time: Some("".to_owned()),
|
||||||
|
};
|
||||||
|
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||||
|
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||||
|
assert_eq!("May 27,2022".to_owned(), content);
|
||||||
|
|
||||||
|
let changeset = DateCellContentChangeset {
|
||||||
|
date: Some(1653609600.to_string()),
|
||||||
|
time: Some("11:23 pm".to_owned()),
|
||||||
|
};
|
||||||
|
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||||
|
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||||
|
assert_eq!("May 27,2022 11:23 PM".to_owned(), content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,7 +616,7 @@ mod tests {
|
|||||||
fn data(s: i64) -> String {
|
fn data(s: i64) -> String {
|
||||||
let json = serde_json::to_string(&DateCellDataSerde {
|
let json = serde_json::to_string(&DateCellDataSerde {
|
||||||
timestamp: s,
|
timestamp: s,
|
||||||
time: "".to_string(),
|
time: None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
TypeOptionCellData::new(&json, FieldType::DateTime).json()
|
TypeOptionCellData::new(&json, FieldType::DateTime).json()
|
||||||
|
@ -111,8 +111,11 @@ pub enum ErrorCode {
|
|||||||
#[display(fmt = "Field's type option data should not be empty")]
|
#[display(fmt = "Field's type option data should not be empty")]
|
||||||
TypeOptionDataIsEmpty = 450,
|
TypeOptionDataIsEmpty = 450,
|
||||||
|
|
||||||
|
#[display(fmt = "Invalid date time format")]
|
||||||
|
InvalidDateTimeFormat = 500,
|
||||||
|
|
||||||
#[display(fmt = "Invalid data")]
|
#[display(fmt = "Invalid data")]
|
||||||
InvalidData = 500,
|
InvalidData = 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorCode {
|
impl ErrorCode {
|
||||||
|
@ -67,7 +67,8 @@ pub enum ErrorCode {
|
|||||||
FieldNotExists = 443,
|
FieldNotExists = 443,
|
||||||
FieldInvalidOperation = 444,
|
FieldInvalidOperation = 444,
|
||||||
TypeOptionDataIsEmpty = 450,
|
TypeOptionDataIsEmpty = 450,
|
||||||
InvalidData = 500,
|
InvalidDateTimeFormat = 500,
|
||||||
|
InvalidData = 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::protobuf::ProtobufEnum for ErrorCode {
|
impl ::protobuf::ProtobufEnum for ErrorCode {
|
||||||
@ -119,7 +120,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
|
|||||||
443 => ::std::option::Option::Some(ErrorCode::FieldNotExists),
|
443 => ::std::option::Option::Some(ErrorCode::FieldNotExists),
|
||||||
444 => ::std::option::Option::Some(ErrorCode::FieldInvalidOperation),
|
444 => ::std::option::Option::Some(ErrorCode::FieldInvalidOperation),
|
||||||
450 => ::std::option::Option::Some(ErrorCode::TypeOptionDataIsEmpty),
|
450 => ::std::option::Option::Some(ErrorCode::TypeOptionDataIsEmpty),
|
||||||
500 => ::std::option::Option::Some(ErrorCode::InvalidData),
|
500 => ::std::option::Option::Some(ErrorCode::InvalidDateTimeFormat),
|
||||||
|
1000 => ::std::option::Option::Some(ErrorCode::InvalidData),
|
||||||
_ => ::std::option::Option::None
|
_ => ::std::option::Option::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,6 +170,7 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
|
|||||||
ErrorCode::FieldNotExists,
|
ErrorCode::FieldNotExists,
|
||||||
ErrorCode::FieldInvalidOperation,
|
ErrorCode::FieldInvalidOperation,
|
||||||
ErrorCode::TypeOptionDataIsEmpty,
|
ErrorCode::TypeOptionDataIsEmpty,
|
||||||
|
ErrorCode::InvalidDateTimeFormat,
|
||||||
ErrorCode::InvalidData,
|
ErrorCode::InvalidData,
|
||||||
];
|
];
|
||||||
values
|
values
|
||||||
@ -197,7 +200,7 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\ncode.proto*\xe5\x07\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
|
\n\ncode.proto*\x81\x08\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
|
||||||
\n\x10UserUnauthorized\x10\x02\x12\x12\n\x0eRecordNotFound\x10\x03\x12\
|
\n\x10UserUnauthorized\x10\x02\x12\x12\n\x0eRecordNotFound\x10\x03\x12\
|
||||||
\x11\n\rUserIdIsEmpty\x10\x04\x12\x18\n\x14WorkspaceNameInvalid\x10d\x12\
|
\x11\n\rUserIdIsEmpty\x10\x04\x12\x18\n\x14WorkspaceNameInvalid\x10d\x12\
|
||||||
\x16\n\x12WorkspaceIdInvalid\x10e\x12\x18\n\x14AppColorStyleInvalid\x10f\
|
\x16\n\x12WorkspaceIdInvalid\x10e\x12\x18\n\x14AppColorStyleInvalid\x10f\
|
||||||
@ -220,8 +223,9 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||||||
\x12\x13\n\x0eFieldIdIsEmpty\x10\xb8\x03\x12\x16\n\x11FieldDoesNotExist\
|
\x12\x13\n\x0eFieldIdIsEmpty\x10\xb8\x03\x12\x16\n\x11FieldDoesNotExist\
|
||||||
\x10\xb9\x03\x12\x1c\n\x17SelectOptionNameIsEmpty\x10\xba\x03\x12\x13\n\
|
\x10\xb9\x03\x12\x1c\n\x17SelectOptionNameIsEmpty\x10\xba\x03\x12\x13\n\
|
||||||
\x0eFieldNotExists\x10\xbb\x03\x12\x1a\n\x15FieldInvalidOperation\x10\
|
\x0eFieldNotExists\x10\xbb\x03\x12\x1a\n\x15FieldInvalidOperation\x10\
|
||||||
\xbc\x03\x12\x1a\n\x15TypeOptionDataIsEmpty\x10\xc2\x03\x12\x10\n\x0bInv\
|
\xbc\x03\x12\x1a\n\x15TypeOptionDataIsEmpty\x10\xc2\x03\x12\x1a\n\x15Inv\
|
||||||
alidData\x10\xf4\x03b\x06proto3\
|
alidDateTimeFormat\x10\xf4\x03\x12\x10\n\x0bInvalidData\x10\xe8\x07b\x06\
|
||||||
|
proto3\
|
||||||
";
|
";
|
||||||
|
|
||||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||||
|
@ -43,5 +43,6 @@ enum ErrorCode {
|
|||||||
FieldNotExists = 443;
|
FieldNotExists = 443;
|
||||||
FieldInvalidOperation = 444;
|
FieldInvalidOperation = 444;
|
||||||
TypeOptionDataIsEmpty = 450;
|
TypeOptionDataIsEmpty = 450;
|
||||||
InvalidData = 500;
|
InvalidDateTimeFormat = 500;
|
||||||
|
InvalidData = 1000;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user