chore: config date cell ui

This commit is contained in:
appflowy 2022-04-09 15:57:12 +08:00
parent 0ba0bb62ce
commit 8a94644add
18 changed files with 243 additions and 77 deletions

View File

@ -200,7 +200,6 @@ void _resolveGridDeps(GetIt getIt) {
getIt.registerFactoryParam<DateCellBloc, CellData, void>(
(cellData, _) => DateCellBloc(
service: CellService(),
cellData: cellData,
),
);

View File

@ -26,15 +26,10 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
_startListening();
},
select: (_Selected value) async {
_service.updateCell(
gridId: state.cellData.gridId,
fieldId: state.cellData.field.id,
rowId: state.cellData.rowId,
data: !state.isSelected ? "Yes" : "No",
);
_updateCellData();
},
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
emit(state.copyWith(isSelected: isSelected(value.cell)));
emit(state.copyWith(isSelected: _isSelected(value.cell)));
},
);
},
@ -50,22 +45,33 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
void _startListening() {
_listener.updateCellNotifier.addPublishListener((result) {
result.fold(
(notificationData) async {
final result = await _service.getCell(
gridId: state.cellData.gridId,
fieldId: state.cellData.field.id,
rowId: state.cellData.rowId,
);
result.fold(
(cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)),
(err) => Log.error(err),
);
},
(notificationData) async => await _loadCellData(),
(err) => Log.error(err),
);
});
_listener.start();
}
Future<void> _loadCellData() async {
final result = await _service.getCell(
gridId: state.cellData.gridId,
fieldId: state.cellData.field.id,
rowId: state.cellData.rowId,
);
result.fold(
(cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)),
(err) => Log.error(err),
);
}
void _updateCellData() {
_service.updateCell(
gridId: state.cellData.gridId,
fieldId: state.cellData.field.id,
rowId: state.cellData.rowId,
data: !state.isSelected ? "Yes" : "No",
);
}
}
@freezed
@ -83,11 +89,11 @@ class CheckboxCellState with _$CheckboxCellState {
}) = _CheckboxCellState;
factory CheckboxCellState.initial(CellData cellData) {
return CheckboxCellState(cellData: cellData, isSelected: isSelected(cellData.cell));
return CheckboxCellState(cellData: cellData, isSelected: _isSelected(cellData.cell));
}
}
bool isSelected(Cell? cell) {
bool _isSelected(Cell? cell) {
final content = cell?.content ?? "";
return content == "Yes";
}

View File

@ -1,5 +1,7 @@
import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -8,17 +10,28 @@ import 'cell_service.dart';
part 'date_cell_bloc.freezed.dart';
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
final CellService service;
final CellData cellData;
final CellService _service;
final CellListener _listener;
DateCellBloc({
required this.service,
required this.cellData,
}) : super(DateCellState.initial()) {
DateCellBloc({required CellData cellData})
: _service = CellService(),
_listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
super(DateCellState.initial(cellData)) {
on<DateCellEvent>(
(event, emit) async {
await event.map(
initial: (_InitialCell value) async {},
event.map(
initial: (_InitialCell value) {
_startListening();
},
selectDay: (_SelectDay value) {
_updateCellData(value.day);
},
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
emit(state.copyWith(
cellData: state.cellData.copyWith(cell: value.cell),
content: value.cell.content,
));
},
);
},
);
@ -26,20 +39,60 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@override
Future<void> close() async {
await _listener.stop();
return super.close();
}
void _startListening() {
_listener.updateCellNotifier.addPublishListener((result) {
result.fold(
(notificationData) => _loadCellData(),
(err) => Log.error(err),
);
});
_listener.start();
}
Future<void> _loadCellData() async {
final result = await _service.getCell(
gridId: state.cellData.gridId,
fieldId: state.cellData.field.id,
rowId: state.cellData.rowId,
);
result.fold(
(cell) => add(DateCellEvent.didReceiveCellUpdate(cell)),
(err) => Log.error(err),
);
}
void _updateCellData(DateTime day) {
final data = day.millisecondsSinceEpoch ~/ 1000;
_service.updateCell(
gridId: state.cellData.gridId,
fieldId: state.cellData.field.id,
rowId: state.cellData.rowId,
data: data.toString(),
);
}
}
@freezed
class DateCellEvent with _$DateCellEvent {
const factory DateCellEvent.initial() = _InitialCell;
const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
}
@freezed
class DateCellState with _$DateCellState {
const factory DateCellState({
Cell? cell,
required CellData cellData,
required String content,
DateTime? selectedDay,
}) = _DateCellState;
factory DateCellState.initial() => const DateCellState();
factory DateCellState.initial(CellData cellData) => DateCellState(
cellData: cellData,
content: cellData.cell?.content ?? "",
);
}

View File

@ -4,6 +4,9 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'row_service.freezed.dart';
class RowService {
final String gridId;
@ -29,19 +32,12 @@ class RowService {
}
}
class CellData extends Equatable {
final String gridId;
final String rowId;
final Field field;
final Cell? cell;
const CellData({
required this.rowId,
required this.gridId,
required this.field,
required this.cell,
});
@override
List<Object?> get props => [cell, field];
@freezed
class CellData with _$CellData {
const factory CellData({
required String gridId,
required String rowId,
required Field field,
Cell? cell,
}) = _CellData;
}

View File

@ -1,7 +1,11 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:window_size/window_size.dart';
class DateCell extends StatefulWidget {
final CellData cellData;
@ -20,7 +24,7 @@ class _DateCellState extends State<DateCell> {
@override
void initState() {
_cellBloc = getIt<DateCellBloc>(param1: widget.cellData);
_cellBloc = getIt<DateCellBloc>(param1: widget.cellData)..add(const DateCellEvent.initial());
super.initState();
}
@ -30,7 +34,20 @@ class _DateCellState extends State<DateCell> {
value: _cellBloc,
child: BlocBuilder<DateCellBloc, DateCellState>(
builder: (context, state) {
return Container();
return SizedBox.expand(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _CellCalendar.show(
context,
onSelected: (day) => context.read<DateCellBloc>().add(DateCellEvent.selectDay(day)),
),
child: MouseRegion(
opaque: false,
cursor: SystemMouseCursors.click,
child: FlowyText.medium(state.content, fontSize: 12),
),
),
);
},
),
);
@ -42,3 +59,73 @@ class _DateCellState extends State<DateCell> {
super.dispose();
}
}
final kToday = DateTime.now();
final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
class _CellCalendar extends StatefulWidget {
final void Function(DateTime) onSelected;
const _CellCalendar({required this.onSelected, Key? key}) : super(key: key);
@override
State<_CellCalendar> createState() => _CellCalendarState();
static Future<void> show(BuildContext context, {required void Function(DateTime) onSelected}) async {
_CellCalendar.remove(context);
final window = await getWindowInfo();
final calendar = _CellCalendar(onSelected: onSelected);
const size = Size(460, 400);
FlowyOverlay.of(context).insertWithRect(
widget: OverlayContainer(
child: calendar,
constraints: BoxConstraints.loose(const Size(460, 400)),
),
identifier: _CellCalendar.identifier(),
anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
anchorSize: window.frame.size,
anchorDirection: AnchorDirection.center,
style: FlowyOverlayStyle(blur: false),
);
}
static void remove(BuildContext context) {
FlowyOverlay.of(context).remove(identifier());
}
static String identifier() {
return (_CellCalendar).toString();
}
}
class _CellCalendarState extends State<_CellCalendar> {
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
@override
Widget build(BuildContext context) {
return TableCalendar(
firstDay: kFirstDay,
lastDay: kLastDay,
focusedDay: _focusedDay,
calendarFormat: CalendarFormat.month,
selectedDayPredicate: (day) {
return isSameDay(_selectedDay, day);
},
onDaySelected: (selectedDay, focusedDay) {
if (!isSameDay(_selectedDay, selectedDay)) {
// Call `setState()` when updating the selected day
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
widget.onSelected(selectedDay);
});
}
},
onFormatChanged: (format) {},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
);
}
}

View File

@ -86,7 +86,12 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
return SizedBox.expand(
child: InkWell(
onTap: () {
SelectOptionCellEditor.show(context, state.cellData, state.options, state.selectedOptions);
SelectOptionCellEditor.show(
context,
state.cellData,
state.options,
state.selectedOptions,
);
},
child: Row(children: children),
),

View File

@ -36,10 +36,6 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
Key? key,
}) : super(key: key);
static String identifier() {
return (SelectOptionCellEditor).toString();
}
@override
Widget build(BuildContext context) {
return BlocProvider(
@ -96,6 +92,10 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
FlowyOverlay.of(context).remove(identifier());
}
static String identifier() {
return (SelectOptionCellEditor).toString();
}
@override
bool asBarrier() => true;
}

View File

@ -19,7 +19,7 @@ class FieldTypeList extends StatelessWidget with FlowyOverlayDelegate {
@override
Widget build(BuildContext context) {
final cells = FieldType.values.where((ty) => ty != FieldType.DateTime).map((fieldType) {
final cells = FieldType.values.map((fieldType) {
return FieldTypeCell(
fieldType: fieldType,
onSelectField: (fieldType) {

View File

@ -95,6 +95,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
meta:
dependency: transitive
description:
@ -176,7 +183,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.3"
version: "0.4.8"
textstyle_extensions:
dependency: "direct main"
description:

View File

@ -7,7 +7,7 @@ packages:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.8"
version: "3.1.6"
async:
dependency: transitive
description:
@ -200,7 +200,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.0"
platform:
dependency: transitive
description:
@ -275,7 +275,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.8"
typed_data:
dependency: transitive
description:

View File

@ -6,9 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
flowy_sdk
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
@ -17,8 +14,3 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@ -346,7 +346,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.0"
pedantic:
dependency: transitive
description:
@ -456,7 +456,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.8"
timing:
dependency: transitive
description:

View File

@ -512,7 +512,7 @@ packages:
name: fluttertoast
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.8"
version: "8.0.9"
freezed:
dependency: "direct dev"
description:
@ -1031,6 +1031,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
simple_gesture_detector:
dependency: transitive
description:
name: simple_gesture_detector
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
sized_context:
dependency: "direct main"
description:
@ -1120,6 +1127,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1+2"
table_calendar:
dependency: "direct main"
description:
name: table_calendar
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.5"
term_glyph:
dependency: transitive
description:

View File

@ -73,6 +73,7 @@ dependencies:
cupertino_icons: ^1.0.2
device_info_plus: ^3.2.1
fluttertoast: ^8.0.8
table_calendar: ^3.0.5
dev_dependencies:
flutter_lints: ^1.0.0

View File

@ -69,9 +69,8 @@ impl CellDataOperation for DateTypeOption {
_cell_meta: Option<CellMeta>,
) -> Result<String, FlowyError> {
let changeset = changeset.into();
if let Err(e) = changeset.parse::<i64>() {
tracing::error!("Parse {} to i64 failed: {}", changeset.to_string(), e);
return Err(FlowyError::internal().context(e));
if changeset.parse::<f64>().is_err() || changeset.parse::<i64>().is_err() {
return Err(FlowyError::internal().context(format!("Parse {} failed", changeset.to_string())));
};
Ok(TypeOptionCellData::new(changeset, self.field_type()).json())

View File

@ -74,7 +74,7 @@ fn crate_log_filter(level: String) -> String {
filters.push(format!("lib_ot={}", level));
filters.push(format!("lib_ws={}", level));
filters.push(format!("lib_infra={}", level));
filters.push(format!("flowy_sync={}", level));
filters.push(format!("flowy_sync={}", "debug"));
filters.push(format!("dart_ffi={}", "info"));
filters.push(format!("flowy_database={}", "info"));

View File

@ -8,7 +8,6 @@ use std::collections::HashMap;
use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
pub const DEFAULT_ROW_HEIGHT: i32 = 42;
pub const DEFAULT_FIELD_WIDTH: i32 = 150;
#[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
pub struct GridMeta {
@ -110,6 +109,7 @@ pub struct FieldMeta {
impl FieldMeta {
pub fn new(name: &str, desc: &str, field_type: FieldType) -> Self {
let width = field_type.default_cell_width();
Self {
id: uuid::Uuid::new_v4().to_string(),
name: name.to_string(),
@ -117,7 +117,7 @@ impl FieldMeta {
field_type,
frozen: false,
visibility: true,
width: DEFAULT_FIELD_WIDTH,
width,
type_options: Default::default(),
}
}
@ -270,6 +270,13 @@ impl FieldType {
let ty = self.clone();
format!("{}", ty as u8)
}
pub fn default_cell_width(&self) -> i32 {
match self {
FieldType::DateTime => 180,
_ => 150,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]

View File

@ -156,7 +156,7 @@ impl GridBlockMetaPad {
match cal_diff::<PlainTextAttributes>(old, new) {
None => Ok(None),
Some(delta) => {
tracing::trace!("[GridBlockMeta] Composing change {}", delta.to_delta_str());
tracing::debug!("[GridBlockMeta] Composing change {}", delta.to_delta_str());
self.delta = self.delta.compose(&delta)?;
Ok(Some(GridBlockMetaChange { delta, md5: self.md5() }))
}