mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add calendar view plugin along with backend data (#1611)
* chore: create build-in calendar data * feat: add new calendar view to plugins * chore: add create calendar page test * chore: disable for creation for now * fix: rebase regression Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
e479425297
commit
e8b21955f8
@ -313,5 +313,8 @@
|
||||
"column": {
|
||||
"create_new_card": "New"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"menuName": "Calendar"
|
||||
}
|
||||
}
|
111
frontend/app_flowy/lib/plugins/calendar/calendar.dart
Normal file
111
frontend/app_flowy/lib/plugins/calendar/calendar.dart
Normal file
@ -0,0 +1,111 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../util.dart';
|
||||
|
||||
class CalendarPluginBuilder extends PluginBuilder {
|
||||
@override
|
||||
Plugin build(dynamic data) {
|
||||
if (data is ViewPB) {
|
||||
return CalendarPlugin(pluginType: pluginType, view: data);
|
||||
} else {
|
||||
throw FlowyPluginException.invalidData;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String get menuName => LocaleKeys.calendar_menuName.tr();
|
||||
|
||||
@override
|
||||
String get menuIcon => "editor/date";
|
||||
|
||||
@override
|
||||
PluginType get pluginType => PluginType.calendar;
|
||||
|
||||
@override
|
||||
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.DatabaseFormat;
|
||||
|
||||
@override
|
||||
ViewLayoutTypePB? get layoutType => ViewLayoutTypePB.Calendar;
|
||||
}
|
||||
|
||||
class CalendarPluginConfig implements PluginConfig {
|
||||
@override
|
||||
bool get creatable => false;
|
||||
}
|
||||
|
||||
class CalendarPlugin extends Plugin {
|
||||
@override
|
||||
final ViewPluginNotifier notifier;
|
||||
final PluginType _pluginType;
|
||||
|
||||
CalendarPlugin({
|
||||
required ViewPB view,
|
||||
required PluginType pluginType,
|
||||
}) : _pluginType = pluginType,
|
||||
notifier = ViewPluginNotifier(view: view);
|
||||
|
||||
@override
|
||||
PluginDisplay get display => CalendarPluginDisplay(notifier: notifier);
|
||||
|
||||
@override
|
||||
PluginId get id => notifier.view.id;
|
||||
|
||||
@override
|
||||
PluginType get ty => _pluginType;
|
||||
}
|
||||
|
||||
class CalendarPluginDisplay extends PluginDisplay {
|
||||
final ViewPluginNotifier notifier;
|
||||
CalendarPluginDisplay({required this.notifier, Key? key});
|
||||
|
||||
ViewPB get view => notifier.view;
|
||||
|
||||
@override
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
notifier.isDeleted.addListener(() {
|
||||
notifier.isDeleted.value.fold(() => null, (deletedView) {
|
||||
if (deletedView.hasIndex()) {
|
||||
context.onDeleted(view, deletedView.index);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return BlankPage(key: ValueKey(view.id));
|
||||
// return BoardPage(key: ValueKey(view.id), view: view);
|
||||
}
|
||||
|
||||
@override
|
||||
List<NavigationItem> get navigationItems => [this];
|
||||
}
|
||||
|
||||
// mark for removal
|
||||
class BlankPage extends StatefulWidget {
|
||||
const BlankPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BlankPage> createState() => _BlankPageState();
|
||||
}
|
||||
|
||||
class _BlankPageState extends State<BlankPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox.expand(
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Container(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ enum PluginType {
|
||||
trash,
|
||||
grid,
|
||||
board,
|
||||
calendar,
|
||||
}
|
||||
|
||||
typedef PluginId = String;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:app_flowy/plugins/calendar/calendar.dart';
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/plugins/blank/blank.dart';
|
||||
@ -17,5 +18,7 @@ class PluginLoadTask extends LaunchTask {
|
||||
registerPlugin(builder: DocumentPluginBuilder());
|
||||
registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig());
|
||||
registerPlugin(builder: BoardPluginBuilder(), config: BoardPluginConfig());
|
||||
registerPlugin(
|
||||
builder: CalendarPluginBuilder(), config: CalendarPluginConfig());
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ extension ViewExtension on ViewPB {
|
||||
switch (layout) {
|
||||
case ViewLayoutTypePB.Board:
|
||||
return PluginType.board;
|
||||
case ViewLayoutTypePB.Calendar:
|
||||
return PluginType.calendar;
|
||||
case ViewLayoutTypePB.Document:
|
||||
return PluginType.editor;
|
||||
case ViewLayoutTypePB.Grid:
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:app_flowy/plugins/board/board.dart';
|
||||
import 'package:app_flowy/plugins/calendar/calendar.dart';
|
||||
import 'package:app_flowy/plugins/document/document.dart';
|
||||
import 'package:app_flowy/plugins/grid/grid.dart';
|
||||
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
||||
@ -37,6 +38,7 @@ void main() {
|
||||
assert(bloc.state.views.last.name == "Test grid");
|
||||
assert(bloc.state.views.last.layout == ViewLayoutTypePB.Grid);
|
||||
});
|
||||
|
||||
test('create a kanban', () async {
|
||||
final app = await testContext.createTestApp();
|
||||
final bloc = AppBloc(app: app)..add(const AppEvent.initial());
|
||||
@ -49,4 +51,17 @@ void main() {
|
||||
assert(bloc.state.views.last.name == "Test board");
|
||||
assert(bloc.state.views.last.layout == ViewLayoutTypePB.Board);
|
||||
});
|
||||
|
||||
test('create a calendar', () async {
|
||||
final app = await testContext.createTestApp();
|
||||
final bloc = AppBloc(app: app)..add(const AppEvent.initial());
|
||||
await blocResponseFuture();
|
||||
|
||||
bloc.add(AppEvent.createView("Test calendar", CalendarPluginBuilder()));
|
||||
await blocResponseFuture();
|
||||
|
||||
assert(bloc.state.views.length == 1);
|
||||
assert(bloc.state.views.last.name == "Test calendar");
|
||||
assert(bloc.state.views.last.layout == ViewLayoutTypePB.Calendar);
|
||||
});
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use flowy_folder::{
|
||||
};
|
||||
use flowy_grid::entities::GridLayout;
|
||||
use flowy_grid::manager::{make_grid_view_data, GridManager};
|
||||
use flowy_grid::util::{make_default_board, make_default_grid};
|
||||
use flowy_grid::util::{make_default_board, make_default_calendar, make_default_grid};
|
||||
use flowy_http_model::revision::Revision;
|
||||
use flowy_http_model::ws_data::ClientRevisionWSData;
|
||||
use flowy_net::ClientServerConfiguration;
|
||||
@ -264,6 +264,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
|
||||
let (build_context, layout) = match layout {
|
||||
ViewLayoutTypePB::Grid => (make_default_grid(), GridLayout::Table),
|
||||
ViewLayoutTypePB::Board => (make_default_board(), GridLayout::Board),
|
||||
ViewLayoutTypePB::Calendar => (make_default_calendar(), GridLayout::Calendar),
|
||||
ViewLayoutTypePB::Document => {
|
||||
return FutureResult::new(async move {
|
||||
Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
|
||||
@ -293,6 +294,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
|
||||
let layout = match layout {
|
||||
ViewLayoutTypePB::Grid => GridLayout::Table,
|
||||
ViewLayoutTypePB::Board => GridLayout::Board,
|
||||
ViewLayoutTypePB::Calendar => GridLayout::Calendar,
|
||||
ViewLayoutTypePB::Document => {
|
||||
return FutureResult::new(async move {
|
||||
Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
|
||||
|
@ -86,6 +86,7 @@ pub enum ViewLayoutTypePB {
|
||||
Document = 0,
|
||||
Grid = 3,
|
||||
Board = 4,
|
||||
Calendar = 5,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewLayoutTypePB {
|
||||
@ -100,6 +101,7 @@ impl std::convert::From<ViewLayoutTypeRevision> for ViewLayoutTypePB {
|
||||
ViewLayoutTypeRevision::Grid => ViewLayoutTypePB::Grid,
|
||||
ViewLayoutTypeRevision::Board => ViewLayoutTypePB::Board,
|
||||
ViewLayoutTypeRevision::Document => ViewLayoutTypePB::Document,
|
||||
ViewLayoutTypeRevision::Calendar => ViewLayoutTypePB::Calendar,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,6 +112,7 @@ impl std::convert::From<ViewLayoutTypePB> for ViewLayoutTypeRevision {
|
||||
ViewLayoutTypePB::Grid => ViewLayoutTypeRevision::Grid,
|
||||
ViewLayoutTypePB::Board => ViewLayoutTypeRevision::Board,
|
||||
ViewLayoutTypePB::Document => ViewLayoutTypeRevision::Document,
|
||||
ViewLayoutTypePB::Calendar => ViewLayoutTypeRevision::Calendar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -570,7 +570,7 @@ impl FieldType {
|
||||
}
|
||||
|
||||
pub fn can_be_group(&self) -> bool {
|
||||
self.is_select_option()
|
||||
self.is_select_option() || self.is_checkbox()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ impl GridLayoutPB {
|
||||
pub enum GridLayout {
|
||||
Table = 0,
|
||||
Board = 1,
|
||||
Calendar = 2,
|
||||
}
|
||||
|
||||
impl std::default::Default for GridLayout {
|
||||
@ -62,6 +63,7 @@ impl std::convert::From<LayoutRevision> for GridLayout {
|
||||
match rev {
|
||||
LayoutRevision::Table => GridLayout::Table,
|
||||
LayoutRevision::Board => GridLayout::Board,
|
||||
LayoutRevision::Calendar => GridLayout::Calendar,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,6 +73,7 @@ impl std::convert::From<GridLayout> for LayoutRevision {
|
||||
match layout {
|
||||
GridLayout::Table => LayoutRevision::Table,
|
||||
GridLayout::Board => LayoutRevision::Board,
|
||||
GridLayout::Calendar => LayoutRevision::Calendar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,17 +74,14 @@ where
|
||||
Ok(group_controller)
|
||||
}
|
||||
|
||||
pub fn find_group_field(field_revs: &[Arc<FieldRevision>], layout: &LayoutRevision) -> Option<Arc<FieldRevision>> {
|
||||
match layout {
|
||||
LayoutRevision::Table => field_revs.iter().find(|field_rev| field_rev.is_primary).cloned(),
|
||||
LayoutRevision::Board => field_revs
|
||||
.iter()
|
||||
.find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
field_type.can_be_group()
|
||||
})
|
||||
.cloned(),
|
||||
}
|
||||
pub fn find_group_field(field_revs: &[Arc<FieldRevision>], _layout: &LayoutRevision) -> Option<Arc<FieldRevision>> {
|
||||
field_revs
|
||||
.iter()
|
||||
.find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
field_type.can_be_group()
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Returns a `default` group configuration for the [FieldRevision]
|
||||
|
@ -70,6 +70,34 @@ pub fn make_default_board() -> BuildGridContext {
|
||||
grid_builder.build()
|
||||
}
|
||||
|
||||
pub fn make_default_calendar() -> BuildGridContext {
|
||||
let mut grid_builder = GridBuilder::new();
|
||||
// text
|
||||
let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
|
||||
.name("Description")
|
||||
.visibility(true)
|
||||
.primary(true)
|
||||
.build();
|
||||
grid_builder.add_field(text_field);
|
||||
|
||||
// date
|
||||
let date_type_option = DateTypeOptionBuilder::default();
|
||||
let date_field = FieldBuilder::new(date_type_option)
|
||||
.name("Date")
|
||||
.visibility(true)
|
||||
.build();
|
||||
grid_builder.add_field(date_field);
|
||||
|
||||
// single select
|
||||
let multi_select_type_option = MultiSelectTypeOptionBuilder::default();
|
||||
let multi_select_field = FieldBuilder::new(multi_select_type_option)
|
||||
.name("Tags")
|
||||
.visibility(true)
|
||||
.build();
|
||||
grid_builder.add_field(multi_select_field);
|
||||
grid_builder.build()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn make_default_board_2() -> BuildGridContext {
|
||||
let mut grid_builder = GridBuilder::new();
|
||||
|
@ -57,6 +57,11 @@ impl GridEditorTest {
|
||||
let view_data: Bytes = build_context.into();
|
||||
ViewTest::new_board_view(&sdk, view_data.to_vec()).await
|
||||
}
|
||||
GridLayout::Calendar => {
|
||||
let build_context = make_test_calendar();
|
||||
let view_data: Bytes = build_context.into();
|
||||
ViewTest::new_calendar_view(&sdk, view_data.to_vec()).await
|
||||
}
|
||||
};
|
||||
|
||||
let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
|
||||
@ -103,7 +108,7 @@ impl GridEditorTest {
|
||||
}
|
||||
|
||||
/// returns the first `FieldRevision` in the build-in test grid.
|
||||
/// Not support duplicate `FieldType` in test grid yet.
|
||||
/// Not support duplicate `FieldType` in test grid yet.
|
||||
pub fn get_first_field_rev(&self, field_type: FieldType) -> &Arc<FieldRevision> {
|
||||
self.field_revs
|
||||
.iter()
|
||||
@ -388,6 +393,7 @@ fn make_test_grid() -> BuildGridContext {
|
||||
grid_builder.build()
|
||||
}
|
||||
|
||||
// Kanban board unit test mock data
|
||||
fn make_test_board() -> BuildGridContext {
|
||||
let mut grid_builder = GridBuilder::new();
|
||||
// Iterate through the FieldType to create the corresponding Field.
|
||||
@ -557,3 +563,8 @@ fn make_test_board() -> BuildGridContext {
|
||||
}
|
||||
grid_builder.build()
|
||||
}
|
||||
|
||||
// Calendar unit test mock data
|
||||
fn make_test_calendar() -> BuildGridContext {
|
||||
todo!()
|
||||
}
|
||||
|
@ -51,6 +51,10 @@ impl ViewTest {
|
||||
Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Board, data).await
|
||||
}
|
||||
|
||||
pub async fn new_calendar_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
|
||||
Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Calendar, data).await
|
||||
}
|
||||
|
||||
pub async fn new_document_view(sdk: &FlowySDKTest) -> Self {
|
||||
let view_data_format = match sdk.document_version() {
|
||||
DocumentVersionPB::V0 => ViewDataFormatPB::DeltaFormat,
|
||||
|
@ -75,6 +75,7 @@ pub enum ViewLayoutTypeRevision {
|
||||
// The for historical reasons, the value of Grid is not 1.
|
||||
Grid = 3,
|
||||
Board = 4,
|
||||
Calendar = 5,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewLayoutTypeRevision {
|
||||
|
@ -13,6 +13,7 @@ pub fn gen_grid_view_id() -> String {
|
||||
pub enum LayoutRevision {
|
||||
Table = 0,
|
||||
Board = 1,
|
||||
Calendar = 2,
|
||||
}
|
||||
|
||||
impl ToString for LayoutRevision {
|
||||
|
Loading…
Reference in New Issue
Block a user