refactor: add plugins

This commit is contained in:
appflowy 2022-02-28 22:38:53 +08:00
parent 74831964a6
commit 8747457836
57 changed files with 975 additions and 647 deletions

View File

@ -0,0 +1,60 @@
library flowy_plugin;
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/widgets.dart';
export "./src/sandbox.dart";
typedef PluginType = String;
typedef PluginDataType = ViewDataType;
abstract class Plugin {
PluginType get pluginType;
String get pluginId;
bool get enable;
void dispose();
PluginDisplay get display;
}
abstract class PluginBuilder {
Plugin build(dynamic data);
String get pluginName;
PluginType get pluginType;
ViewDataType get dataType;
}
abstract class PluginDisplay with NavigationItem {
@override
Widget get leftBarItem;
@override
Widget? get rightBarItem;
List<NavigationItem> get navigationItems;
Widget buildWidget();
}
void registerPlugin({required PluginBuilder builder}) {
getIt<PluginSandbox>().registerPlugin(builder.pluginType, builder);
}
Plugin makePlugin({required String pluginType, dynamic data}) {
final plugin = getIt<PluginSandbox>().buildPlugin(pluginType, data);
return plugin;
}
enum FlowyPluginException {
invalidData,
}

View File

@ -0,0 +1 @@
class PluginRunner {}

View File

@ -0,0 +1,40 @@
import 'dart:collection';
import 'package:flutter/services.dart';
import '../plugin.dart';
import 'runner.dart';
class PluginSandbox {
final LinkedHashMap<String, PluginBuilder> _pluginMap = LinkedHashMap();
late PluginRunner pluginRunner;
PluginSandbox() {
pluginRunner = PluginRunner();
}
int indexOf(String pluginType) {
final index = _pluginMap.keys.toList().indexWhere((ty) => ty == pluginType);
if (index == -1) {
throw PlatformException(code: '-1', message: "Can't find the flowy plugin type: $pluginType");
}
return index;
}
Plugin buildPlugin(String pluginType, dynamic data) {
final index = indexOf(pluginType);
final plugin = _pluginMap[index]!.build(data);
return plugin;
}
void registerPlugin(String pluginType, PluginBuilder builder) {
if (_pluginMap.containsKey(pluginType)) {
throw PlatformException(code: '-1', message: "$pluginType was registered before");
}
_pluginMap[pluginType] = builder;
}
List<String> get supportPluginTypes => _pluginMap.keys.toList();
List<PluginBuilder> get builders => _pluginMap.values.toList();
}

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/tasks/prelude.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -41,6 +42,7 @@ class FlowyRunner {
getIt<AppLauncher>().addTask(InitRustSDKTask());
if (!env.isTest()) {
getIt<AppLauncher>().addTask(PluginLoadTask());
getIt<AppLauncher>().addTask(InitAppWidgetTask());
getIt<AppLauncher>().addTask(InitPlatformServiceTask());
}
@ -58,6 +60,7 @@ Future<void> initGetIt(
getIt.registerFactory<EntryPoint>(() => f);
getIt.registerLazySingleton<FlowySDK>(() => const FlowySDK());
getIt.registerLazySingleton<AppLauncher>(() => AppLauncher(env, getIt));
getIt.registerSingleton<PluginSandbox>(PluginSandbox());
await UserDepsResolver.resolve(getIt);
await HomeDepsResolver.resolve(getIt);

View File

@ -0,0 +1,39 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/presentation/stack_page/blank/blank_page.dart';
import 'package:app_flowy/workspace/presentation/stack_page/doc/doc_stack_page.dart';
import 'package:app_flowy/workspace/presentation/stack_page/trash/trash_page.dart';
enum DefaultPluginEnum {
blank,
trash,
}
extension FlowyDefaultPluginExt on DefaultPluginEnum {
String type() {
switch (this) {
case DefaultPluginEnum.blank:
return "Blank";
case DefaultPluginEnum.trash:
return "Trash";
}
}
}
bool isDefaultPlugin(String pluginType) {
return DefaultPluginEnum.values.map((e) => e.type()).contains(pluginType);
}
class PluginLoadTask extends LaunchTask {
@override
LaunchTaskType get type => LaunchTaskType.dataProcessing;
@override
Future<void> initialize(LaunchContext context) async {
registerPlugin(builder: DocumentPluginBuilder());
registerPlugin(builder: TrashPluginBuilder());
registerPlugin(builder: BlankPluginBuilder());
}
}

View File

@ -1,3 +1,4 @@
export 'app_widget.dart';
export 'init_sdk.dart';
export 'rust_sdk.dart';
export 'platform_service.dart';
export 'load_plugin.dart';

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/workspace/infrastructure/repos/app_repo.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
@ -21,7 +22,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
);
await _fetchViews(emit);
}, createView: (CreateView value) async {
final viewOrFailed = await repo.createView(name: value.name, desc: value.desc, viewType: value.viewType);
final viewOrFailed = await repo.createView(name: value.name, desc: value.desc, dataType: value.dataType);
viewOrFailed.fold(
(view) => emit(state.copyWith(
latestCreatedView: view,
@ -95,7 +96,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
@freezed
class AppEvent with _$AppEvent {
const factory AppEvent.initial() = Initial;
const factory AppEvent.createView(String name, String desc, ViewType viewType) = CreateView;
const factory AppEvent.createView(String name, String desc, PluginDataType dataType) = CreateView;
const factory AppEvent.delete() = Delete;
const factory AppEvent.rename(String newName) = Rename;
const factory AppEvent.didReceiveViews(List<View> views) = ReceiveViews;

View File

@ -1,7 +1,7 @@
import 'dart:async';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/tasks/load_plugin.dart';
import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
import 'package:app_flowy/workspace/presentation/stack_page/blank/blank_page.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
@ -26,7 +26,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
emit(state.copyWith(isCollapse: !isCollapse));
},
openPage: (e) async {
emit(state.copyWith(stackContext: e.context));
emit(state.copyWith(plugin: e.plugin));
},
createApp: (CreateApp event) async {
await _performActionOnCreateApp(event, emit);
@ -82,7 +82,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
class MenuEvent with _$MenuEvent {
const factory MenuEvent.initial() = _Initial;
const factory MenuEvent.collapse() = Collapse;
const factory MenuEvent.openPage(HomeStackContext context) = OpenPage;
const factory MenuEvent.openPage(Plugin plugin) = OpenPage;
const factory MenuEvent.createApp(String name, {String? desc}) = CreateApp;
const factory MenuEvent.didReceiveApps(Either<List<App>, FlowyError> appsOrFail) = ReceiveApps;
}
@ -93,13 +93,13 @@ class MenuState with _$MenuState {
required bool isCollapse,
required Option<List<App>> apps,
required Either<Unit, FlowyError> successOrFailure,
required HomeStackContext stackContext,
required Plugin plugin,
}) = _MenuState;
factory MenuState.initial() => MenuState(
isCollapse: false,
apps: none(),
successOrFailure: left(unit),
stackContext: BlankStackContext(),
plugin: makePlugin(pluginType: DefaultPluginEnum.blank.type()),
);
}

View File

@ -25,9 +25,9 @@ class _$MenuEventTearOff {
return const Collapse();
}
OpenPage openPage(HomeStackContext<dynamic, dynamic> context) {
OpenPage openPage(Plugin plugin) {
return OpenPage(
context,
plugin,
);
}
@ -54,8 +54,7 @@ mixin _$MenuEvent {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() collapse,
required TResult Function(HomeStackContext<dynamic, dynamic> context)
openPage,
required TResult Function(Plugin plugin) openPage,
required TResult Function(String name, String? desc) createApp,
required TResult Function(Either<List<App>, FlowyError> appsOrFail)
didReceiveApps,
@ -65,7 +64,7 @@ mixin _$MenuEvent {
TResult? whenOrNull<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
}) =>
@ -74,7 +73,7 @@ mixin _$MenuEvent {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
required TResult orElse(),
@ -164,8 +163,7 @@ class _$_Initial implements _Initial {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() collapse,
required TResult Function(HomeStackContext<dynamic, dynamic> context)
openPage,
required TResult Function(Plugin plugin) openPage,
required TResult Function(String name, String? desc) createApp,
required TResult Function(Either<List<App>, FlowyError> appsOrFail)
didReceiveApps,
@ -178,7 +176,7 @@ class _$_Initial implements _Initial {
TResult? whenOrNull<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
}) {
@ -190,7 +188,7 @@ class _$_Initial implements _Initial {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
required TResult orElse(),
@ -285,8 +283,7 @@ class _$Collapse implements Collapse {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() collapse,
required TResult Function(HomeStackContext<dynamic, dynamic> context)
openPage,
required TResult Function(Plugin plugin) openPage,
required TResult Function(String name, String? desc) createApp,
required TResult Function(Either<List<App>, FlowyError> appsOrFail)
didReceiveApps,
@ -299,7 +296,7 @@ class _$Collapse implements Collapse {
TResult? whenOrNull<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
}) {
@ -311,7 +308,7 @@ class _$Collapse implements Collapse {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
required TResult orElse(),
@ -371,7 +368,7 @@ abstract class Collapse implements MenuEvent {
abstract class $OpenPageCopyWith<$Res> {
factory $OpenPageCopyWith(OpenPage value, $Res Function(OpenPage) then) =
_$OpenPageCopyWithImpl<$Res>;
$Res call({HomeStackContext<dynamic, dynamic> context});
$Res call({Plugin plugin});
}
/// @nodoc
@ -385,13 +382,13 @@ class _$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
@override
$Res call({
Object? context = freezed,
Object? plugin = freezed,
}) {
return _then(OpenPage(
context == freezed
? _value.context
: context // ignore: cast_nullable_to_non_nullable
as HomeStackContext<dynamic, dynamic>,
plugin == freezed
? _value.plugin
: plugin // ignore: cast_nullable_to_non_nullable
as Plugin,
));
}
}
@ -399,27 +396,27 @@ class _$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
/// @nodoc
class _$OpenPage implements OpenPage {
const _$OpenPage(this.context);
const _$OpenPage(this.plugin);
@override
final HomeStackContext<dynamic, dynamic> context;
final Plugin plugin;
@override
String toString() {
return 'MenuEvent.openPage(context: $context)';
return 'MenuEvent.openPage(plugin: $plugin)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is OpenPage &&
(identical(other.context, context) ||
const DeepCollectionEquality().equals(other.context, context)));
(identical(other.plugin, plugin) ||
const DeepCollectionEquality().equals(other.plugin, plugin)));
}
@override
int get hashCode =>
runtimeType.hashCode ^ const DeepCollectionEquality().hash(context);
runtimeType.hashCode ^ const DeepCollectionEquality().hash(plugin);
@JsonKey(ignore: true)
@override
@ -431,13 +428,12 @@ class _$OpenPage implements OpenPage {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() collapse,
required TResult Function(HomeStackContext<dynamic, dynamic> context)
openPage,
required TResult Function(Plugin plugin) openPage,
required TResult Function(String name, String? desc) createApp,
required TResult Function(Either<List<App>, FlowyError> appsOrFail)
didReceiveApps,
}) {
return openPage(context);
return openPage(plugin);
}
@override
@ -445,11 +441,11 @@ class _$OpenPage implements OpenPage {
TResult? whenOrNull<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
}) {
return openPage?.call(context);
return openPage?.call(plugin);
}
@override
@ -457,13 +453,13 @@ class _$OpenPage implements OpenPage {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
required TResult orElse(),
}) {
if (openPage != null) {
return openPage(context);
return openPage(plugin);
}
return orElse();
}
@ -510,11 +506,9 @@ class _$OpenPage implements OpenPage {
}
abstract class OpenPage implements MenuEvent {
const factory OpenPage(HomeStackContext<dynamic, dynamic> context) =
_$OpenPage;
const factory OpenPage(Plugin plugin) = _$OpenPage;
HomeStackContext<dynamic, dynamic> get context =>
throw _privateConstructorUsedError;
Plugin get plugin => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$OpenPageCopyWith<OpenPage> get copyWith =>
throw _privateConstructorUsedError;
@ -595,8 +589,7 @@ class _$CreateApp implements CreateApp {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() collapse,
required TResult Function(HomeStackContext<dynamic, dynamic> context)
openPage,
required TResult Function(Plugin plugin) openPage,
required TResult Function(String name, String? desc) createApp,
required TResult Function(Either<List<App>, FlowyError> appsOrFail)
didReceiveApps,
@ -609,7 +602,7 @@ class _$CreateApp implements CreateApp {
TResult? whenOrNull<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
}) {
@ -621,7 +614,7 @@ class _$CreateApp implements CreateApp {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
required TResult orElse(),
@ -750,8 +743,7 @@ class _$ReceiveApps implements ReceiveApps {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() collapse,
required TResult Function(HomeStackContext<dynamic, dynamic> context)
openPage,
required TResult Function(Plugin plugin) openPage,
required TResult Function(String name, String? desc) createApp,
required TResult Function(Either<List<App>, FlowyError> appsOrFail)
didReceiveApps,
@ -764,7 +756,7 @@ class _$ReceiveApps implements ReceiveApps {
TResult? whenOrNull<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
}) {
@ -776,7 +768,7 @@ class _$ReceiveApps implements ReceiveApps {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? collapse,
TResult Function(HomeStackContext<dynamic, dynamic> context)? openPage,
TResult Function(Plugin plugin)? openPage,
TResult Function(String name, String? desc)? createApp,
TResult Function(Either<List<App>, FlowyError> appsOrFail)? didReceiveApps,
required TResult orElse(),
@ -847,12 +839,12 @@ class _$MenuStateTearOff {
{required bool isCollapse,
required Option<List<App>> apps,
required Either<Unit, FlowyError> successOrFailure,
required HomeStackContext<dynamic, dynamic> stackContext}) {
required Plugin plugin}) {
return _MenuState(
isCollapse: isCollapse,
apps: apps,
successOrFailure: successOrFailure,
stackContext: stackContext,
plugin: plugin,
);
}
}
@ -866,8 +858,7 @@ mixin _$MenuState {
Option<List<App>> get apps => throw _privateConstructorUsedError;
Either<Unit, FlowyError> get successOrFailure =>
throw _privateConstructorUsedError;
HomeStackContext<dynamic, dynamic> get stackContext =>
throw _privateConstructorUsedError;
Plugin get plugin => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$MenuStateCopyWith<MenuState> get copyWith =>
@ -882,7 +873,7 @@ abstract class $MenuStateCopyWith<$Res> {
{bool isCollapse,
Option<List<App>> apps,
Either<Unit, FlowyError> successOrFailure,
HomeStackContext<dynamic, dynamic> stackContext});
Plugin plugin});
}
/// @nodoc
@ -898,7 +889,7 @@ class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
Object? isCollapse = freezed,
Object? apps = freezed,
Object? successOrFailure = freezed,
Object? stackContext = freezed,
Object? plugin = freezed,
}) {
return _then(_value.copyWith(
isCollapse: isCollapse == freezed
@ -913,10 +904,10 @@ class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
? _value.successOrFailure
: successOrFailure // ignore: cast_nullable_to_non_nullable
as Either<Unit, FlowyError>,
stackContext: stackContext == freezed
? _value.stackContext
: stackContext // ignore: cast_nullable_to_non_nullable
as HomeStackContext<dynamic, dynamic>,
plugin: plugin == freezed
? _value.plugin
: plugin // ignore: cast_nullable_to_non_nullable
as Plugin,
));
}
}
@ -931,7 +922,7 @@ abstract class _$MenuStateCopyWith<$Res> implements $MenuStateCopyWith<$Res> {
{bool isCollapse,
Option<List<App>> apps,
Either<Unit, FlowyError> successOrFailure,
HomeStackContext<dynamic, dynamic> stackContext});
Plugin plugin});
}
/// @nodoc
@ -948,7 +939,7 @@ class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
Object? isCollapse = freezed,
Object? apps = freezed,
Object? successOrFailure = freezed,
Object? stackContext = freezed,
Object? plugin = freezed,
}) {
return _then(_MenuState(
isCollapse: isCollapse == freezed
@ -963,10 +954,10 @@ class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
? _value.successOrFailure
: successOrFailure // ignore: cast_nullable_to_non_nullable
as Either<Unit, FlowyError>,
stackContext: stackContext == freezed
? _value.stackContext
: stackContext // ignore: cast_nullable_to_non_nullable
as HomeStackContext<dynamic, dynamic>,
plugin: plugin == freezed
? _value.plugin
: plugin // ignore: cast_nullable_to_non_nullable
as Plugin,
));
}
}
@ -978,7 +969,7 @@ class _$_MenuState implements _MenuState {
{required this.isCollapse,
required this.apps,
required this.successOrFailure,
required this.stackContext});
required this.plugin});
@override
final bool isCollapse;
@ -987,11 +978,11 @@ class _$_MenuState implements _MenuState {
@override
final Either<Unit, FlowyError> successOrFailure;
@override
final HomeStackContext<dynamic, dynamic> stackContext;
final Plugin plugin;
@override
String toString() {
return 'MenuState(isCollapse: $isCollapse, apps: $apps, successOrFailure: $successOrFailure, stackContext: $stackContext)';
return 'MenuState(isCollapse: $isCollapse, apps: $apps, successOrFailure: $successOrFailure, plugin: $plugin)';
}
@override
@ -1006,9 +997,8 @@ class _$_MenuState implements _MenuState {
(identical(other.successOrFailure, successOrFailure) ||
const DeepCollectionEquality()
.equals(other.successOrFailure, successOrFailure)) &&
(identical(other.stackContext, stackContext) ||
const DeepCollectionEquality()
.equals(other.stackContext, stackContext)));
(identical(other.plugin, plugin) ||
const DeepCollectionEquality().equals(other.plugin, plugin)));
}
@override
@ -1017,7 +1007,7 @@ class _$_MenuState implements _MenuState {
const DeepCollectionEquality().hash(isCollapse) ^
const DeepCollectionEquality().hash(apps) ^
const DeepCollectionEquality().hash(successOrFailure) ^
const DeepCollectionEquality().hash(stackContext);
const DeepCollectionEquality().hash(plugin);
@JsonKey(ignore: true)
@override
@ -1030,7 +1020,7 @@ abstract class _MenuState implements MenuState {
{required bool isCollapse,
required Option<List<App>> apps,
required Either<Unit, FlowyError> successOrFailure,
required HomeStackContext<dynamic, dynamic> stackContext}) = _$_MenuState;
required Plugin plugin}) = _$_MenuState;
@override
bool get isCollapse => throw _privateConstructorUsedError;
@ -1040,8 +1030,7 @@ abstract class _MenuState implements MenuState {
Either<Unit, FlowyError> get successOrFailure =>
throw _privateConstructorUsedError;
@override
HomeStackContext<dynamic, dynamic> get stackContext =>
throw _privateConstructorUsedError;
Plugin get plugin => throw _privateConstructorUsedError;
@override
@JsonKey(ignore: true)
_$MenuStateCopyWith<_MenuState> get copyWith =>

View File

@ -9,7 +9,6 @@ part 'view_bloc.freezed.dart';
class ViewMenuBloc extends Bloc<ViewEvent, ViewState> {
final ViewRepository repo;
final ViewListener listener;
ViewMenuBloc({

View File

@ -1,27 +0,0 @@
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flowy_infra/image.dart';
AssetImage assetImageForViewType(ViewType type) {
final imageName = _imageNameForViewType(type);
return AssetImage('assets/images/$imageName');
}
extension SvgViewType on View {
Widget thumbnail({Color? iconColor}) {
final imageName = _imageNameForViewType(viewType);
final Widget widget = svg(imageName, color: iconColor);
return widget;
}
}
String _imageNameForViewType(ViewType type) {
switch (type) {
case ViewType.RichText:
return "file_icon";
case ViewType.Plugin:
return "file_icon";
default:
return "file_icon";
}
}

View File

@ -1,3 +1,5 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/tasks/load_plugin.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -10,23 +12,13 @@ typedef NavigationCallback = void Function(String id);
abstract class NavigationItem {
Widget get leftBarItem;
Widget? get rightBarItem => null;
String get identifier;
NavigationCallback get action => (id) {
getIt<HomeStackManager>().setStackWithId(id);
};
}
enum HomeStackType {
blank,
document,
kanban,
trash,
}
List<HomeStackType> pages = HomeStackType.values.toList();
abstract class HomeStackContext<T, S> with NavigationItem {
abstract class HomeStackContext<T> with NavigationItem {
List<NavigationItem> get navigationItems;
@override
@ -35,40 +27,35 @@ abstract class HomeStackContext<T, S> with NavigationItem {
@override
Widget? get rightBarItem;
@override
String get identifier;
ValueNotifier<T> get isUpdated;
HomeStackType get type;
Widget buildWidget();
void dispose();
}
class HomeStackNotifier extends ChangeNotifier {
HomeStackContext stackContext;
Plugin _plugin;
PublishNotifier<bool> collapsedNotifier = PublishNotifier();
Widget get titleWidget => stackContext.leftBarItem;
Widget get titleWidget => _plugin.display.leftBarItem;
HomeStackNotifier({HomeStackContext? context}) : stackContext = context ?? BlankStackContext();
HomeStackNotifier({Plugin? plugin}) : _plugin = plugin ?? makePlugin(pluginType: DefaultPluginEnum.blank.type());
set context(HomeStackContext context) {
if (stackContext.identifier == context.identifier) {
set plugin(Plugin newPlugin) {
if (newPlugin.pluginId == _plugin.pluginId) {
return;
}
stackContext.isUpdated.removeListener(notifyListeners);
stackContext.dispose();
// stackContext.isUpdated.removeListener(notifyListeners);
_plugin.dispose();
stackContext = context;
stackContext.isUpdated.addListener(notifyListeners);
_plugin = newPlugin;
// stackContext.isUpdated.addListener(notifyListeners);
notifyListeners();
}
HomeStackContext get context => stackContext;
Plugin get plugin => _plugin;
}
// HomeStack is initialized as singleton to controll the page stack.
@ -77,13 +64,13 @@ class HomeStackManager {
HomeStackManager();
Widget title() {
return _notifier.context.leftBarItem;
return _notifier.plugin.display.leftBarItem;
}
PublishNotifier<bool> get collapsedNotifier => _notifier.collapsedNotifier;
void setStack(HomeStackContext context) {
_notifier.context = context;
void setPlugin(Plugin newPlugin) {
_notifier.plugin = newPlugin;
}
void setStackWithId(String id) {}
@ -109,10 +96,10 @@ class HomeStackManager {
],
child: Consumer(builder: (ctx, HomeStackNotifier notifier, child) {
return FadingIndexedStack(
index: pages.indexOf(notifier.context.type),
children: HomeStackType.values.map((viewType) {
if (viewType == notifier.context.type) {
return notifier.context.buildWidget();
index: getIt<PluginSandbox>().indexOf(notifier.plugin.pluginType),
children: getIt<PluginSandbox>().supportPluginTypes.map((pluginType) {
if (pluginType == notifier.plugin.pluginType) {
return notifier.plugin.display.buildWidget();
} else {
return const BlankStackPage();
}

View File

@ -1,40 +1,18 @@
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/stack_page/blank/blank_page.dart';
import 'package:app_flowy/workspace/presentation/stack_page/doc/doc_stack_page.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
extension ToHomeStackContext on View {
HomeStackContext stackContext() {
switch (viewType) {
case ViewType.RichText:
return DocumentStackContext(view: this);
case ViewType.Plugin:
return DocumentStackContext(view: this);
default:
return BlankStackContext();
}
}
enum FlowyPlugin {
editor,
kanban,
}
extension ToHomeStackType on View {
HomeStackType stackType() {
switch (viewType) {
case ViewType.RichText:
return HomeStackType.document;
case ViewType.PlainText:
return HomeStackType.kanban;
default:
return HomeStackType.blank;
}
}
}
extension ViewTypeExtension on ViewType {
extension FlowyPluginExtension on FlowyPlugin {
String displayName() {
switch (this) {
case ViewType.RichText:
case FlowyPlugin.editor:
return "Doc";
case ViewType.Plugin:
case FlowyPlugin.kanban:
return "Kanban";
default:
return "";
@ -43,12 +21,24 @@ extension ViewTypeExtension on ViewType {
bool enable() {
switch (this) {
case ViewType.RichText:
case FlowyPlugin.editor:
return true;
case ViewType.Plugin:
case FlowyPlugin.kanban:
return false;
default:
return false;
}
}
}
extension ViewExtension on View {
Widget renderThumbnail({Color? iconColor}) {
String thumbnail = this.thumbnail;
if (thumbnail.isEmpty) {
thumbnail = "file_icon";
}
final Widget widget = svg(thumbnail, color: iconColor);
return widget;
}
}

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
@ -26,13 +27,13 @@ class AppRepository {
Future<Either<View, FlowyError>> createView({
required String name,
required String desc,
required ViewType viewType,
required PluginDataType dataType,
}) {
final request = CreateViewPayload.create()
..belongToId = appId
..name = name
..desc = desc
..viewType = viewType;
..dataType = dataType;
return FolderEventCreateView(request).send();
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
import 'package:app_flowy/workspace/application/home/home_listen_bloc.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
@ -109,7 +110,8 @@ class _HomeScreenState extends State<HomeScreen> {
Widget _buildHomeMenu({required HomeLayout layout, required BuildContext context}) {
if (initialView == null && widget.workspaceSetting.hasLatestView()) {
initialView = widget.workspaceSetting.latestView;
getIt<HomeStackManager>().setStack(initialView!.stackContext());
final plugin = makePlugin(pluginType: "RichText", data: initialView);
getIt<HomeStackManager>().setPlugin(plugin);
}
HomeMenu homeMenu = HomeMenu(

View File

@ -17,8 +17,8 @@ class NavigationNotifier with ChangeNotifier {
void update(HomeStackNotifier notifier) {
bool shouldNotify = false;
if (navigationItems != notifier.context.navigationItems) {
navigationItems = notifier.context.navigationItems;
if (navigationItems != notifier.plugin.display.navigationItems) {
navigationItems = notifier.plugin.display.navigationItems;
shouldNotify = true;
}
@ -59,7 +59,7 @@ class FlowyNavigation extends StatelessWidget {
create: (_) {
final notifier = Provider.of<HomeStackNotifier>(context, listen: false);
return NavigationNotifier(
navigationItems: notifier.context.navigationItems,
navigationItems: notifier.plugin.display.navigationItems,
collapasedNotifier: notifier.collapsedNotifier,
);
},

View File

@ -1,24 +1,60 @@
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/startup/tasks/load_plugin.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pbenum.dart';
import 'package:flutter/material.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
class BlankStackContext extends HomeStackContext {
final ValueNotifier<bool> _isUpdated = ValueNotifier<bool>(false);
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
class BlankPluginBuilder implements PluginBuilder {
@override
Plugin build(dynamic data) {
return BlankPagePlugin(pluginType: pluginType);
}
@override
String get identifier => "1";
String get pluginName => "Blank";
@override
PluginType get pluginType => DefaultPluginEnum.blank.type();
@override
ViewDataType get dataType => ViewDataType.PlainText;
}
class BlankPagePlugin implements Plugin {
late PluginType _pluginType;
BlankPagePlugin({
required String pluginType,
}) {
_pluginType = pluginType;
}
@override
void dispose() {}
@override
PluginDisplay get display => BlankPagePluginDisplay();
@override
bool get enable => true;
@override
String get pluginId => "BlankStack";
@override
PluginType get pluginType => _pluginType;
}
class BlankPagePluginDisplay extends PluginDisplay {
@override
Widget get leftBarItem => FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12);
@override
Widget? get rightBarItem => null;
@override
HomeStackType get type => HomeStackType.blank;
@override
Widget buildWidget() {
return const BlankStackPage();
@ -26,12 +62,6 @@ class BlankStackContext extends HomeStackContext {
@override
List<NavigationItem> get navigationItems => [this];
@override
ValueNotifier<bool> get isUpdated => _isUpdated;
@override
void dispose() {}
}
class BlankStackPage extends StatefulWidget {

View File

@ -1,8 +1,8 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/appearance.dart';
import 'package:app_flowy/workspace/application/doc/share_bloc.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/domain/view_ext.dart';
import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
@ -24,12 +24,34 @@ import 'package:provider/provider.dart';
import 'document_page.dart';
class DocumentStackContext extends HomeStackContext<int, ShareActionWrapper> {
View _view;
class DocumentPluginBuilder implements PluginBuilder {
@override
Plugin build(dynamic data) {
if (data is View) {
return DocumentPlugin(pluginType: pluginType, view: data);
} else {
throw FlowyPluginException.invalidData;
}
}
@override
String get pluginName => "Doc";
@override
PluginType get pluginType => "RichText";
@override
ViewDataType get dataType => ViewDataType.RichText;
}
class DocumentPlugin implements Plugin {
late View _view;
late ViewListener _listener;
final ValueNotifier<int> _isUpdated = ValueNotifier<int>(0);
late PluginType _pluginType;
DocumentStackContext({required View view, Key? key}) : _view = view {
DocumentPlugin({required PluginType pluginType, required View view, Key? key}) : _view = view {
_pluginType = pluginType;
_listener = getIt<ViewListener>(param1: view);
_listener.updatedNotifier.addPublishListener((result) {
result.fold(
@ -43,37 +65,46 @@ class DocumentStackContext extends HomeStackContext<int, ShareActionWrapper> {
_listener.start();
}
@override
void dispose() {
_listener.close();
}
@override
PluginDisplay get display => DocumentPluginDisplay(view: _view);
@override
bool get enable => true;
@override
PluginType get pluginType => _pluginType;
@override
String get pluginId => _view.id;
}
class DocumentPluginDisplay extends PluginDisplay {
final View _view;
DocumentPluginDisplay({required View view, Key? key}) : _view = view;
@override
Widget buildWidget() => DocumentPage(view: _view, key: ValueKey(_view.id));
@override
Widget get leftBarItem => DocumentLeftBarItem(view: _view);
@override
Widget? get rightBarItem => DocumentShareButton(view: _view);
@override
String get identifier => _view.id;
@override
HomeStackType get type => _view.stackType();
@override
Widget buildWidget() => DocumentPage(view: _view, key: ValueKey(_view.id));
@override
List<NavigationItem> get navigationItems => _makeNavigationItems();
@override
ValueNotifier<int> get isUpdated => _isUpdated;
List<NavigationItem> _makeNavigationItems() {
return [
this,
];
}
@override
void dispose() {
_listener.close();
}
}
class DocumentLeftBarItem extends StatefulWidget {

View File

@ -10,17 +10,6 @@ import 'package:fluttertoast/fluttertoast.dart';
late FToast fToast;
// [[diagram: HomeStack's widget structure]]
//
//
// BlankStackContext BlankStackPage
//
// HomeStack HomeStackManager HomeStackContext
//
// DocStackContext DocStackPage
//
//
//
class HomeStack extends StatelessWidget {
static GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
// final Size size;

View File

@ -1,4 +1,6 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/startup/tasks/load_plugin.dart';
import 'package:app_flowy/workspace/application/trash/trash_bloc.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/stack_page/trash/widget/sizes.dart';
@ -12,6 +14,7 @@ import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pbenum.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
@ -19,32 +22,57 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
import 'widget/trash_header.dart';
class TrashStackContext extends HomeStackContext {
final ValueNotifier<bool> _isUpdated = ValueNotifier<bool>(false);
class TrashPluginBuilder implements PluginBuilder {
@override
Plugin build(dynamic data) {
return TrashPlugin(pluginType: pluginType);
}
@override
String get identifier => "TrashStackContext";
String get pluginName => "Trash";
@override
PluginType get pluginType => DefaultPluginEnum.trash.type();
@override
ViewDataType get dataType => ViewDataType.PlainText;
}
class TrashPlugin implements Plugin {
late PluginType _pluginType;
TrashPlugin({required PluginType pluginType}) {
_pluginType = pluginType;
}
@override
void dispose() {}
@override
PluginDisplay get display => TrashPluginDisplay();
@override
bool get enable => true;
@override
String get pluginId => "TrashStack";
@override
PluginType get pluginType => _pluginType;
}
class TrashPluginDisplay extends PluginDisplay {
@override
Widget get leftBarItem => FlowyText.medium(LocaleKeys.trash_text.tr(), fontSize: 12);
@override
Widget? get rightBarItem => null;
@override
HomeStackType get type => HomeStackType.trash;
@override
Widget buildWidget() => const TrashStackPage(key: ValueKey('TrashStackPage'));
@override
List<NavigationItem> get navigationItems => [this];
@override
ValueNotifier<bool> get isUpdated => _isUpdated;
@override
void dispose() {}
}
class TrashStackPage extends StatefulWidget {

View File

@ -1,13 +1,10 @@
import 'package:app_flowy/workspace/domain/image.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
import 'package:app_flowy/workspace/presentation/home/navigation.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flowy_infra_ui/style_widget/extension.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:provider/provider.dart';
class HomeTopBar extends StatelessWidget {
@ -28,7 +25,7 @@ class HomeTopBar extends StatelessWidget {
value: Provider.of<HomeStackNotifier>(context, listen: false),
child: Consumer(
builder: (BuildContext context, HomeStackNotifier notifier, Widget? child) {
return notifier.stackContext.rightBarItem ?? const SizedBox();
return notifier.plugin.display.rightBarItem ?? const SizedBox();
},
),
) // _renderMoreButton(),
@ -41,27 +38,3 @@ class HomeTopBar extends StatelessWidget {
);
}
}
class HomeTitle extends StatelessWidget {
final String title;
final ViewType type;
const HomeTitle({
Key? key,
required this.title,
required this.type,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Flexible(
child: Row(
children: [
Image(fit: BoxFit.scaleDown, width: 15, height: 15, image: assetImageForViewType(type)),
const HSpace(6),
FlowyText(title, fontSize: 16),
],
),
);
}
}

View File

@ -70,9 +70,9 @@ class HomeMenu extends StatelessWidget {
child: MultiBlocListener(
listeners: [
BlocListener<MenuBloc, MenuState>(
listenWhen: (p, c) => p.stackContext != c.stackContext,
listenWhen: (p, c) => p.plugin.pluginId != c.plugin.pluginId,
listener: (context, state) {
getIt<HomeStackManager>().setStack(state.stackContext);
getIt<HomeStackManager>().setPlugin(state.plugin);
},
),
BlocListener<MenuBloc, MenuState>(

View File

@ -1,3 +1,6 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/startup/tasks/load_plugin.dart';
import 'package:app_flowy/workspace/domain/view_ext.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
@ -5,13 +8,12 @@ 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/icon_button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
class AddButton extends StatelessWidget {
final Function(ViewType) onSelected;
final Function(PluginBuilder) onSelected;
const AddButton({
Key? key,
required this.onSelected,
@ -35,21 +37,29 @@ class AddButton extends StatelessWidget {
}
class ActionList {
final Function(ViewType) onSelected;
final Function(PluginBuilder) onSelected;
final BuildContext anchorContext;
final String _identifier = 'DisclosureButtonActionList';
const ActionList({required this.anchorContext, required this.onSelected});
void show(BuildContext buildContext) {
final items = ViewType.values.where((element) => element.enable()).map((ty) {
final items = getIt<PluginSandbox>()
.builders
.where(
(builder) => !isDefaultPlugin(builder.pluginType),
)
.map(
(pluginBuilder) {
return CreateItem(
viewType: ty,
onSelected: (viewType) {
pluginBuilder: pluginBuilder,
onSelected: (builder) {
FlowyOverlay.of(buildContext).remove(_identifier);
onSelected(viewType);
});
}).toList();
onSelected(builder);
},
);
},
).toList();
ListOverlay.showWithAnchor(
buildContext,
@ -65,11 +75,11 @@ class ActionList {
}
class CreateItem extends StatelessWidget {
final ViewType viewType;
final Function(ViewType) onSelected;
final PluginBuilder pluginBuilder;
final Function(PluginBuilder) onSelected;
const CreateItem({
Key? key,
required this.viewType,
required this.pluginBuilder,
required this.onSelected,
}) : super(key: key);
@ -82,9 +92,9 @@ class CreateItem extends StatelessWidget {
config: config,
builder: (context, onHover) {
return GestureDetector(
onTap: () => onSelected(viewType),
onTap: () => onSelected(pluginBuilder),
child: FlowyText.medium(
viewType.displayName(),
pluginBuilder.pluginName,
color: theme.textColor,
fontSize: 12,
).padding(horizontal: 10, vertical: 6),

View File

@ -102,10 +102,12 @@ class MenuAppHeader extends StatelessWidget {
return Tooltip(
message: LocaleKeys.menuAppHeader_addPageTooltip.tr(),
child: AddButton(
onSelected: (viewType) {
context
.read<AppBloc>()
.add(AppEvent.createView(LocaleKeys.menuAppHeader_defaultNewPageName.tr(), "", viewType));
onSelected: (pluginBuilder) {
context.read<AppBloc>().add(AppEvent.createView(
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
"",
pluginBuilder.dataType,
));
},
).padding(right: MenuAppSizes.headerPadding),
);

View File

@ -1,6 +1,7 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/view/view_bloc.dart';
import 'package:app_flowy/workspace/domain/edit_action/view_edit.dart';
import 'package:app_flowy/workspace/domain/view_ext.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:easy_localization/easy_localization.dart';
@ -12,7 +13,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:app_flowy/workspace/domain/image.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
@ -55,7 +55,7 @@ class ViewSectionItem extends StatelessWidget {
Widget _render(BuildContext context, bool onHover, ViewState state, Color iconColor) {
List<Widget> children = [
SizedBox(width: 16, height: 16, child: state.view.thumbnail(iconColor: iconColor)),
SizedBox(width: 16, height: 16, child: state.view.renderThumbnail(iconColor: iconColor)),
const HSpace(2),
Expanded(child: FlowyText.regular(state.view.name, fontSize: 12, overflow: TextOverflow.clip)),
];

View File

@ -106,7 +106,7 @@ class ViewSectionNotifier with ChangeNotifier {
if (view != null) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
getIt<HomeStackManager>().setStack(view.stackContext());
getIt<HomeStackManager>().setPlugin(view.stackContext());
});
} else {
// do nothing

View File

@ -1,7 +1,8 @@
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/startup/tasks/load_plugin.dart';
import 'package:app_flowy/workspace/application/appearance.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/stack_page/trash/trash_page.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/menu.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
@ -22,7 +23,7 @@ class MenuTrash extends StatelessWidget {
child: InkWell(
onTap: () {
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = null;
getIt<HomeStackManager>().setStack(TrashStackContext());
getIt<HomeStackManager>().setPlugin(makePlugin(pluginType: DefaultPluginEnum.trash.type()));
},
child: _render(context),
),

View File

@ -20,11 +20,13 @@ class View extends $pb.GeneratedMessage {
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongToId')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
..e<ViewType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewType.RichText, valueOf: ViewType.valueOf, enumValues: ViewType.values)
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.RichText, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
..aInt64(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'version')
..aOM<RepeatedView>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongings', subBuilder: RepeatedView.create)
..aInt64(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'modifiedTime')
..aInt64(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createTime')
..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
..aOS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
..hasRequiredFields = false
;
@ -34,11 +36,13 @@ class View extends $pb.GeneratedMessage {
$core.String? belongToId,
$core.String? name,
$core.String? desc,
ViewType? viewType,
ViewDataType? dataType,
$fixnum.Int64? version,
RepeatedView? belongings,
$fixnum.Int64? modifiedTime,
$fixnum.Int64? createTime,
$core.String? extData,
$core.String? thumbnail,
}) {
final _result = create();
if (id != null) {
@ -53,8 +57,8 @@ class View extends $pb.GeneratedMessage {
if (desc != null) {
_result.desc = desc;
}
if (viewType != null) {
_result.viewType = viewType;
if (dataType != null) {
_result.dataType = dataType;
}
if (version != null) {
_result.version = version;
@ -68,6 +72,12 @@ class View extends $pb.GeneratedMessage {
if (createTime != null) {
_result.createTime = createTime;
}
if (extData != null) {
_result.extData = extData;
}
if (thumbnail != null) {
_result.thumbnail = thumbnail;
}
return _result;
}
factory View.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@ -128,13 +138,13 @@ class View extends $pb.GeneratedMessage {
void clearDesc() => clearField(4);
@$pb.TagNumber(5)
ViewType get viewType => $_getN(4);
ViewDataType get dataType => $_getN(4);
@$pb.TagNumber(5)
set viewType(ViewType v) { setField(5, v); }
set dataType(ViewDataType v) { setField(5, v); }
@$pb.TagNumber(5)
$core.bool hasViewType() => $_has(4);
$core.bool hasDataType() => $_has(4);
@$pb.TagNumber(5)
void clearViewType() => clearField(5);
void clearDataType() => clearField(5);
@$pb.TagNumber(6)
$fixnum.Int64 get version => $_getI64(5);
@ -173,6 +183,24 @@ class View extends $pb.GeneratedMessage {
$core.bool hasCreateTime() => $_has(8);
@$pb.TagNumber(9)
void clearCreateTime() => clearField(9);
@$pb.TagNumber(10)
$core.String get extData => $_getSZ(9);
@$pb.TagNumber(10)
set extData($core.String v) { $_setString(9, v); }
@$pb.TagNumber(10)
$core.bool hasExtData() => $_has(9);
@$pb.TagNumber(10)
void clearExtData() => clearField(10);
@$pb.TagNumber(11)
$core.String get thumbnail => $_getSZ(10);
@$pb.TagNumber(11)
set thumbnail($core.String v) { $_setString(10, v); }
@$pb.TagNumber(11)
$core.bool hasThumbnail() => $_has(10);
@$pb.TagNumber(11)
void clearThumbnail() => clearField(11);
}
class RepeatedView extends $pb.GeneratedMessage {
@ -232,8 +260,8 @@ class CreateViewPayload extends $pb.GeneratedMessage {
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
..e<ViewType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewType.RichText, valueOf: ViewType.valueOf, enumValues: ViewType.values)
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ext')
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.RichText, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
..hasRequiredFields = false
;
@ -243,8 +271,8 @@ class CreateViewPayload extends $pb.GeneratedMessage {
$core.String? name,
$core.String? desc,
$core.String? thumbnail,
ViewType? viewType,
$core.String? ext,
ViewDataType? dataType,
$core.String? extData,
}) {
final _result = create();
if (belongToId != null) {
@ -259,11 +287,11 @@ class CreateViewPayload extends $pb.GeneratedMessage {
if (thumbnail != null) {
_result.thumbnail = thumbnail;
}
if (viewType != null) {
_result.viewType = viewType;
if (dataType != null) {
_result.dataType = dataType;
}
if (ext != null) {
_result.ext = ext;
if (extData != null) {
_result.extData = extData;
}
return _result;
}
@ -328,22 +356,22 @@ class CreateViewPayload extends $pb.GeneratedMessage {
void clearThumbnail() => clearField(4);
@$pb.TagNumber(5)
ViewType get viewType => $_getN(4);
ViewDataType get dataType => $_getN(4);
@$pb.TagNumber(5)
set viewType(ViewType v) { setField(5, v); }
set dataType(ViewDataType v) { setField(5, v); }
@$pb.TagNumber(5)
$core.bool hasViewType() => $_has(4);
$core.bool hasDataType() => $_has(4);
@$pb.TagNumber(5)
void clearViewType() => clearField(5);
void clearDataType() => clearField(5);
@$pb.TagNumber(6)
$core.String get ext => $_getSZ(5);
$core.String get extData => $_getSZ(5);
@$pb.TagNumber(6)
set ext($core.String v) { $_setString(5, v); }
set extData($core.String v) { $_setString(5, v); }
@$pb.TagNumber(6)
$core.bool hasExt() => $_has(5);
$core.bool hasExtData() => $_has(5);
@$pb.TagNumber(6)
void clearExt() => clearField(6);
void clearExtData() => clearField(6);
}
class CreateViewParams extends $pb.GeneratedMessage {
@ -352,9 +380,10 @@ class CreateViewParams extends $pb.GeneratedMessage {
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
..e<ViewType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewType.RichText, valueOf: ViewType.valueOf, enumValues: ViewType.values)
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ext')
..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.RichText, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
..aOS(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
..hasRequiredFields = false
;
@ -364,9 +393,10 @@ class CreateViewParams extends $pb.GeneratedMessage {
$core.String? name,
$core.String? desc,
$core.String? thumbnail,
ViewType? viewType,
$core.String? ext,
ViewDataType? dataType,
$core.String? extData,
$core.String? viewId,
$core.String? data,
}) {
final _result = create();
if (belongToId != null) {
@ -381,15 +411,18 @@ class CreateViewParams extends $pb.GeneratedMessage {
if (thumbnail != null) {
_result.thumbnail = thumbnail;
}
if (viewType != null) {
_result.viewType = viewType;
if (dataType != null) {
_result.dataType = dataType;
}
if (ext != null) {
_result.ext = ext;
if (extData != null) {
_result.extData = extData;
}
if (viewId != null) {
_result.viewId = viewId;
}
if (data != null) {
_result.data = data;
}
return _result;
}
factory CreateViewParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@ -450,22 +483,22 @@ class CreateViewParams extends $pb.GeneratedMessage {
void clearThumbnail() => clearField(4);
@$pb.TagNumber(5)
ViewType get viewType => $_getN(4);
ViewDataType get dataType => $_getN(4);
@$pb.TagNumber(5)
set viewType(ViewType v) { setField(5, v); }
set dataType(ViewDataType v) { setField(5, v); }
@$pb.TagNumber(5)
$core.bool hasViewType() => $_has(4);
$core.bool hasDataType() => $_has(4);
@$pb.TagNumber(5)
void clearViewType() => clearField(5);
void clearDataType() => clearField(5);
@$pb.TagNumber(6)
$core.String get ext => $_getSZ(5);
$core.String get extData => $_getSZ(5);
@$pb.TagNumber(6)
set ext($core.String v) { $_setString(5, v); }
set extData($core.String v) { $_setString(5, v); }
@$pb.TagNumber(6)
$core.bool hasExt() => $_has(5);
$core.bool hasExtData() => $_has(5);
@$pb.TagNumber(6)
void clearExt() => clearField(6);
void clearExtData() => clearField(6);
@$pb.TagNumber(7)
$core.String get viewId => $_getSZ(6);
@ -475,6 +508,15 @@ class CreateViewParams extends $pb.GeneratedMessage {
$core.bool hasViewId() => $_has(6);
@$pb.TagNumber(7)
void clearViewId() => clearField(7);
@$pb.TagNumber(8)
$core.String get data => $_getSZ(7);
@$pb.TagNumber(8)
set data($core.String v) { $_setString(7, v); }
@$pb.TagNumber(8)
$core.bool hasData() => $_has(7);
@$pb.TagNumber(8)
void clearData() => clearField(8);
}
class ViewId extends $pb.GeneratedMessage {

View File

@ -9,18 +9,18 @@
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
class ViewType extends $pb.ProtobufEnum {
static const ViewType RichText = ViewType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
static const ViewType PlainText = ViewType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PlainText');
class ViewDataType extends $pb.ProtobufEnum {
static const ViewDataType RichText = ViewDataType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
static const ViewDataType PlainText = ViewDataType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PlainText');
static const $core.List<ViewType> values = <ViewType> [
static const $core.List<ViewDataType> values = <ViewDataType> [
RichText,
PlainText,
];
static final $core.Map<$core.int, ViewType> _byValue = $pb.ProtobufEnum.initByValue(values);
static ViewType? valueOf($core.int value) => _byValue[value];
static final $core.Map<$core.int, ViewDataType> _byValue = $pb.ProtobufEnum.initByValue(values);
static ViewDataType? valueOf($core.int value) => _byValue[value];
const ViewType._($core.int v, $core.String n) : super(v, n);
const ViewDataType._($core.int v, $core.String n) : super(v, n);
}

View File

@ -8,17 +8,17 @@
import 'dart:core' as $core;
import 'dart:convert' as $convert;
import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use viewTypeDescriptor instead')
const ViewType$json = const {
'1': 'ViewType',
@$core.Deprecated('Use viewDataTypeDescriptor instead')
const ViewDataType$json = const {
'1': 'ViewDataType',
'2': const [
const {'1': 'RichText', '2': 0},
const {'1': 'PlainText', '2': 1},
],
};
/// Descriptor for `ViewType`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List viewTypeDescriptor = $convert.base64Decode('CghWaWV3VHlwZRIMCghSaWNoVGV4dBAAEg0KCVBsYWluVGV4dBAB');
/// Descriptor for `ViewDataType`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List viewDataTypeDescriptor = $convert.base64Decode('CgxWaWV3RGF0YVR5cGUSDAoIUmljaFRleHQQABINCglQbGFpblRleHQQAQ==');
@$core.Deprecated('Use viewDescriptor instead')
const View$json = const {
'1': 'View',
@ -27,16 +27,18 @@ const View$json = const {
const {'1': 'belong_to_id', '3': 2, '4': 1, '5': 9, '10': 'belongToId'},
const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
const {'1': 'desc', '3': 4, '4': 1, '5': 9, '10': 'desc'},
const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewType', '10': 'viewType'},
const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
const {'1': 'version', '3': 6, '4': 1, '5': 3, '10': 'version'},
const {'1': 'belongings', '3': 7, '4': 1, '5': 11, '6': '.RepeatedView', '10': 'belongings'},
const {'1': 'modified_time', '3': 8, '4': 1, '5': 3, '10': 'modifiedTime'},
const {'1': 'create_time', '3': 9, '4': 1, '5': 3, '10': 'createTime'},
const {'1': 'ext_data', '3': 10, '4': 1, '5': 9, '10': 'extData'},
const {'1': 'thumbnail', '3': 11, '4': 1, '5': 9, '10': 'thumbnail'},
],
};
/// Descriptor for `View`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIgCgxiZWxvbmdfdG9faWQYAiABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgDIAEoCVIEbmFtZRISCgRkZXNjGAQgASgJUgRkZXNjEiYKCXZpZXdfdHlwZRgFIAEoDjIJLlZpZXdUeXBlUgh2aWV3VHlwZRIYCgd2ZXJzaW9uGAYgASgDUgd2ZXJzaW9uEi0KCmJlbG9uZ2luZ3MYByABKAsyDS5SZXBlYXRlZFZpZXdSCmJlbG9uZ2luZ3MSIwoNbW9kaWZpZWRfdGltZRgIIAEoA1IMbW9kaWZpZWRUaW1lEh8KC2NyZWF0ZV90aW1lGAkgASgDUgpjcmVhdGVUaW1l');
final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIgCgxiZWxvbmdfdG9faWQYAiABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgDIAEoCVIEbmFtZRISCgRkZXNjGAQgASgJUgRkZXNjEioKCWRhdGFfdHlwZRgFIAEoDjINLlZpZXdEYXRhVHlwZVIIZGF0YVR5cGUSGAoHdmVyc2lvbhgGIAEoA1IHdmVyc2lvbhItCgpiZWxvbmdpbmdzGAcgASgLMg0uUmVwZWF0ZWRWaWV3UgpiZWxvbmdpbmdzEiMKDW1vZGlmaWVkX3RpbWUYCCABKANSDG1vZGlmaWVkVGltZRIfCgtjcmVhdGVfdGltZRgJIAEoA1IKY3JlYXRlVGltZRIZCghleHRfZGF0YRgKIAEoCVIHZXh0RGF0YRIcCgl0aHVtYm5haWwYCyABKAlSCXRodW1ibmFpbA==');
@$core.Deprecated('Use repeatedViewDescriptor instead')
const RepeatedView$json = const {
'1': 'RepeatedView',
@ -55,8 +57,8 @@ const CreateViewPayload$json = const {
const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'thumbnail'},
const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewType', '10': 'viewType'},
const {'1': 'ext', '3': 6, '4': 1, '5': 9, '10': 'ext'},
const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
const {'1': 'ext_data', '3': 6, '4': 1, '5': 9, '10': 'extData'},
],
'8': const [
const {'1': 'one_of_thumbnail'},
@ -64,7 +66,7 @@ const CreateViewPayload$json = const {
};
/// Descriptor for `CreateViewPayload`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List createViewPayloadDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UGF5bG9hZBIgCgxiZWxvbmdfdG9faWQYASABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEh4KCXRodW1ibmFpbBgEIAEoCUgAUgl0aHVtYm5haWwSJgoJdmlld190eXBlGAUgASgOMgkuVmlld1R5cGVSCHZpZXdUeXBlEhAKA2V4dBgGIAEoCVIDZXh0QhIKEG9uZV9vZl90aHVtYm5haWw=');
final $typed_data.Uint8List createViewPayloadDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UGF5bG9hZBIgCgxiZWxvbmdfdG9faWQYASABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEh4KCXRodW1ibmFpbBgEIAEoCUgAUgl0aHVtYm5haWwSKgoJZGF0YV90eXBlGAUgASgOMg0uVmlld0RhdGFUeXBlUghkYXRhVHlwZRIZCghleHRfZGF0YRgGIAEoCVIHZXh0RGF0YUISChBvbmVfb2ZfdGh1bWJuYWls');
@$core.Deprecated('Use createViewParamsDescriptor instead')
const CreateViewParams$json = const {
'1': 'CreateViewParams',
@ -73,14 +75,15 @@ const CreateViewParams$json = const {
const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '10': 'thumbnail'},
const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewType', '10': 'viewType'},
const {'1': 'ext', '3': 6, '4': 1, '5': 9, '10': 'ext'},
const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
const {'1': 'ext_data', '3': 6, '4': 1, '5': 9, '10': 'extData'},
const {'1': 'view_id', '3': 7, '4': 1, '5': 9, '10': 'viewId'},
const {'1': 'data', '3': 8, '4': 1, '5': 9, '10': 'data'},
],
};
/// Descriptor for `CreateViewParams`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List createViewParamsDescriptor = $convert.base64Decode('ChBDcmVhdGVWaWV3UGFyYW1zEiAKDGJlbG9uZ190b19pZBgBIAEoCVIKYmVsb25nVG9JZBISCgRuYW1lGAIgASgJUgRuYW1lEhIKBGRlc2MYAyABKAlSBGRlc2MSHAoJdGh1bWJuYWlsGAQgASgJUgl0aHVtYm5haWwSJgoJdmlld190eXBlGAUgASgOMgkuVmlld1R5cGVSCHZpZXdUeXBlEhAKA2V4dBgGIAEoCVIDZXh0EhcKB3ZpZXdfaWQYByABKAlSBnZpZXdJZA==');
final $typed_data.Uint8List createViewParamsDescriptor = $convert.base64Decode('ChBDcmVhdGVWaWV3UGFyYW1zEiAKDGJlbG9uZ190b19pZBgBIAEoCVIKYmVsb25nVG9JZBISCgRuYW1lGAIgASgJUgRuYW1lEhIKBGRlc2MYAyABKAlSBGRlc2MSHAoJdGh1bWJuYWlsGAQgASgJUgl0aHVtYm5haWwSKgoJZGF0YV90eXBlGAUgASgOMg0uVmlld0RhdGFUeXBlUghkYXRhVHlwZRIZCghleHRfZGF0YRgGIAEoCVIHZXh0RGF0YRIXCgd2aWV3X2lkGAcgASgJUgZ2aWV3SWQSEgoEZGF0YRgIIAEoCVIEZGF0YQ==');
@$core.Deprecated('Use viewIdDescriptor instead')
const ViewId$json = const {
'1': 'ViewId',

View File

@ -8,7 +8,7 @@ use flowy_collaboration::{
errors::CollaborateError,
};
use flowy_error::{FlowyError, FlowyResult};
use flowy_sync::{DeltaMD5, RevisionCompact, RevisionManager, TransformDeltas};
use flowy_sync::{DeltaMD5, RevisionCompact, RevisionManager, RichTextTransformDeltas, TransformDeltas};
use futures::stream::StreamExt;
use lib_ot::{
core::{Interval, OperationTransformable},
@ -102,7 +102,7 @@ impl EditBlockQueue {
server_prime = Some(s_prime);
}
drop(read_guard);
Ok::<TransformDeltas<RichTextAttributes>, CollaborateError>(TransformDeltas {
Ok::<RichTextTransformDeltas, CollaborateError>(TransformDeltas {
client_prime,
server_prime,
})
@ -232,7 +232,7 @@ pub(crate) enum EditorCommand {
},
TransformDelta {
delta: RichTextDelta,
ret: Ret<TransformDeltas<RichTextAttributes>>,
ret: Ret<RichTextTransformDeltas>,
},
Insert {
index: usize,

View File

@ -10,7 +10,8 @@ use flowy_collaboration::{
use flowy_error::{internal_error, FlowyError};
use flowy_sync::*;
use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_ot::{core::Delta, rich_text::RichTextAttributes};
use lib_ot::rich_text::RichTextAttributes;
use lib_ot::rich_text::RichTextDelta;
use lib_ws::WSConnectState;
use std::{sync::Arc, time::Duration};
use tokio::sync::{
@ -31,12 +32,8 @@ pub(crate) async fn make_block_ws_manager(
) -> Arc<RevisionWebSocketManager> {
let ws_data_provider = Arc::new(WSDataProvider::new(&doc_id, Arc::new(rev_manager.clone())));
let resolver = Arc::new(BlockConflictResolver { edit_cmd_tx });
let conflict_controller = ConflictController::<RichTextAttributes>::new(
&user_id,
resolver,
Arc::new(ws_data_provider.clone()),
rev_manager,
);
let conflict_controller =
RichTextConflictController::new(&user_id, resolver, Arc::new(ws_data_provider.clone()), rev_manager);
let ws_data_stream = Arc::new(BlockRevisionWSDataStream::new(conflict_controller));
let ws_data_sink = Arc::new(BlockWSDataSink(ws_data_provider));
let ping_duration = Duration::from_millis(DOCUMENT_SYNC_INTERVAL_IN_MILLIS);
@ -66,11 +63,11 @@ fn listen_document_ws_state(_user_id: &str, _doc_id: &str, mut subscriber: broad
}
pub(crate) struct BlockRevisionWSDataStream {
conflict_controller: Arc<ConflictController<RichTextAttributes>>,
conflict_controller: Arc<RichTextConflictController>,
}
impl BlockRevisionWSDataStream {
pub fn new(conflict_controller: ConflictController<RichTextAttributes>) -> Self {
pub fn new(conflict_controller: RichTextConflictController) -> Self {
Self {
conflict_controller: Arc::new(conflict_controller),
}
@ -112,7 +109,7 @@ struct BlockConflictResolver {
}
impl ConflictResolver<RichTextAttributes> for BlockConflictResolver {
fn compose_delta(&self, delta: Delta<RichTextAttributes>) -> BoxResultFuture<DeltaMD5, FlowyError> {
fn compose_delta(&self, delta: RichTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let tx = self.edit_cmd_tx.clone();
Box::pin(async move {
let (ret, rx) = oneshot::channel();
@ -131,11 +128,11 @@ impl ConflictResolver<RichTextAttributes> for BlockConflictResolver {
fn transform_delta(
&self,
delta: Delta<RichTextAttributes>,
) -> BoxResultFuture<flowy_sync::TransformDeltas<RichTextAttributes>, FlowyError> {
delta: RichTextDelta,
) -> BoxResultFuture<flowy_sync::RichTextTransformDeltas, FlowyError> {
let tx = self.edit_cmd_tx.clone();
Box::pin(async move {
let (ret, rx) = oneshot::channel::<CollaborateResult<TransformDeltas<RichTextAttributes>>>();
let (ret, rx) = oneshot::channel::<CollaborateResult<RichTextTransformDeltas>>();
tx.send(EditorCommand::TransformDelta { delta, ret })
.await
.map_err(internal_error)?;
@ -146,7 +143,7 @@ impl ConflictResolver<RichTextAttributes> for BlockConflictResolver {
})
}
fn reset_delta(&self, delta: Delta<RichTextAttributes>) -> BoxResultFuture<DeltaMD5, FlowyError> {
fn reset_delta(&self, delta: RichTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let tx = self.edit_cmd_tx.clone();
Box::pin(async move {
let (ret, rx) = oneshot::channel();

View File

@ -12,7 +12,7 @@ use flowy_sync::{
RevisionWebSocket, RevisionWebSocketManager,
};
use lib_infra::future::FutureResult;
use lib_ot::core::PlainAttributes;
use lib_ot::core::PlainTextAttributes;
use lib_sqlite::ConnectionPool;
use parking_lot::RwLock;
use std::sync::Arc;
@ -144,7 +144,7 @@ impl RevisionCompact for FolderRevisionCompact {
let (base_rev_id, rev_id) = first_revision.pair_rev_id();
let md5 = last_revision.md5.clone();
let delta = make_delta_from_revisions::<PlainAttributes>(revisions)?;
let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
let delta_data = delta.to_bytes();
Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5))
}

View File

@ -1,7 +1,7 @@
use crate::{
entities::{
trash::{Trash, TrashType},
view::{RepeatedView, UpdateViewParams, View, ViewType},
view::{RepeatedView, UpdateViewParams, View, ViewDataType},
},
errors::FlowyError,
services::persistence::version_1::app_sql::AppTable,
@ -119,16 +119,16 @@ pub(crate) struct ViewTable {
pub modified_time: i64,
pub create_time: i64,
pub thumbnail: String,
pub view_type: ViewTableType,
pub view_type: SqlViewDataType,
pub version: i64,
pub is_trash: bool,
}
impl ViewTable {
pub fn new(view: View) -> Self {
let view_type = match view.view_type {
ViewType::RichText => ViewTableType::RichText,
ViewType::PlainText => ViewTableType::Text,
let data_type = match view.data_type {
ViewDataType::RichText => SqlViewDataType::RichText,
ViewDataType::PlainText => SqlViewDataType::PlainText,
};
ViewTable {
@ -138,9 +138,8 @@ impl ViewTable {
desc: view.desc,
modified_time: view.modified_time,
create_time: view.create_time,
// TODO: thumbnail
thumbnail: "".to_owned(),
view_type,
thumbnail: view.thumbnail,
view_type: data_type,
version: 0,
is_trash: false,
}
@ -149,9 +148,9 @@ impl ViewTable {
impl std::convert::From<ViewTable> for View {
fn from(table: ViewTable) -> Self {
let view_type = match table.view_type {
ViewTableType::RichText => ViewType::RichText,
ViewTableType::Text => ViewType::PlainText,
let data_type = match table.view_type {
SqlViewDataType::RichText => ViewDataType::RichText,
SqlViewDataType::PlainText => ViewDataType::PlainText,
};
View {
@ -159,11 +158,13 @@ impl std::convert::From<ViewTable> for View {
belong_to_id: table.belong_to_id,
name: table.name,
desc: table.desc,
view_type,
data_type,
belongings: RepeatedView::default(),
modified_time: table.modified_time,
version: table.version,
create_time: table.create_time,
ext_data: "".to_string(),
thumbnail: table.thumbnail,
}
}
}
@ -215,34 +216,34 @@ impl ViewChangeset {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
#[repr(i32)]
#[sql_type = "Integer"]
pub enum ViewTableType {
pub enum SqlViewDataType {
RichText = 0,
Text = 1,
PlainText = 1,
}
impl std::default::Default for ViewTableType {
impl std::default::Default for SqlViewDataType {
fn default() -> Self {
ViewTableType::RichText
SqlViewDataType::RichText
}
}
impl std::convert::From<i32> for ViewTableType {
impl std::convert::From<i32> for SqlViewDataType {
fn from(value: i32) -> Self {
match value {
0 => ViewTableType::RichText,
1 => ViewTableType::Text,
0 => SqlViewDataType::RichText,
1 => SqlViewDataType::PlainText,
o => {
log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
ViewTableType::Text
SqlViewDataType::PlainText
}
}
}
}
impl ViewTableType {
impl SqlViewDataType {
pub fn value(&self) -> i32 {
*self as i32
}
}
impl_sql_integer_expression!(ViewTableType);
impl_sql_integer_expression!(SqlViewDataType);

View File

@ -24,7 +24,7 @@ use crate::{
use flowy_database::kv::KV;
use flowy_document::BlockManager;
use flowy_folder_data_model::entities::share::{ExportData, ExportParams};
use flowy_folder_data_model::entities::view::ViewType;
use lib_infra::uuid_string;
const LATEST_VIEW_ID: &str = "latest_view_id";
@ -62,10 +62,10 @@ impl ViewController {
#[tracing::instrument(level = "trace", skip(self, params), fields(name = %params.name), err)]
pub(crate) async fn create_view_from_params(&self, params: CreateViewParams) -> Result<View, FlowyError> {
let view_data = if params.ext.is_empty() {
let view_data = if params.data.is_empty() {
initial_delta_string()
} else {
params.ext.clone()
params.data.clone()
};
let delta_data = Bytes::from(view_data);
@ -78,12 +78,11 @@ impl ViewController {
Ok(view)
}
#[tracing::instrument(level = "debug", skip(self, view_id, view_type, repeated_revision), err)]
#[tracing::instrument(level = "debug", skip(self, view_id, repeated_revision), err)]
pub(crate) async fn create_view(
&self,
view_id: &str,
repeated_revision: RepeatedRevision,
view_type: ViewType,
) -> Result<(), FlowyError> {
if repeated_revision.is_empty() {
return Err(FlowyError::internal().context("The content of the view should not be empty"));
@ -173,11 +172,12 @@ impl ViewController {
let duplicate_params = CreateViewParams {
belong_to_id: view.belong_to_id.clone(),
name: format!("{} (copy)", &view.name),
desc: view.desc.clone(),
thumbnail: "".to_owned(),
view_type: view.view_type.clone(),
ext: document_json,
desc: view.desc,
thumbnail: view.thumbnail,
data_type: view.data_type,
data: document_json,
view_id: uuid_string(),
ext_data: view.ext_data,
};
let _ = self.create_view_from_params(duplicate_params).await?;

View File

@ -10,7 +10,7 @@ use flowy_collaboration::{
use flowy_error::FlowyError;
use flowy_sync::*;
use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_ot::core::{Delta, OperationTransformable, PlainAttributes, PlainDelta};
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta};
use parking_lot::RwLock;
use std::{sync::Arc, time::Duration};
@ -23,8 +23,12 @@ pub(crate) async fn make_folder_ws_manager(
) -> Arc<RevisionWebSocketManager> {
let ws_data_provider = Arc::new(WSDataProvider::new(folder_id, Arc::new(rev_manager.clone())));
let resolver = Arc::new(FolderConflictResolver { folder_pad });
let conflict_controller =
ConflictController::<PlainAttributes>::new(user_id, resolver, Arc::new(ws_data_provider.clone()), rev_manager);
let conflict_controller = ConflictController::<PlainTextAttributes>::new(
user_id,
resolver,
Arc::new(ws_data_provider.clone()),
rev_manager,
);
let ws_data_stream = Arc::new(FolderRevisionWSDataStream::new(conflict_controller));
let ws_data_sink = Arc::new(FolderWSDataSink(ws_data_provider));
let ping_duration = Duration::from_millis(FOLDER_SYNC_INTERVAL_IN_MILLIS);
@ -50,8 +54,8 @@ struct FolderConflictResolver {
folder_pad: Arc<RwLock<FolderPad>>,
}
impl ConflictResolver<PlainAttributes> for FolderConflictResolver {
fn compose_delta(&self, delta: Delta<PlainAttributes>) -> BoxResultFuture<DeltaMD5, FlowyError> {
impl ConflictResolver<PlainTextAttributes> for FolderConflictResolver {
fn compose_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let folder_pad = self.folder_pad.clone();
Box::pin(async move {
let md5 = folder_pad.write().compose_remote_delta(delta)?;
@ -61,13 +65,13 @@ impl ConflictResolver<PlainAttributes> for FolderConflictResolver {
fn transform_delta(
&self,
delta: Delta<PlainAttributes>,
) -> BoxResultFuture<TransformDeltas<PlainAttributes>, FlowyError> {
delta: PlainTextDelta,
) -> BoxResultFuture<TransformDeltas<PlainTextAttributes>, FlowyError> {
let folder_pad = self.folder_pad.clone();
Box::pin(async move {
let read_guard = folder_pad.read();
let mut server_prime: Option<PlainDelta> = None;
let client_prime: PlainDelta;
let mut server_prime: Option<PlainTextDelta> = None;
let client_prime: PlainTextDelta;
if read_guard.is_empty() {
// Do nothing
client_prime = delta;
@ -84,7 +88,7 @@ impl ConflictResolver<PlainAttributes> for FolderConflictResolver {
})
}
fn reset_delta(&self, delta: Delta<PlainAttributes>) -> BoxResultFuture<DeltaMD5, FlowyError> {
fn reset_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let folder_pad = self.folder_pad.clone();
Box::pin(async move {
let md5 = folder_pad.write().reset_folder(delta)?;
@ -94,11 +98,11 @@ impl ConflictResolver<PlainAttributes> for FolderConflictResolver {
}
struct FolderRevisionWSDataStream {
conflict_controller: Arc<ConflictController<PlainAttributes>>,
conflict_controller: Arc<PlainTextConflictController>,
}
impl FolderRevisionWSDataStream {
pub fn new(conflict_controller: ConflictController<PlainAttributes>) -> Self {
pub fn new(conflict_controller: PlainTextConflictController) -> Self {
Self {
conflict_controller: Arc::new(conflict_controller),
}

View File

@ -5,7 +5,7 @@ use flowy_folder_data_model::entities::workspace::WorkspaceId;
use flowy_folder_data_model::entities::{
app::{App, AppId, CreateAppPayload, UpdateAppPayload},
trash::{RepeatedTrash, TrashId, TrashType},
view::{CreateViewPayload, UpdateViewPayload, View, ViewType},
view::{CreateViewPayload, UpdateViewPayload, View, ViewDataType},
workspace::{CreateWorkspacePayload, RepeatedWorkspace, Workspace},
};
use flowy_test::{event_builder::*, FlowySDKTest};
@ -109,13 +109,14 @@ pub async fn delete_app(sdk: &FlowySDKTest, app_id: &str) {
.await;
}
pub async fn create_view(sdk: &FlowySDKTest, app_id: &str, name: &str, desc: &str, view_type: ViewType) -> View {
pub async fn create_view(sdk: &FlowySDKTest, app_id: &str, name: &str, desc: &str, view_type: ViewDataType) -> View {
let request = CreateViewPayload {
belong_to_id: app_id.to_string(),
name: name.to_string(),
desc: desc.to_string(),
thumbnail: None,
view_type,
data_type: view_type,
ext_data: "".to_string(),
};
let view = FolderEventBuilder::new(sdk.clone())
.event(CreateView)

View File

@ -4,7 +4,7 @@ use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEdito
use flowy_folder_data_model::entities::{
app::{App, RepeatedApp},
trash::Trash,
view::{RepeatedView, View, ViewType},
view::{RepeatedView, View, ViewDataType},
workspace::Workspace,
};
use flowy_sync::REVISION_WRITE_INTERVAL_IN_MILLIS;
@ -68,7 +68,7 @@ impl FolderTest {
let _ = sdk.init_user().await;
let mut workspace = create_workspace(&sdk, "FolderWorkspace", "Folder test workspace").await;
let mut app = create_app(&sdk, &workspace.id, "Folder App", "Folder test app").await;
let view = create_view(&sdk, &app.id, "Folder View", "Folder test view", ViewType::RichText).await;
let view = create_view(&sdk, &app.id, "Folder View", "Folder test view", ViewDataType::RichText).await;
app.belongings = RepeatedView {
items: vec![view.clone()],
};
@ -146,7 +146,7 @@ impl FolderTest {
}
FolderScript::CreateView { name, desc } => {
let view = create_view(sdk, &self.app.id, &name, &desc, ViewType::RichText).await;
let view = create_view(sdk, &self.app.id, &name, &desc, ViewDataType::RichText).await;
self.view = view;
}
FolderScript::AssertView(view) => {

View File

@ -300,11 +300,13 @@ impl FolderCouldServiceV1 for LocalServer {
belong_to_id: params.belong_to_id,
name: params.name,
desc: params.desc,
view_type: params.view_type,
data_type: params.data_type,
version: 0,
belongings: RepeatedView::default(),
modified_time: time,
create_time: time,
ext_data: params.ext_data,
thumbnail: params.thumbnail,
};
FutureResult::new(async { Ok(view) })
}

View File

@ -9,7 +9,8 @@ use flowy_collaboration::{
};
use flowy_error::{FlowyError, FlowyResult};
use lib_infra::future::BoxResultFuture;
use lib_ot::core::{Attributes, Delta};
use lib_ot::core::{Attributes, Delta, PlainTextAttributes};
use lib_ot::rich_text::RichTextAttributes;
use serde::de::DeserializeOwned;
use std::{convert::TryFrom, sync::Arc};
@ -29,6 +30,9 @@ pub trait ConflictRevisionSink: Send + Sync + 'static {
fn ack(&self, rev_id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError>;
}
pub type RichTextConflictController = ConflictController<RichTextAttributes>;
pub type PlainTextConflictController = ConflictController<PlainTextAttributes>;
pub struct ConflictController<T>
where
T: Attributes + Send + Sync,
@ -171,6 +175,8 @@ where
}
}
pub type RichTextTransformDeltas = TransformDeltas<RichTextAttributes>;
pub struct TransformDeltas<T>
where
T: Attributes,

View File

@ -88,7 +88,8 @@ async fn create_view(sdk: &FlowySDKTest, app_id: &str) -> View {
name: "View A".to_string(),
desc: "".to_string(),
thumbnail: Some("http://1.png".to_string()),
view_type: ViewType::RichText,
data_type: ViewDataType::RichText,
ext_data: "".to_string(),
};
let view = FolderEventBuilder::new(sdk.clone())

View File

@ -6,7 +6,7 @@ use crate::{
errors::{CollaborateError, CollaborateResult},
};
use flowy_folder_data_model::entities::{trash::Trash, workspace::Workspace};
use lib_ot::core::{PlainAttributes, PlainDelta, PlainDeltaBuilder};
use lib_ot::core::{PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
@ -34,7 +34,7 @@ impl FolderPadBuilder {
self
}
pub(crate) fn build_with_delta(self, mut delta: PlainDelta) -> CollaborateResult<FolderPad> {
pub(crate) fn build_with_delta(self, mut delta: PlainTextDelta) -> CollaborateResult<FolderPad> {
if delta.is_empty() {
delta = default_folder_delta();
}
@ -47,7 +47,7 @@ impl FolderPadBuilder {
}
pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
let folder_delta: FolderDelta = make_delta_from_revisions::<PlainAttributes>(revisions)?;
let folder_delta: FolderDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
self.build_with_delta(folder_delta)
}
@ -57,7 +57,7 @@ impl FolderPadBuilder {
Ok(FolderPad {
workspaces: self.workspaces,
trash: self.trash,
root: PlainDeltaBuilder::new().insert(&json).build(),
root: PlainTextDeltaBuilder::new().insert(&json).build(),
})
}
}

View File

@ -8,7 +8,7 @@ use crate::{
};
use dissimilar::*;
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
use lib_ot::core::{Delta, FlowyStr, OperationTransformable, PlainAttributes, PlainDeltaBuilder};
use lib_ot::core::{FlowyStr, OperationTransformable, PlainTextDelta, PlainTextDeltaBuilder};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
@ -21,7 +21,7 @@ pub struct FolderPad {
}
pub fn default_folder_delta() -> FolderDelta {
PlainDeltaBuilder::new()
PlainTextDeltaBuilder::new()
.insert(r#"{"workspaces":[],"trash":[]}"#)
.build()
}
@ -385,9 +385,9 @@ impl FolderPad {
}
}
fn cal_diff(old: String, new: String) -> Delta<PlainAttributes> {
fn cal_diff(old: String, new: String) -> PlainTextDelta {
let chunks = dissimilar::diff(&old, &new);
let mut delta_builder = PlainDeltaBuilder::new();
let mut delta_builder = PlainTextDeltaBuilder::new();
for chunk in &chunks {
match chunk {
Chunk::Equal(s) => {
@ -410,7 +410,7 @@ mod tests {
use crate::{client_folder::folder_pad::FolderPad, entities::folder_info::FolderDelta};
use chrono::Utc;
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
use lib_ot::core::{OperationTransformable, PlainTextDelta, PlainTextDeltaBuilder};
#[test]
fn folder_add_workspace() {
@ -725,7 +725,7 @@ mod tests {
fn test_folder() -> (FolderPad, FolderDelta, Workspace) {
let mut folder = FolderPad::default();
let folder_json = serde_json::to_string(&folder).unwrap();
let mut delta = PlainDeltaBuilder::new().insert(&folder_json).build();
let mut delta = PlainTextDeltaBuilder::new().insert(&folder_json).build();
let mut workspace = Workspace::default();
workspace.name = "😁 my first workspace".to_owned();
@ -767,7 +767,7 @@ mod tests {
fn test_trash() -> (FolderPad, FolderDelta, Trash) {
let mut folder = FolderPad::default();
let folder_json = serde_json::to_string(&folder).unwrap();
let mut delta = PlainDeltaBuilder::new().insert(&folder_json).build();
let mut delta = PlainTextDeltaBuilder::new().insert(&folder_json).build();
let mut trash = Trash::default();
trash.name = "🚽 my first trash".to_owned();
@ -780,7 +780,7 @@ mod tests {
(folder, delta, trash)
}
fn make_folder_from_delta(mut initial_delta: FolderDelta, deltas: Vec<PlainDelta>) -> FolderPad {
fn make_folder_from_delta(mut initial_delta: FolderDelta, deltas: Vec<PlainTextDelta>) -> FolderPad {
for delta in deltas {
initial_delta = initial_delta.compose(&delta).unwrap();
}

View File

@ -1,7 +1,7 @@
use flowy_derive::ProtoBuf;
use lib_ot::core::PlainDelta;
use lib_ot::core::PlainTextDelta;
pub type FolderDelta = PlainDelta;
pub type FolderDelta = PlainTextDelta;
#[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)]
pub struct FolderInfo {

View File

@ -12,7 +12,7 @@ use crate::{
use async_stream::stream;
use futures::stream::StreamExt;
use lib_infra::future::BoxResultFuture;
use lib_ot::core::PlainAttributes;
use lib_ot::core::PlainTextAttributes;
use std::{collections::HashMap, fmt::Debug, sync::Arc};
use tokio::{
sync::{mpsc, oneshot, RwLock},
@ -187,7 +187,7 @@ impl ServerFolderManager {
}
}
type FolderRevisionSynchronizer = RevisionSynchronizer<PlainAttributes>;
type FolderRevisionSynchronizer = RevisionSynchronizer<PlainTextAttributes>;
struct OpenFolderHandler {
folder_id: String,

View File

@ -1,5 +1,5 @@
use crate::{entities::folder_info::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
use lib_ot::core::{Delta, OperationTransformable, PlainAttributes};
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta};
pub struct ServerFolder {
folder_id: String,
@ -15,21 +15,18 @@ impl ServerFolder {
}
}
impl RevisionSyncObject<PlainAttributes> for ServerFolder {
impl RevisionSyncObject<PlainTextAttributes> for ServerFolder {
fn id(&self) -> &str {
&self.folder_id
}
fn compose(&mut self, other: &Delta<PlainAttributes>) -> Result<(), CollaborateError> {
fn compose(&mut self, other: &PlainTextDelta) -> Result<(), CollaborateError> {
let new_delta = self.delta.compose(other)?;
self.delta = new_delta;
Ok(())
}
fn transform(
&self,
other: &Delta<PlainAttributes>,
) -> Result<(Delta<PlainAttributes>, Delta<PlainAttributes>), CollaborateError> {
fn transform(&self, other: &PlainTextDelta) -> Result<(PlainTextDelta, PlainTextDelta), CollaborateError> {
let value = self.delta.transform(other)?;
Ok(value)
}
@ -38,7 +35,7 @@ impl RevisionSyncObject<PlainAttributes> for ServerFolder {
self.delta.to_json()
}
fn set_delta(&mut self, new_delta: Delta<PlainAttributes>) {
fn set_delta(&mut self, new_delta: PlainTextDelta) {
self.delta = new_delta;
}
}

View File

@ -28,7 +28,8 @@ pub struct View {
pub desc: String,
#[pb(index = 5)]
pub view_type: ViewType,
#[serde(default)]
pub data_type: ViewDataType,
#[pb(index = 6)]
pub version: i64,
@ -41,6 +42,12 @@ pub struct View {
#[pb(index = 9)]
pub create_time: i64,
#[pb(index = 10)]
pub ext_data: String,
#[pb(index = 11)]
pub thumbnail: String,
}
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone, Serialize, Deserialize)]
@ -65,25 +72,25 @@ impl std::convert::From<View> for Trash {
}
#[derive(Eq, PartialEq, Debug, ProtoBuf_Enum, Clone, Serialize)]
pub enum ViewType {
pub enum ViewDataType {
RichText = 0,
PlainText = 1,
}
impl std::default::Default for ViewType {
impl std::default::Default for ViewDataType {
fn default() -> Self {
ViewType::PlainText
ViewDataType::PlainText
}
}
impl std::convert::From<i32> for ViewType {
impl std::convert::From<i32> for ViewDataType {
fn from(val: i32) -> Self {
match val {
0 => ViewType::RichText,
1 => ViewType::PlainText,
0 => ViewDataType::RichText,
1 => ViewDataType::PlainText,
_ => {
log::error!("Invalid view type: {}", val);
ViewType::PlainText
ViewDataType::PlainText
}
}
}
@ -104,10 +111,10 @@ pub struct CreateViewPayload {
pub thumbnail: Option<String>,
#[pb(index = 5)]
pub view_type: ViewType,
pub data_type: ViewDataType,
#[pb(index = 6)]
pub ext: String,
pub ext_data: String,
}
#[derive(Default, ProtoBuf, Debug, Clone)]
@ -125,35 +132,16 @@ pub struct CreateViewParams {
pub thumbnail: String,
#[pb(index = 5)]
pub view_type: ViewType,
pub data_type: ViewDataType,
#[pb(index = 6)]
pub ext: String,
pub ext_data: String,
#[pb(index = 7)]
pub view_id: String,
}
impl CreateViewParams {
pub fn new(
belong_to_id: String,
name: String,
desc: String,
view_type: ViewType,
thumbnail: String,
ext: String,
view_id: String,
) -> Self {
Self {
belong_to_id,
name,
desc,
thumbnail,
view_type,
ext,
view_id,
}
}
#[pb(index = 8)]
pub data: String,
}
impl TryInto<CreateViewParams> for CreateViewPayload {
@ -163,21 +151,22 @@ impl TryInto<CreateViewParams> for CreateViewPayload {
let name = ViewName::parse(self.name)?.0;
let belong_to_id = AppIdentify::parse(self.belong_to_id)?.0;
let view_id = uuid::Uuid::new_v4().to_string();
let ext = ViewExtensionData::parse(self.ext)?.0;
let ext_data = ViewExtensionData::parse(self.ext_data)?.0;
let thumbnail = match self.thumbnail {
None => "".to_string(),
Some(thumbnail) => ViewThumbnail::parse(thumbnail)?.0,
};
Ok(CreateViewParams::new(
Ok(CreateViewParams {
belong_to_id,
name,
self.desc,
self.view_type,
desc: self.desc,
data_type: self.data_type,
thumbnail,
ext,
ext_data,
view_id,
))
data: "".to_string(),
})
}
}
@ -280,7 +269,7 @@ impl TryInto<UpdateViewParams> for UpdateViewPayload {
}
}
impl<'de> Deserialize<'de> for ViewType {
impl<'de> Deserialize<'de> for ViewDataType {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
@ -288,7 +277,7 @@ impl<'de> Deserialize<'de> for ViewType {
struct ViewTypeVisitor();
impl<'de> Visitor<'de> for ViewTypeVisitor {
type Value = ViewType;
type Value = ViewDataType;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Plugin, RichText")
}
@ -301,10 +290,10 @@ impl<'de> Deserialize<'de> for ViewType {
match s {
"Doc" | "RichText" => {
// Rename ViewType::Doc to ViewType::RichText, So we need to migrate the ViewType manually.
view_type = ViewType::RichText;
view_type = ViewDataType::RichText;
}
"Plugin" => {
view_type = ViewType::PlainText;
view_type = ViewDataType::PlainText;
}
unknown => {
return Err(de::Error::invalid_value(Unexpected::Str(unknown), &self));

View File

@ -30,11 +30,13 @@ pub struct View {
pub belong_to_id: ::std::string::String,
pub name: ::std::string::String,
pub desc: ::std::string::String,
pub view_type: ViewType,
pub data_type: ViewDataType,
pub version: i64,
pub belongings: ::protobuf::SingularPtrField<RepeatedView>,
pub modified_time: i64,
pub create_time: i64,
pub ext_data: ::std::string::String,
pub thumbnail: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
@ -155,19 +157,19 @@ impl View {
::std::mem::replace(&mut self.desc, ::std::string::String::new())
}
// .ViewType view_type = 5;
// .ViewDataType data_type = 5;
pub fn get_view_type(&self) -> ViewType {
self.view_type
pub fn get_data_type(&self) -> ViewDataType {
self.data_type
}
pub fn clear_view_type(&mut self) {
self.view_type = ViewType::RichText;
pub fn clear_data_type(&mut self) {
self.data_type = ViewDataType::RichText;
}
// Param is passed by value, moved
pub fn set_view_type(&mut self, v: ViewType) {
self.view_type = v;
pub fn set_data_type(&mut self, v: ViewDataType) {
self.data_type = v;
}
// int64 version = 6;
@ -247,6 +249,58 @@ impl View {
pub fn set_create_time(&mut self, v: i64) {
self.create_time = v;
}
// string ext_data = 10;
pub fn get_ext_data(&self) -> &str {
&self.ext_data
}
pub fn clear_ext_data(&mut self) {
self.ext_data.clear();
}
// Param is passed by value, moved
pub fn set_ext_data(&mut self, v: ::std::string::String) {
self.ext_data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_ext_data(&mut self) -> &mut ::std::string::String {
&mut self.ext_data
}
// Take field
pub fn take_ext_data(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.ext_data, ::std::string::String::new())
}
// string thumbnail = 11;
pub fn get_thumbnail(&self) -> &str {
&self.thumbnail
}
pub fn clear_thumbnail(&mut self) {
self.thumbnail.clear();
}
// Param is passed by value, moved
pub fn set_thumbnail(&mut self, v: ::std::string::String) {
self.thumbnail = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_thumbnail(&mut self) -> &mut ::std::string::String {
&mut self.thumbnail
}
// Take field
pub fn take_thumbnail(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.thumbnail, ::std::string::String::new())
}
}
impl ::protobuf::Message for View {
@ -276,7 +330,7 @@ impl ::protobuf::Message for View {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.desc)?;
},
5 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.view_type, 5, &mut self.unknown_fields)?
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.data_type, 5, &mut self.unknown_fields)?
},
6 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
@ -302,6 +356,12 @@ impl ::protobuf::Message for View {
let tmp = is.read_int64()?;
self.create_time = tmp;
},
10 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ext_data)?;
},
11 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.thumbnail)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
@ -326,8 +386,8 @@ impl ::protobuf::Message for View {
if !self.desc.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.desc);
}
if self.view_type != ViewType::RichText {
my_size += ::protobuf::rt::enum_size(5, self.view_type);
if self.data_type != ViewDataType::RichText {
my_size += ::protobuf::rt::enum_size(5, self.data_type);
}
if self.version != 0 {
my_size += ::protobuf::rt::value_size(6, self.version, ::protobuf::wire_format::WireTypeVarint);
@ -342,6 +402,12 @@ impl ::protobuf::Message for View {
if self.create_time != 0 {
my_size += ::protobuf::rt::value_size(9, self.create_time, ::protobuf::wire_format::WireTypeVarint);
}
if !self.ext_data.is_empty() {
my_size += ::protobuf::rt::string_size(10, &self.ext_data);
}
if !self.thumbnail.is_empty() {
my_size += ::protobuf::rt::string_size(11, &self.thumbnail);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
@ -360,8 +426,8 @@ impl ::protobuf::Message for View {
if !self.desc.is_empty() {
os.write_string(4, &self.desc)?;
}
if self.view_type != ViewType::RichText {
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
if self.data_type != ViewDataType::RichText {
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.data_type))?;
}
if self.version != 0 {
os.write_int64(6, self.version)?;
@ -377,6 +443,12 @@ impl ::protobuf::Message for View {
if self.create_time != 0 {
os.write_int64(9, self.create_time)?;
}
if !self.ext_data.is_empty() {
os.write_string(10, &self.ext_data)?;
}
if !self.thumbnail.is_empty() {
os.write_string(11, &self.thumbnail)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
@ -435,10 +507,10 @@ impl ::protobuf::Message for View {
|m: &View| { &m.desc },
|m: &mut View| { &mut m.desc },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewType>>(
"view_type",
|m: &View| { &m.view_type },
|m: &mut View| { &mut m.view_type },
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewDataType>>(
"data_type",
|m: &View| { &m.data_type },
|m: &mut View| { &mut m.data_type },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"version",
@ -460,6 +532,16 @@ impl ::protobuf::Message for View {
|m: &View| { &m.create_time },
|m: &mut View| { &mut m.create_time },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"ext_data",
|m: &View| { &m.ext_data },
|m: &mut View| { &mut m.ext_data },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"thumbnail",
|m: &View| { &m.thumbnail },
|m: &mut View| { &mut m.thumbnail },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<View>(
"View",
fields,
@ -480,11 +562,13 @@ impl ::protobuf::Clear for View {
self.belong_to_id.clear();
self.name.clear();
self.desc.clear();
self.view_type = ViewType::RichText;
self.data_type = ViewDataType::RichText;
self.version = 0;
self.belongings.clear();
self.modified_time = 0;
self.create_time = 0;
self.ext_data.clear();
self.thumbnail.clear();
self.unknown_fields.clear();
}
}
@ -673,8 +757,8 @@ pub struct CreateViewPayload {
pub belong_to_id: ::std::string::String,
pub name: ::std::string::String,
pub desc: ::std::string::String,
pub view_type: ViewType,
pub ext: ::std::string::String,
pub data_type: ViewDataType,
pub ext_data: ::std::string::String,
// message oneof groups
pub one_of_thumbnail: ::std::option::Option<CreateViewPayload_oneof_one_of_thumbnail>,
// special fields
@ -825,45 +909,45 @@ impl CreateViewPayload {
}
}
// .ViewType view_type = 5;
// .ViewDataType data_type = 5;
pub fn get_view_type(&self) -> ViewType {
self.view_type
pub fn get_data_type(&self) -> ViewDataType {
self.data_type
}
pub fn clear_view_type(&mut self) {
self.view_type = ViewType::RichText;
pub fn clear_data_type(&mut self) {
self.data_type = ViewDataType::RichText;
}
// Param is passed by value, moved
pub fn set_view_type(&mut self, v: ViewType) {
self.view_type = v;
pub fn set_data_type(&mut self, v: ViewDataType) {
self.data_type = v;
}
// string ext = 6;
// string ext_data = 6;
pub fn get_ext(&self) -> &str {
&self.ext
pub fn get_ext_data(&self) -> &str {
&self.ext_data
}
pub fn clear_ext(&mut self) {
self.ext.clear();
pub fn clear_ext_data(&mut self) {
self.ext_data.clear();
}
// Param is passed by value, moved
pub fn set_ext(&mut self, v: ::std::string::String) {
self.ext = v;
pub fn set_ext_data(&mut self, v: ::std::string::String) {
self.ext_data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_ext(&mut self) -> &mut ::std::string::String {
&mut self.ext
pub fn mut_ext_data(&mut self) -> &mut ::std::string::String {
&mut self.ext_data
}
// Take field
pub fn take_ext(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.ext, ::std::string::String::new())
pub fn take_ext_data(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.ext_data, ::std::string::String::new())
}
}
@ -892,10 +976,10 @@ impl ::protobuf::Message for CreateViewPayload {
self.one_of_thumbnail = ::std::option::Option::Some(CreateViewPayload_oneof_one_of_thumbnail::thumbnail(is.read_string()?));
},
5 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.view_type, 5, &mut self.unknown_fields)?
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.data_type, 5, &mut self.unknown_fields)?
},
6 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ext)?;
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ext_data)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@ -918,11 +1002,11 @@ impl ::protobuf::Message for CreateViewPayload {
if !self.desc.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.desc);
}
if self.view_type != ViewType::RichText {
my_size += ::protobuf::rt::enum_size(5, self.view_type);
if self.data_type != ViewDataType::RichText {
my_size += ::protobuf::rt::enum_size(5, self.data_type);
}
if !self.ext.is_empty() {
my_size += ::protobuf::rt::string_size(6, &self.ext);
if !self.ext_data.is_empty() {
my_size += ::protobuf::rt::string_size(6, &self.ext_data);
}
if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
match v {
@ -946,11 +1030,11 @@ impl ::protobuf::Message for CreateViewPayload {
if !self.desc.is_empty() {
os.write_string(3, &self.desc)?;
}
if self.view_type != ViewType::RichText {
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
if self.data_type != ViewDataType::RichText {
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.data_type))?;
}
if !self.ext.is_empty() {
os.write_string(6, &self.ext)?;
if !self.ext_data.is_empty() {
os.write_string(6, &self.ext_data)?;
}
if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
match v {
@ -1017,15 +1101,15 @@ impl ::protobuf::Message for CreateViewPayload {
CreateViewPayload::has_thumbnail,
CreateViewPayload::get_thumbnail,
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewType>>(
"view_type",
|m: &CreateViewPayload| { &m.view_type },
|m: &mut CreateViewPayload| { &mut m.view_type },
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewDataType>>(
"data_type",
|m: &CreateViewPayload| { &m.data_type },
|m: &mut CreateViewPayload| { &mut m.data_type },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"ext",
|m: &CreateViewPayload| { &m.ext },
|m: &mut CreateViewPayload| { &mut m.ext },
"ext_data",
|m: &CreateViewPayload| { &m.ext_data },
|m: &mut CreateViewPayload| { &mut m.ext_data },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateViewPayload>(
"CreateViewPayload",
@ -1047,8 +1131,8 @@ impl ::protobuf::Clear for CreateViewPayload {
self.name.clear();
self.desc.clear();
self.one_of_thumbnail = ::std::option::Option::None;
self.view_type = ViewType::RichText;
self.ext.clear();
self.data_type = ViewDataType::RichText;
self.ext_data.clear();
self.unknown_fields.clear();
}
}
@ -1072,9 +1156,10 @@ pub struct CreateViewParams {
pub name: ::std::string::String,
pub desc: ::std::string::String,
pub thumbnail: ::std::string::String,
pub view_type: ViewType,
pub ext: ::std::string::String,
pub data_type: ViewDataType,
pub ext_data: ::std::string::String,
pub view_id: ::std::string::String,
pub data: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
@ -1195,45 +1280,45 @@ impl CreateViewParams {
::std::mem::replace(&mut self.thumbnail, ::std::string::String::new())
}
// .ViewType view_type = 5;
// .ViewDataType data_type = 5;
pub fn get_view_type(&self) -> ViewType {
self.view_type
pub fn get_data_type(&self) -> ViewDataType {
self.data_type
}
pub fn clear_view_type(&mut self) {
self.view_type = ViewType::RichText;
pub fn clear_data_type(&mut self) {
self.data_type = ViewDataType::RichText;
}
// Param is passed by value, moved
pub fn set_view_type(&mut self, v: ViewType) {
self.view_type = v;
pub fn set_data_type(&mut self, v: ViewDataType) {
self.data_type = v;
}
// string ext = 6;
// string ext_data = 6;
pub fn get_ext(&self) -> &str {
&self.ext
pub fn get_ext_data(&self) -> &str {
&self.ext_data
}
pub fn clear_ext(&mut self) {
self.ext.clear();
pub fn clear_ext_data(&mut self) {
self.ext_data.clear();
}
// Param is passed by value, moved
pub fn set_ext(&mut self, v: ::std::string::String) {
self.ext = v;
pub fn set_ext_data(&mut self, v: ::std::string::String) {
self.ext_data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_ext(&mut self) -> &mut ::std::string::String {
&mut self.ext
pub fn mut_ext_data(&mut self) -> &mut ::std::string::String {
&mut self.ext_data
}
// Take field
pub fn take_ext(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.ext, ::std::string::String::new())
pub fn take_ext_data(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.ext_data, ::std::string::String::new())
}
// string view_id = 7;
@ -1261,6 +1346,32 @@ impl CreateViewParams {
pub fn take_view_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.view_id, ::std::string::String::new())
}
// string data = 8;
pub fn get_data(&self) -> &str {
&self.data
}
pub fn clear_data(&mut self) {
self.data.clear();
}
// Param is passed by value, moved
pub fn set_data(&mut self, v: ::std::string::String) {
self.data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_data(&mut self) -> &mut ::std::string::String {
&mut self.data
}
// Take field
pub fn take_data(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.data, ::std::string::String::new())
}
}
impl ::protobuf::Message for CreateViewParams {
@ -1285,14 +1396,17 @@ impl ::protobuf::Message for CreateViewParams {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.thumbnail)?;
},
5 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.view_type, 5, &mut self.unknown_fields)?
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.data_type, 5, &mut self.unknown_fields)?
},
6 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ext)?;
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ext_data)?;
},
7 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.view_id)?;
},
8 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
@ -1317,15 +1431,18 @@ impl ::protobuf::Message for CreateViewParams {
if !self.thumbnail.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.thumbnail);
}
if self.view_type != ViewType::RichText {
my_size += ::protobuf::rt::enum_size(5, self.view_type);
if self.data_type != ViewDataType::RichText {
my_size += ::protobuf::rt::enum_size(5, self.data_type);
}
if !self.ext.is_empty() {
my_size += ::protobuf::rt::string_size(6, &self.ext);
if !self.ext_data.is_empty() {
my_size += ::protobuf::rt::string_size(6, &self.ext_data);
}
if !self.view_id.is_empty() {
my_size += ::protobuf::rt::string_size(7, &self.view_id);
}
if !self.data.is_empty() {
my_size += ::protobuf::rt::string_size(8, &self.data);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
@ -1344,15 +1461,18 @@ impl ::protobuf::Message for CreateViewParams {
if !self.thumbnail.is_empty() {
os.write_string(4, &self.thumbnail)?;
}
if self.view_type != ViewType::RichText {
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
if self.data_type != ViewDataType::RichText {
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.data_type))?;
}
if !self.ext.is_empty() {
os.write_string(6, &self.ext)?;
if !self.ext_data.is_empty() {
os.write_string(6, &self.ext_data)?;
}
if !self.view_id.is_empty() {
os.write_string(7, &self.view_id)?;
}
if !self.data.is_empty() {
os.write_string(8, &self.data)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
@ -1411,21 +1531,26 @@ impl ::protobuf::Message for CreateViewParams {
|m: &CreateViewParams| { &m.thumbnail },
|m: &mut CreateViewParams| { &mut m.thumbnail },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewType>>(
"view_type",
|m: &CreateViewParams| { &m.view_type },
|m: &mut CreateViewParams| { &mut m.view_type },
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewDataType>>(
"data_type",
|m: &CreateViewParams| { &m.data_type },
|m: &mut CreateViewParams| { &mut m.data_type },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"ext",
|m: &CreateViewParams| { &m.ext },
|m: &mut CreateViewParams| { &mut m.ext },
"ext_data",
|m: &CreateViewParams| { &m.ext_data },
|m: &mut CreateViewParams| { &mut m.ext_data },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"view_id",
|m: &CreateViewParams| { &m.view_id },
|m: &mut CreateViewParams| { &mut m.view_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"data",
|m: &CreateViewParams| { &m.data },
|m: &mut CreateViewParams| { &mut m.data },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateViewParams>(
"CreateViewParams",
fields,
@ -1446,9 +1571,10 @@ impl ::protobuf::Clear for CreateViewParams {
self.name.clear();
self.desc.clear();
self.thumbnail.clear();
self.view_type = ViewType::RichText;
self.ext.clear();
self.data_type = ViewDataType::RichText;
self.ext_data.clear();
self.view_id.clear();
self.data.clear();
self.unknown_fields.clear();
}
}
@ -2589,28 +2715,28 @@ impl ::protobuf::reflect::ProtobufValue for UpdateViewParams {
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum ViewType {
pub enum ViewDataType {
RichText = 0,
PlainText = 1,
}
impl ::protobuf::ProtobufEnum for ViewType {
impl ::protobuf::ProtobufEnum for ViewDataType {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<ViewType> {
fn from_i32(value: i32) -> ::std::option::Option<ViewDataType> {
match value {
0 => ::std::option::Option::Some(ViewType::RichText),
1 => ::std::option::Option::Some(ViewType::PlainText),
0 => ::std::option::Option::Some(ViewDataType::RichText),
1 => ::std::option::Option::Some(ViewDataType::PlainText),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [ViewType] = &[
ViewType::RichText,
ViewType::PlainText,
static values: &'static [ViewDataType] = &[
ViewDataType::RichText,
ViewDataType::PlainText,
];
values
}
@ -2618,58 +2744,61 @@ impl ::protobuf::ProtobufEnum for ViewType {
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<ViewType>("ViewType", file_descriptor_proto())
::protobuf::reflect::EnumDescriptor::new_pb_name::<ViewDataType>("ViewDataType", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for ViewType {
impl ::std::marker::Copy for ViewDataType {
}
impl ::std::default::Default for ViewType {
impl ::std::default::Default for ViewDataType {
fn default() -> Self {
ViewType::RichText
ViewDataType::RichText
}
}
impl ::protobuf::reflect::ProtobufValue for ViewType {
impl ::protobuf::reflect::ProtobufValue for ViewDataType {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\nview.proto\"\x97\x02\n\x04View\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
\n\nview.proto\"\xd4\x02\n\x04View\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
\x02id\x12\x20\n\x0cbelong_to_id\x18\x02\x20\x01(\tR\nbelongToId\x12\x12\
\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x04\x20\x01\
(\tR\x04desc\x12&\n\tview_type\x18\x05\x20\x01(\x0e2\t.ViewTypeR\x08view\
Type\x12\x18\n\x07version\x18\x06\x20\x01(\x03R\x07version\x12-\n\nbelon\
gings\x18\x07\x20\x01(\x0b2\r.RepeatedViewR\nbelongings\x12#\n\rmodified\
_time\x18\x08\x20\x01(\x03R\x0cmodifiedTime\x12\x1f\n\x0bcreate_time\x18\
\t\x20\x01(\x03R\ncreateTime\"+\n\x0cRepeatedView\x12\x1b\n\x05items\x18\
\x01\x20\x03(\x0b2\x05.ViewR\x05items\"\xcb\x01\n\x11CreateViewPayload\
\x12\x20\n\x0cbelong_to_id\x18\x01\x20\x01(\tR\nbelongToId\x12\x12\n\x04\
name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\
\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\0R\tthumbnail\x12&\n\t\
view_type\x18\x05\x20\x01(\x0e2\t.ViewTypeR\x08viewType\x12\x10\n\x03ext\
\x18\x06\x20\x01(\tR\x03extB\x12\n\x10one_of_thumbnail\"\xcd\x01\n\x10Cr\
eateViewParams\x12\x20\n\x0cbelong_to_id\x18\x01\x20\x01(\tR\nbelongToId\
\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\
\x20\x01(\tR\x04desc\x12\x1c\n\tthumbnail\x18\x04\x20\x01(\tR\tthumbnail\
\x12&\n\tview_type\x18\x05\x20\x01(\x0e2\t.ViewTypeR\x08viewType\x12\x10\
\n\x03ext\x18\x06\x20\x01(\tR\x03ext\x12\x17\n\x07view_id\x18\x07\x20\
\x01(\tR\x06viewId\"\x1e\n\x06ViewId\x12\x14\n\x05value\x18\x01\x20\x01(\
\tR\x05value\"&\n\x0eRepeatedViewId\x12\x14\n\x05items\x18\x01\x20\x03(\
\tR\x05items\"\xaa\x01\n\x11UpdateViewPayload\x12\x17\n\x07view_id\x18\
\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04n\
ame\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\x04desc\x12\x1e\n\tthumbn\
ail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\x0bone_of_nameB\r\n\x0bone_\
of_descB\x12\n\x10one_of_thumbnail\"\xa9\x01\n\x10UpdateViewParams\x12\
\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\
\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\
\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\
\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail*'\n\x08Vi\
ewType\x12\x0c\n\x08RichText\x10\0\x12\r\n\tPlainText\x10\x01b\x06proto3\
(\tR\x04desc\x12*\n\tdata_type\x18\x05\x20\x01(\x0e2\r.ViewDataTypeR\x08\
dataType\x12\x18\n\x07version\x18\x06\x20\x01(\x03R\x07version\x12-\n\nb\
elongings\x18\x07\x20\x01(\x0b2\r.RepeatedViewR\nbelongings\x12#\n\rmodi\
fied_time\x18\x08\x20\x01(\x03R\x0cmodifiedTime\x12\x1f\n\x0bcreate_time\
\x18\t\x20\x01(\x03R\ncreateTime\x12\x19\n\x08ext_data\x18\n\x20\x01(\tR\
\x07extData\x12\x1c\n\tthumbnail\x18\x0b\x20\x01(\tR\tthumbnail\"+\n\x0c\
RepeatedView\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.ViewR\x05items\
\"\xd8\x01\n\x11CreateViewPayload\x12\x20\n\x0cbelong_to_id\x18\x01\x20\
\x01(\tR\nbelongToId\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\
\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12\x1e\n\tthumbnail\x18\x04\
\x20\x01(\tH\0R\tthumbnail\x12*\n\tdata_type\x18\x05\x20\x01(\x0e2\r.Vie\
wDataTypeR\x08dataType\x12\x19\n\x08ext_data\x18\x06\x20\x01(\tR\x07extD\
ataB\x12\n\x10one_of_thumbnail\"\xee\x01\n\x10CreateViewParams\x12\x20\n\
\x0cbelong_to_id\x18\x01\x20\x01(\tR\nbelongToId\x12\x12\n\x04name\x18\
\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\
\x12\x1c\n\tthumbnail\x18\x04\x20\x01(\tR\tthumbnail\x12*\n\tdata_type\
\x18\x05\x20\x01(\x0e2\r.ViewDataTypeR\x08dataType\x12\x19\n\x08ext_data\
\x18\x06\x20\x01(\tR\x07extData\x12\x17\n\x07view_id\x18\x07\x20\x01(\tR\
\x06viewId\x12\x12\n\x04data\x18\x08\x20\x01(\tR\x04data\"\x1e\n\x06View\
Id\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"&\n\x0eRepeatedViewI\
d\x12\x14\n\x05items\x18\x01\x20\x03(\tR\x05items\"\xaa\x01\n\x11UpdateV\
iewPayload\x12\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\
\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\
\x01(\tH\x01R\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthu\
mbnailB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnai\
l\"\xa9\x01\n\x10UpdateViewParams\x12\x17\n\x07view_id\x18\x01\x20\x01(\
\tR\x06viewId\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\
\x04desc\x18\x03\x20\x01(\tH\x01R\x04desc\x12\x1e\n\tthumbnail\x18\x04\
\x20\x01(\tH\x02R\tthumbnailB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\
\x12\n\x10one_of_thumbnail*+\n\x0cViewDataType\x12\x0c\n\x08RichText\x10\
\0\x12\r\n\tPlainText\x10\x01b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -5,11 +5,13 @@ message View {
string belong_to_id = 2;
string name = 3;
string desc = 4;
ViewType view_type = 5;
ViewDataType data_type = 5;
int64 version = 6;
RepeatedView belongings = 7;
int64 modified_time = 8;
int64 create_time = 9;
string ext_data = 10;
string thumbnail = 11;
}
message RepeatedView {
repeated View items = 1;
@ -19,17 +21,18 @@ message CreateViewPayload {
string name = 2;
string desc = 3;
oneof one_of_thumbnail { string thumbnail = 4; };
ViewType view_type = 5;
string ext = 6;
ViewDataType data_type = 5;
string ext_data = 6;
}
message CreateViewParams {
string belong_to_id = 1;
string name = 2;
string desc = 3;
string thumbnail = 4;
ViewType view_type = 5;
string ext = 6;
ViewDataType data_type = 5;
string ext_data = 6;
string view_id = 7;
string data = 8;
}
message ViewId {
string value = 1;
@ -49,7 +52,7 @@ message UpdateViewParams {
oneof one_of_desc { string desc = 3; };
oneof one_of_thumbnail { string thumbnail = 4; };
}
enum ViewType {
enum ViewDataType {
RichText = 0;
PlainText = 1;
}

View File

@ -1,6 +1,6 @@
use crate::entities::{
app::{App, RepeatedApp},
view::{RepeatedView, View, ViewType},
view::{RepeatedView, View, ViewDataType},
workspace::Workspace,
};
use chrono::Utc;
@ -49,17 +49,19 @@ fn create_default_view(app_id: String, time: chrono::DateTime<Utc>) -> View {
let view_id = uuid::Uuid::new_v4();
let name = "Read me".to_string();
let desc = "".to_string();
let view_type = ViewType::RichText;
let data_type = ViewDataType::RichText;
View {
id: view_id.to_string(),
belong_to_id: app_id,
name,
desc,
view_type,
data_type,
version: 0,
belongings: Default::default(),
modified_time: time.timestamp(),
create_time: time.timestamp(),
ext_data: "".to_string(),
thumbnail: "".to_string(),
}
}

View File

@ -1,6 +1,6 @@
use crate::core::{trim, Attributes, Delta, PlainAttributes};
use crate::core::{trim, Attributes, Delta, PlainTextAttributes};
pub type PlainDeltaBuilder = DeltaBuilder<PlainAttributes>;
pub type PlainTextDeltaBuilder = DeltaBuilder<PlainTextAttributes>;
pub struct DeltaBuilder<T: Attributes> {
delta: Delta<T>,

View File

@ -13,7 +13,7 @@ use std::{
str::FromStr,
};
pub type PlainDelta = Delta<PlainAttributes>;
pub type PlainTextDelta = Delta<PlainTextAttributes>;
// TODO: optimize the memory usage with Arc::make_mut or Cow
#[derive(Clone, Debug, PartialEq, Eq)]

View File

@ -1,10 +1,10 @@
use crate::{
core::{Attributes, Operation, PlainAttributes},
core::{Attributes, Operation, PlainTextAttributes},
rich_text::RichTextAttributes,
};
pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>;
pub type PlainTextOpBuilder = OpBuilder<PlainAttributes>;
pub type PlainTextOpBuilder = OpBuilder<PlainTextAttributes>;
pub struct OpBuilder<T: Attributes> {
ty: Operation<T>,

View File

@ -339,14 +339,14 @@ where
}
#[derive(Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize)]
pub struct PlainAttributes();
impl fmt::Display for PlainAttributes {
pub struct PlainTextAttributes();
impl fmt::Display for PlainTextAttributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PlainAttributes")
}
}
impl Attributes for PlainAttributes {
impl Attributes for PlainTextAttributes {
fn is_empty(&self) -> bool {
true
}
@ -356,7 +356,7 @@ impl Attributes for PlainAttributes {
fn extend_other(&mut self, _other: Self) {}
}
impl OperationTransformable for PlainAttributes {
impl OperationTransformable for PlainTextAttributes {
fn compose(&self, _other: &Self) -> Result<Self, OTError> {
Ok(self.clone())
}

View File

@ -40,6 +40,7 @@ impl fmt::Display for RichTextAttributes {
}
}
#[inline(always)]
pub fn plain_attributes() -> RichTextAttributes {
RichTextAttributes::default()
}
@ -58,7 +59,7 @@ impl RichTextAttributes {
self.inner.insert(key, value);
}
pub fn add_kv(&mut self, key: RichTextAttributeKey, value: RichTextAttributeValue) {
pub fn insert(&mut self, key: RichTextAttributeKey, value: RichTextAttributeValue) {
self.inner.insert(key, value);
}

View File

@ -101,7 +101,7 @@ impl<'de> Deserialize<'de> for RichTextAttributes {
let mut attributes = RichTextAttributes::new();
while let Some(key) = map.next_key::<RichTextAttributeKey>()? {
let value = map.next_value::<RichTextAttributeValue>()?;
attributes.add_kv(key, value);
attributes.insert(key, value);
}
Ok(attributes)