mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge remote-tracking branch 'upstream/main' into export_notify
This commit is contained in:
commit
3c27108ec7
1
.github/workflows/ci.yaml
vendored
1
.github/workflows/ci.yaml
vendored
@ -34,6 +34,7 @@ jobs:
|
||||
with:
|
||||
channel: 'stable'
|
||||
cache: true
|
||||
flutter-version: '3.0.0'
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v2
|
||||
|
2
.github/workflows/dart_lint.yml
vendored
2
.github/workflows/dart_lint.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- uses: subosito/flutter-action@v1
|
||||
with:
|
||||
flutter-version: '2.10.0'
|
||||
flutter-version: '3.0.0'
|
||||
channel: "stable"
|
||||
- name: Deps Flutter
|
||||
run: flutter packages pub get
|
||||
|
1
.github/workflows/dart_test.yml
vendored
1
.github/workflows/dart_test.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.0.0'
|
||||
cache: true
|
||||
|
||||
- name: Cache Cargo
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -50,6 +50,7 @@ jobs:
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.0.0'
|
||||
|
||||
- name: Pre build
|
||||
working-directory: frontend
|
||||
@ -98,6 +99,7 @@ jobs:
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.0.0'
|
||||
|
||||
- name: Pre build
|
||||
working-directory: frontend
|
||||
|
@ -7,6 +7,7 @@ extend = [
|
||||
{ path = "scripts/makefile/docker.toml" },
|
||||
{ path = "scripts/makefile/env.toml" },
|
||||
{ path = "scripts/makefile/flutter.toml" },
|
||||
{ path = "scripts/makefile/tool.toml" },
|
||||
]
|
||||
|
||||
[config]
|
||||
|
20
frontend/app_flowy/.vscode/launch.json
vendored
20
frontend/app_flowy/.vscode/launch.json
vendored
@ -5,18 +5,30 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "app_flowy",
|
||||
// This task builds the Rust and Dart code of AppFlowy.
|
||||
"name": "Build",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "build_flowy_sdk",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug"
|
||||
},
|
||||
"cwd": "${workspaceRoot}"
|
||||
},
|
||||
{
|
||||
"name": "app_flowy(trace)",
|
||||
// This task only build the Dart code of AppFlowy.
|
||||
"name": "Build (Dart)",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug"
|
||||
},
|
||||
"cwd": "${workspaceRoot}"
|
||||
},
|
||||
{
|
||||
"name": "Build (trace log)",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/lib/main.dart",
|
||||
"type": "dart",
|
||||
@ -27,7 +39,7 @@
|
||||
"cwd": "${workspaceRoot}"
|
||||
},
|
||||
{
|
||||
"name": "app_flowy (profile mode)",
|
||||
"name": "Build (profile mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "profile"
|
||||
|
3
frontend/app_flowy/assets/images/grid/field/url.svg
Normal file
3
frontend/app_flowy/assets/images/grid/field/url.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 7.688L8.27223 12.1469C7.69304 12.6931 6.90749 13 6.0884 13C5.26931 13 4.48376 12.6931 3.90457 12.1469C3.32538 11.6006 3 10.8598 3 10.0873C3 9.31474 3.32538 8.57387 3.90457 8.02763L8.63234 3.56875C9.01847 3.20459 9.54216 3 10.0882 3C10.6343 3 11.158 3.20459 11.5441 3.56875C11.9302 3.93291 12.1472 4.42683 12.1472 4.94183C12.1472 5.45684 11.9302 5.95075 11.5441 6.31491L6.8112 10.7738C6.61814 10.9559 6.35629 11.0582 6.08326 11.0582C5.81022 11.0582 5.54838 10.9559 5.35531 10.7738C5.16225 10.5917 5.05379 10.3448 5.05379 10.0873C5.05379 9.82975 5.16225 9.58279 5.35531 9.40071L9.72297 5.28632" stroke="#333333" stroke-width="0.9989" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 797 B |
@ -160,6 +160,7 @@
|
||||
"numberFieldName": "Numbers",
|
||||
"singleSelectFieldName": "Select",
|
||||
"multiSelectFieldName": "Multiselect",
|
||||
"urlFieldName": "URL",
|
||||
"numberFormat": " Number format",
|
||||
"dateFormat": " Date format",
|
||||
"includeTime": " Include time",
|
||||
@ -168,6 +169,7 @@
|
||||
"dateFormatLocal": "Month/Month/Day",
|
||||
"dateFormatUS": "Month/Month/Day",
|
||||
"timeFormat": " Time format",
|
||||
"invalidTimeFormat": "Invalid format",
|
||||
"timeFormatTwelveHour": "12 hour",
|
||||
"timeFormatTwentyFourHour": "24 hour",
|
||||
"addSelectOption": "Add an option",
|
||||
|
67
frontend/app_flowy/lib/core/frameless_window.dart
Normal file
67
frontend/app_flowy/lib/core/frameless_window.dart
Normal file
@ -0,0 +1,67 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
class CocoaWindowChannel {
|
||||
CocoaWindowChannel._();
|
||||
|
||||
final MethodChannel _channel = const MethodChannel("flutter/cocoaWindow");
|
||||
|
||||
static final CocoaWindowChannel instance = CocoaWindowChannel._();
|
||||
|
||||
Future<void> setWindowPosition(Offset offset) async {
|
||||
await _channel.invokeMethod("setWindowPosition", [offset.dx, offset.dy]);
|
||||
}
|
||||
|
||||
Future<List<double>> getWindowPosition() async {
|
||||
final raw = await _channel.invokeMethod("getWindowPosition");
|
||||
final arr = raw as List<dynamic>;
|
||||
final List<double> result = arr.map((s) => s as double).toList();
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<void> zoom() async {
|
||||
await _channel.invokeMethod("zoom");
|
||||
}
|
||||
}
|
||||
|
||||
class MoveWindowDetector extends StatefulWidget {
|
||||
const MoveWindowDetector({Key? key, this.child}) : super(key: key);
|
||||
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
_MoveWindowDetectorState createState() => _MoveWindowDetectorState();
|
||||
}
|
||||
|
||||
class _MoveWindowDetectorState extends State<MoveWindowDetector> {
|
||||
double winX = 0;
|
||||
double winY = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!Platform.isMacOS) {
|
||||
return widget.child ?? Container();
|
||||
}
|
||||
return GestureDetector(
|
||||
// https://stackoverflow.com/questions/52965799/flutter-gesturedetector-not-working-with-containers-in-stack
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onDoubleTap: () async {
|
||||
await CocoaWindowChannel.instance.zoom();
|
||||
},
|
||||
onPanStart: (DragStartDetails details) {
|
||||
winX = details.globalPosition.dx;
|
||||
winY = details.globalPosition.dy;
|
||||
},
|
||||
onPanUpdate: (DragUpdateDetails details) async {
|
||||
final windowPos = await CocoaWindowChannel.instance.getWindowPosition();
|
||||
final double dx = windowPos[0];
|
||||
final double dy = windowPos[1];
|
||||
final deltaX = details.globalPosition.dx - winX;
|
||||
final deltaY = details.globalPosition.dy - winY;
|
||||
await CocoaWindowChannel.instance.setWindowPosition(Offset(dx + deltaX, dy - deltaY));
|
||||
},
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
@ -88,8 +88,9 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
||||
}
|
||||
|
||||
_launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
final uri = Uri.parse(url);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugin/plugin.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/app/app_listener.dart';
|
||||
|
@ -10,13 +10,13 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
|
||||
import 'dart:convert' show utf8;
|
||||
part 'cell_service.freezed.dart';
|
||||
part 'data_loader.dart';
|
||||
part 'context_builder.dart';
|
||||
|
@ -1,8 +1,9 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellContext = _GridCellContext<Cell, String>;
|
||||
typedef GridCellContext = _GridCellContext<String, String>;
|
||||
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
|
||||
typedef GridDateCellContext = _GridCellContext<DateCellData, DateCalData>;
|
||||
typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
|
||||
|
||||
class GridCellContextBuilder {
|
||||
final GridCellCache _cellCache;
|
||||
@ -16,26 +17,33 @@ class GridCellContextBuilder {
|
||||
_GridCellContext build() {
|
||||
switch (_gridCell.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellContext(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: DateCellDataParser(),
|
||||
);
|
||||
|
||||
return GridDateCellContext(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: DateCellDataLoader(gridCell: _gridCell),
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.Number:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
config: const GridCellDataConfig(
|
||||
reloadOnCellChanged: true,
|
||||
reloadOnFieldChanged: true,
|
||||
),
|
||||
parser: StringCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true),
|
||||
);
|
||||
return GridCellContext(
|
||||
gridCell: _gridCell,
|
||||
@ -44,26 +52,49 @@ class GridCellContextBuilder {
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.RichText:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellContext(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.SingleSelect:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: SelectOptionCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
);
|
||||
|
||||
return GridSelectOptionCellContext(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: SelectOptionCellDataLoader(gridCell: _gridCell),
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
|
||||
case FieldType.URL:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: URLCellDataParser(),
|
||||
);
|
||||
return GridURLCellContext(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
||||
// T: the type of the CellData
|
||||
// D: the type of the data that will be save to disk
|
||||
// ignore: must_be_immutable
|
||||
class _GridCellContext<T, D> extends Equatable {
|
||||
final GridCell gridCell;
|
||||
@ -77,7 +108,8 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
late final ValueNotifier<T?> _cellDataNotifier;
|
||||
bool isListening = false;
|
||||
VoidCallback? _onFieldChangedFn;
|
||||
Timer? _delayOperation;
|
||||
Timer? _loadDataOperation;
|
||||
Timer? _saveDataOperation;
|
||||
|
||||
_GridCellContext({
|
||||
required this.gridCell,
|
||||
@ -107,7 +139,7 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
|
||||
FieldType get fieldType => gridCell.field.fieldType;
|
||||
|
||||
VoidCallback? startListening({required void Function(T) onCellChanged}) {
|
||||
VoidCallback? startListening({required void Function(T?) onCellChanged}) {
|
||||
if (isListening) {
|
||||
Log.error("Already started. It seems like you should call clone first");
|
||||
return null;
|
||||
@ -131,10 +163,7 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
}
|
||||
|
||||
onCellChangedFn() {
|
||||
final value = _cellDataNotifier.value;
|
||||
if (value is T) {
|
||||
onCellChanged(value);
|
||||
}
|
||||
onCellChanged(_cellDataNotifier.value);
|
||||
|
||||
if (cellDataLoader.config.reloadOnCellChanged) {
|
||||
_loadData();
|
||||
@ -149,9 +178,9 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
_cellDataNotifier.removeListener(fn);
|
||||
}
|
||||
|
||||
T? getCellData() {
|
||||
T? getCellData({bool loadIfNoCache = true}) {
|
||||
final data = cellCache.get(_cacheKey);
|
||||
if (data == null) {
|
||||
if (data == null && loadIfNoCache) {
|
||||
_loadData();
|
||||
}
|
||||
return data;
|
||||
@ -161,13 +190,26 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
return _fieldService.getFieldTypeOptionData(fieldType: fieldType);
|
||||
}
|
||||
|
||||
Future<Option<FlowyError>> saveCellData(D data) {
|
||||
return cellDataPersistence.save(data);
|
||||
void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
|
||||
if (deduplicate) {
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
||||
final result = await cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final result = await cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
cellDataLoader.loadData().then((data) {
|
||||
_cellDataNotifier.value = data;
|
||||
cellCache.insert(GridCellCacheData(key: _cacheKey, object: data));
|
||||
@ -176,7 +218,8 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_delayOperation?.cancel();
|
||||
_loadDataOperation?.cancel();
|
||||
_saveDataOperation?.cancel();
|
||||
|
||||
if (_onFieldChangedFn != null) {
|
||||
cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
|
@ -4,8 +4,8 @@ abstract class IGridCellDataConfig {
|
||||
// The cell data will reload if it receives the field's change notification.
|
||||
bool get reloadOnFieldChanged;
|
||||
|
||||
// The cell data will reload if it receives the cell's change notification.
|
||||
// For example, the number cell should be reloaded after user input the number.
|
||||
// When the reloadOnCellChanged is true, it will load the cell data after user input.
|
||||
// For example: The number cell reload the cell data that carries the format
|
||||
// user input: 12
|
||||
// cell display: $12
|
||||
bool get reloadOnCellChanged;
|
||||
@ -30,60 +30,49 @@ abstract class IGridCellDataLoader<T> {
|
||||
IGridCellDataConfig get config;
|
||||
}
|
||||
|
||||
class GridCellDataLoader extends IGridCellDataLoader<Cell> {
|
||||
abstract class ICellDataParser<T> {
|
||||
T? parserData(List<int> data);
|
||||
}
|
||||
|
||||
class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
|
||||
final CellService service = CellService();
|
||||
final GridCell gridCell;
|
||||
final ICellDataParser<T> parser;
|
||||
|
||||
@override
|
||||
final IGridCellDataConfig config;
|
||||
|
||||
GridCellDataLoader({
|
||||
required this.gridCell,
|
||||
required this.parser,
|
||||
this.config = const GridCellDataConfig(),
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Cell?> loadData() {
|
||||
Future<T?> loadData() {
|
||||
final fut = service.getCell(
|
||||
gridId: gridCell.gridId,
|
||||
fieldId: gridCell.field.id,
|
||||
rowId: gridCell.rowId,
|
||||
);
|
||||
return fut.then((result) {
|
||||
return result.fold((data) => data, (err) {
|
||||
return fut.then(
|
||||
(result) => result.fold((Cell cell) {
|
||||
try {
|
||||
if (cell.data.isEmpty) {
|
||||
return null;
|
||||
} else {
|
||||
return parser.parserData(cell.data);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Log.error('$parser parser cellData failed, $e');
|
||||
Log.error('Stack trace \n $s');
|
||||
return null;
|
||||
}
|
||||
}, (err) {
|
||||
Log.error(err);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DateCellDataLoader extends IGridCellDataLoader<DateCellData> {
|
||||
final GridCell gridCell;
|
||||
final IGridCellDataConfig _config;
|
||||
DateCellDataLoader({
|
||||
required this.gridCell,
|
||||
}) : _config = const GridCellDataConfig(reloadOnFieldChanged: true);
|
||||
|
||||
@override
|
||||
IGridCellDataConfig get config => _config;
|
||||
|
||||
@override
|
||||
Future<DateCellData?> loadData() {
|
||||
final payload = CellIdentifierPayload.create()
|
||||
..gridId = gridCell.gridId
|
||||
..fieldId = gridCell.field.id
|
||||
..rowId = gridCell.rowId;
|
||||
|
||||
return GridEventGetDateCellData(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(data) => data,
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,3 +98,31 @@ class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellDat
|
||||
@override
|
||||
IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
|
||||
}
|
||||
|
||||
class StringCellDataParser implements ICellDataParser<String> {
|
||||
@override
|
||||
String? parserData(List<int> data) {
|
||||
return utf8.decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
class DateCellDataParser implements ICellDataParser<DateCellData> {
|
||||
@override
|
||||
DateCellData? parserData(List<int> data) {
|
||||
return DateCellData.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
|
||||
class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> {
|
||||
@override
|
||||
SelectOptionCellData? parserData(List<int> data) {
|
||||
return SelectOptionCellData.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
|
||||
class URLCellDataParser implements ICellDataParser<URLCellData> {
|
||||
@override
|
||||
URLCellData? parserData(List<int> data) {
|
||||
return URLCellData.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -16,15 +15,15 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
}) : super(CheckboxCellState.initial(cellContext)) {
|
||||
on<CheckboxCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_Initial value) {
|
||||
await event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
},
|
||||
select: (_Selected value) async {
|
||||
select: () async {
|
||||
_updateCellData();
|
||||
},
|
||||
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
|
||||
emit(state.copyWith(isSelected: _isSelected(value.cell)));
|
||||
didReceiveCellUpdate: (cellData) {
|
||||
emit(state.copyWith(isSelected: _isSelected(cellData)));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -43,9 +42,9 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_onCellChangedFn = cellContext.startListening(onCellChanged: ((cell) {
|
||||
_onCellChangedFn = cellContext.startListening(onCellChanged: ((cellData) {
|
||||
if (!isClosed) {
|
||||
add(CheckboxCellEvent.didReceiveCellUpdate(cell));
|
||||
add(CheckboxCellEvent.didReceiveCellUpdate(cellData));
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -59,7 +58,7 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
class CheckboxCellEvent with _$CheckboxCellEvent {
|
||||
const factory CheckboxCellEvent.initial() = _Initial;
|
||||
const factory CheckboxCellEvent.select() = _Selected;
|
||||
const factory CheckboxCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory CheckboxCellEvent.didReceiveCellUpdate(String? cellData) = _DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -73,7 +72,6 @@ class CheckboxCellState with _$CheckboxCellState {
|
||||
}
|
||||
}
|
||||
|
||||
bool _isSelected(Cell? cell) {
|
||||
final content = cell?.content ?? "";
|
||||
return content == "Yes";
|
||||
bool _isSelected(String? cellData) {
|
||||
return cellData == "Yes";
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:easy_localization/easy_localization.dart' show StringTranslateExtension;
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -34,7 +37,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
setFocusedDay: (focusedDay) {
|
||||
emit(state.copyWith(focusedDay: focusedDay));
|
||||
},
|
||||
didReceiveCellUpdate: (DateCellData cellData) {
|
||||
didReceiveCellUpdate: (DateCellData? cellData) {
|
||||
final dateData = dateDataFromCellData(cellData);
|
||||
final time = dateData.foldRight("", (dateData, previous) => dateData.time);
|
||||
emit(state.copyWith(dateData: dateData, time: time));
|
||||
@ -80,25 +83,41 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await cellContext.saveCellData(newDateData);
|
||||
result.fold(
|
||||
() => emit(state.copyWith(
|
||||
dateData: Some(newDateData),
|
||||
timeFormatError: none(),
|
||||
)),
|
||||
(err) {
|
||||
switch (ErrorCode.valueOf(err.code)!) {
|
||||
case ErrorCode.InvalidDateTimeFormat:
|
||||
emit(state.copyWith(
|
||||
dateData: Some(newDateData),
|
||||
timeFormatError: Some(err.toString()),
|
||||
));
|
||||
break;
|
||||
default:
|
||||
Log.error(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
cellContext.saveCellData(newDateData, resultCallback: (result) {
|
||||
result.fold(
|
||||
() => emit(state.copyWith(
|
||||
dateData: Some(newDateData),
|
||||
timeFormatError: none(),
|
||||
)),
|
||||
(err) {
|
||||
switch (ErrorCode.valueOf(err.code)!) {
|
||||
case ErrorCode.InvalidDateTimeFormat:
|
||||
emit(state.copyWith(
|
||||
dateData: Some(newDateData),
|
||||
timeFormatError: Some(timeFormatPrompt(err)),
|
||||
));
|
||||
break;
|
||||
default:
|
||||
Log.error(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
String timeFormatPrompt(FlowyError error) {
|
||||
String msg = LocaleKeys.grid_field_invalidTimeFormat.tr() + ". ";
|
||||
switch (state.dateTypeOption.timeFormat) {
|
||||
case TimeFormat.TwelveHour:
|
||||
msg = msg + "e.g. 01: 00 AM";
|
||||
break;
|
||||
case TimeFormat.TwentyFourHour:
|
||||
msg = msg + "e.g. 13: 00";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -165,7 +184,7 @@ class DateCalEvent with _$DateCalEvent {
|
||||
const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat;
|
||||
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
|
||||
const factory DateCalEvent.setTime(String time) = _Time;
|
||||
const factory DateCalEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate;
|
||||
const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -16,7 +16,13 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () => _startListening(),
|
||||
didReceiveCellUpdate: (DateCellData value) => emit(state.copyWith(data: Some(value))),
|
||||
didReceiveCellUpdate: (DateCellData? cellData) {
|
||||
if (cellData != null) {
|
||||
emit(state.copyWith(data: Some(cellData)));
|
||||
} else {
|
||||
emit(state.copyWith(data: none()));
|
||||
}
|
||||
},
|
||||
didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)),
|
||||
);
|
||||
},
|
||||
@ -47,7 +53,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
@freezed
|
||||
class DateCellEvent with _$DateCellEvent {
|
||||
const factory DateCellEvent.initial() = _InitialCell;
|
||||
const factory DateCellEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate;
|
||||
const factory DateCellEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
|
||||
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -20,7 +19,7 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
_startListening();
|
||||
},
|
||||
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
|
||||
emit(state.copyWith(content: value.cell.content));
|
||||
emit(state.copyWith(content: value.cellContent ?? ""));
|
||||
},
|
||||
updateCell: (_UpdateCell value) async {
|
||||
await _updateCellValue(value, emit);
|
||||
@ -46,9 +45,9 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
|
||||
void _startListening() {
|
||||
_onCellChangedFn = cellContext.startListening(
|
||||
onCellChanged: ((cell) {
|
||||
onCellChanged: ((cellContent) {
|
||||
if (!isClosed) {
|
||||
add(NumberCellEvent.didReceiveCellUpdate(cell));
|
||||
add(NumberCellEvent.didReceiveCellUpdate(cellContent));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -59,7 +58,7 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
class NumberCellEvent with _$NumberCellEvent {
|
||||
const factory NumberCellEvent.initial() = _Initial;
|
||||
const factory NumberCellEvent.updateCell(String text) = _UpdateCell;
|
||||
const factory NumberCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory NumberCellEvent.didReceiveCellUpdate(String? cellContent) = _DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -69,7 +68,7 @@ class NumberCellState with _$NumberCellState {
|
||||
}) = _NumberCellState;
|
||||
|
||||
factory NumberCellState.initial(GridCellContext context) {
|
||||
final cell = context.getCellData();
|
||||
return NumberCellState(content: cell?.content ?? "");
|
||||
final cellContent = context.getCellData() ?? "";
|
||||
return NumberCellState(content: cellContent);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
|
||||
},
|
||||
didReceiveOptions: (_DidReceiveOptions value) {
|
||||
emit(state.copyWith(
|
||||
options: value.options,
|
||||
selectedOptions: value.selectedOptions,
|
||||
));
|
||||
},
|
||||
@ -45,8 +44,7 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
|
||||
onCellChanged: ((selectOptionContext) {
|
||||
if (!isClosed) {
|
||||
add(SelectOptionCellEvent.didReceiveOptions(
|
||||
selectOptionContext.options,
|
||||
selectOptionContext.selectOptions,
|
||||
selectOptionContext?.selectOptions ?? [],
|
||||
));
|
||||
}
|
||||
}),
|
||||
@ -58,7 +56,6 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
|
||||
class SelectOptionCellEvent with _$SelectOptionCellEvent {
|
||||
const factory SelectOptionCellEvent.initial() = _InitialCell;
|
||||
const factory SelectOptionCellEvent.didReceiveOptions(
|
||||
List<SelectOption> options,
|
||||
List<SelectOption> selectedOptions,
|
||||
) = _DidReceiveOptions;
|
||||
}
|
||||
@ -66,7 +63,6 @@ class SelectOptionCellEvent with _$SelectOptionCellEvent {
|
||||
@freezed
|
||||
class SelectOptionCellState with _$SelectOptionCellState {
|
||||
const factory SelectOptionCellState({
|
||||
required List<SelectOption> options,
|
||||
required List<SelectOption> selectedOptions,
|
||||
}) = _SelectOptionCellState;
|
||||
|
||||
@ -74,7 +70,6 @@ class SelectOptionCellState with _$SelectOptionCellState {
|
||||
final data = context.getCellData();
|
||||
|
||||
return SelectOptionCellState(
|
||||
options: data?.options ?? [],
|
||||
selectedOptions: data?.selectOptions ?? [],
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
|
||||
@ -6,23 +7,28 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
|
||||
import 'select_option_service.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
part 'select_option_editor_bloc.freezed.dart';
|
||||
|
||||
class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
|
||||
final SelectOptionService _selectOptionService;
|
||||
final GridSelectOptionCellContext cellContext;
|
||||
late final GridFieldsListener _fieldListener;
|
||||
void Function()? _onCellChangedFn;
|
||||
Timer? _delayOperation;
|
||||
|
||||
SelectOptionCellEditorBloc({
|
||||
required this.cellContext,
|
||||
}) : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell),
|
||||
_fieldListener = GridFieldsListener(gridId: cellContext.gridId),
|
||||
super(SelectOptionEditorState.initial(cellContext)) {
|
||||
on<SelectOptionEditorEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_Initial value) async {
|
||||
_startListening();
|
||||
_loadOptions();
|
||||
},
|
||||
didReceiveOptions: (_DidReceiveOptions value) {
|
||||
final result = _makeOptions(state.filter, value.options);
|
||||
@ -62,6 +68,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
|
||||
cellContext.removeListener(_onCellChangedFn!);
|
||||
_onCellChangedFn = null;
|
||||
}
|
||||
_delayOperation?.cancel();
|
||||
await _fieldListener.stop();
|
||||
cellContext.dispose();
|
||||
return super.close();
|
||||
}
|
||||
@ -105,6 +113,24 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
|
||||
));
|
||||
}
|
||||
|
||||
void _loadOptions() {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
_selectOptionService.getOpitonContext().then((result) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
return result.fold(
|
||||
(data) => add(SelectOptionEditorEvent.didReceiveOptions(data.options, data.selectOptions)),
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_MakeOptionResult _makeOptions(Option<String> filter, List<SelectOption> allOptions) {
|
||||
final List<SelectOption> options = List.from(allOptions);
|
||||
Option<String> createOption = filter;
|
||||
@ -134,13 +160,21 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
|
||||
_onCellChangedFn = cellContext.startListening(
|
||||
onCellChanged: ((selectOptionContext) {
|
||||
if (!isClosed) {
|
||||
add(SelectOptionEditorEvent.didReceiveOptions(
|
||||
selectOptionContext.options,
|
||||
selectOptionContext.selectOptions,
|
||||
));
|
||||
_loadOptions();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
_fieldListener.start(onFieldsChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
if (changeset.updatedFields.isNotEmpty) {
|
||||
_loadOptions();
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +201,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
|
||||
}) = _SelectOptionEditorState;
|
||||
|
||||
factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) {
|
||||
final data = context.getCellData();
|
||||
final data = context.getCellData(loadIfNoCache: false);
|
||||
return SelectOptionEditorState(
|
||||
options: data?.options ?? [],
|
||||
allOptions: data?.options ?? [],
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -14,21 +13,16 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
}) : super(TextCellState.initial(cellContext)) {
|
||||
on<TextCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialCell value) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
_startListening();
|
||||
},
|
||||
updateText: (_UpdateText value) {
|
||||
cellContext.saveCellData(value.text);
|
||||
emit(state.copyWith(content: value.text));
|
||||
updateText: (text) {
|
||||
cellContext.saveCellData(text);
|
||||
emit(state.copyWith(content: text));
|
||||
},
|
||||
didReceiveCellData: (_DidReceiveCellData value) {
|
||||
emit(state.copyWith(content: value.cellData.cell?.content ?? ""));
|
||||
},
|
||||
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
|
||||
emit(state.copyWith(
|
||||
content: value.cell.content,
|
||||
));
|
||||
didReceiveCellUpdate: (content) {
|
||||
emit(state.copyWith(content: content));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -47,9 +41,9 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
|
||||
void _startListening() {
|
||||
_onCellChangedFn = cellContext.startListening(
|
||||
onCellChanged: ((cell) {
|
||||
onCellChanged: ((cellContent) {
|
||||
if (!isClosed) {
|
||||
add(TextCellEvent.didReceiveCellUpdate(cell));
|
||||
add(TextCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -59,8 +53,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
@freezed
|
||||
class TextCellEvent with _$TextCellEvent {
|
||||
const factory TextCellEvent.initial() = _InitialCell;
|
||||
const factory TextCellEvent.didReceiveCellData(GridCell cellData) = _DidReceiveCellData;
|
||||
const factory TextCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory TextCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate;
|
||||
const factory TextCellEvent.updateText(String text) = _UpdateText;
|
||||
}
|
||||
|
||||
@ -71,6 +64,6 @@ class TextCellState with _$TextCellState {
|
||||
}) = _TextCellState;
|
||||
|
||||
factory TextCellState.initial(GridCellContext context) => TextCellState(
|
||||
content: context.getCellData()?.content ?? "",
|
||||
content: context.getCellData() ?? "",
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'cell_service/cell_service.dart';
|
||||
|
||||
part 'url_cell_bloc.freezed.dart';
|
||||
|
||||
class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
||||
final GridURLCellContext cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
URLCellBloc({
|
||||
required this.cellContext,
|
||||
}) : super(URLCellState.initial(cellContext)) {
|
||||
on<URLCellEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
},
|
||||
didReceiveCellUpdate: (cellData) {
|
||||
emit(state.copyWith(
|
||||
content: cellData?.content ?? "",
|
||||
url: cellData?.url ?? "",
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
if (_onCellChangedFn != null) {
|
||||
cellContext.removeListener(_onCellChangedFn!);
|
||||
_onCellChangedFn = null;
|
||||
}
|
||||
cellContext.dispose();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_onCellChangedFn = cellContext.startListening(
|
||||
onCellChanged: ((cellData) {
|
||||
if (!isClosed) {
|
||||
add(URLCellEvent.didReceiveCellUpdate(cellData));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class URLCellEvent with _$URLCellEvent {
|
||||
const factory URLCellEvent.initial() = _InitialCell;
|
||||
const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class URLCellState with _$URLCellState {
|
||||
const factory URLCellState({
|
||||
required String content,
|
||||
required String url,
|
||||
}) = _URLCellState;
|
||||
|
||||
factory URLCellState.initial(GridURLCellContext context) {
|
||||
final cellData = context.getCellData();
|
||||
return URLCellState(
|
||||
content: cellData?.content ?? "",
|
||||
url: cellData?.url ?? "",
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'cell_service/cell_service.dart';
|
||||
|
||||
part 'url_cell_editor_bloc.freezed.dart';
|
||||
|
||||
class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
|
||||
final GridURLCellContext cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
URLCellEditorBloc({
|
||||
required this.cellContext,
|
||||
}) : super(URLCellEditorState.initial(cellContext)) {
|
||||
on<URLCellEditorEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
},
|
||||
updateText: (text) {
|
||||
cellContext.saveCellData(text, deduplicate: true);
|
||||
emit(state.copyWith(content: text));
|
||||
},
|
||||
didReceiveCellUpdate: (cellData) {
|
||||
emit(state.copyWith(content: cellData?.content ?? ""));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
if (_onCellChangedFn != null) {
|
||||
cellContext.removeListener(_onCellChangedFn!);
|
||||
_onCellChangedFn = null;
|
||||
}
|
||||
cellContext.dispose();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_onCellChangedFn = cellContext.startListening(
|
||||
onCellChanged: ((cellData) {
|
||||
if (!isClosed) {
|
||||
add(URLCellEditorEvent.didReceiveCellUpdate(cellData));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class URLCellEditorEvent with _$URLCellEditorEvent {
|
||||
const factory URLCellEditorEvent.initial() = _InitialCell;
|
||||
const factory URLCellEditorEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
|
||||
const factory URLCellEditorEvent.updateText(String text) = _UpdateText;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class URLCellEditorState with _$URLCellEditorState {
|
||||
const factory URLCellEditorState({
|
||||
required String content,
|
||||
}) = _URLCellEditorState;
|
||||
|
||||
factory URLCellEditorState.initial(GridURLCellContext context) {
|
||||
final cellData = context.getCellData();
|
||||
return URLCellEditorState(
|
||||
content: cellData?.content ?? "",
|
||||
);
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
@ -6,8 +8,6 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'cell/cell_service/cell_service.dart';
|
||||
import 'row/row_service.dart';
|
||||
|
||||
|
@ -49,6 +49,9 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
unauthorized: (_Unauthorized value) {
|
||||
emit(state.copyWith(unauthorized: true));
|
||||
},
|
||||
collapseMenu: (e) {
|
||||
emit(state.copyWith(isMenuCollapsed: !state.isMenuCollapsed));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -77,6 +80,7 @@ class HomeEvent with _$HomeEvent {
|
||||
const factory HomeEvent.dismissEditPannel() = _DismissEditPannel;
|
||||
const factory HomeEvent.didReceiveWorkspaceSetting(CurrentWorkspaceSetting setting) = _DidReceiveWorkspaceSetting;
|
||||
const factory HomeEvent.unauthorized(String msg) = _Unauthorized;
|
||||
const factory HomeEvent.collapseMenu() = _CollapseMenu;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -87,6 +91,7 @@ class HomeState with _$HomeState {
|
||||
required Option<EditPannelContext> pannelContext,
|
||||
required CurrentWorkspaceSetting workspaceSetting,
|
||||
required bool unauthorized,
|
||||
required bool isMenuCollapsed,
|
||||
}) = _HomeState;
|
||||
|
||||
factory HomeState.initial(CurrentWorkspaceSetting workspaceSetting) => HomeState(
|
||||
@ -95,5 +100,6 @@ class HomeState with _$HomeState {
|
||||
pannelContext: none(),
|
||||
workspaceSetting: workspaceSetting,
|
||||
unauthorized: false,
|
||||
isMenuCollapsed: false,
|
||||
);
|
||||
}
|
||||
|
@ -25,10 +25,6 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
||||
listener.start(addAppCallback: _handleAppsOrFail);
|
||||
await _fetchApps(emit);
|
||||
},
|
||||
collapse: (e) async {
|
||||
final isCollapse = state.isCollapse;
|
||||
emit(state.copyWith(isCollapse: !isCollapse));
|
||||
},
|
||||
openPage: (e) async {
|
||||
emit(state.copyWith(plugin: e.plugin));
|
||||
},
|
||||
@ -94,7 +90,6 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
||||
@freezed
|
||||
class MenuEvent with _$MenuEvent {
|
||||
const factory MenuEvent.initial() = _Initial;
|
||||
const factory MenuEvent.collapse() = _Collapse;
|
||||
const factory MenuEvent.openPage(Plugin plugin) = _OpenPage;
|
||||
const factory MenuEvent.createApp(String name, {String? desc}) = _CreateApp;
|
||||
const factory MenuEvent.moveApp(int fromIndex, int toIndex) = _MoveApp;
|
||||
@ -104,14 +99,12 @@ class MenuEvent with _$MenuEvent {
|
||||
@freezed
|
||||
class MenuState with _$MenuState {
|
||||
const factory MenuState({
|
||||
required bool isCollapse,
|
||||
required List<App> apps,
|
||||
required Either<Unit, FlowyError> successOrFailure,
|
||||
required Plugin plugin,
|
||||
}) = _MenuState;
|
||||
|
||||
factory MenuState.initial() => MenuState(
|
||||
isCollapse: false,
|
||||
apps: [],
|
||||
successOrFailure: left(unit),
|
||||
plugin: makePlugin(pluginType: DefaultPlugin.blank.type()),
|
||||
|
@ -1,8 +1,12 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:time/time.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
@ -11,6 +15,7 @@ import 'package:app_flowy/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/navigation.dart';
|
||||
import 'package:app_flowy/core/frameless_window.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
@ -152,7 +157,7 @@ class HomeStackManager {
|
||||
child: Selector<HomeStackNotifier, Widget>(
|
||||
selector: (context, notifier) => notifier.titleWidget,
|
||||
builder: (context, widget, child) {
|
||||
return const HomeTopBar();
|
||||
return const MoveWindowDetector(child: HomeTopBar());
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -191,6 +196,14 @@ class HomeTopBar extends StatelessWidget {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
BlocBuilder<HomeBloc, HomeState>(
|
||||
buildWhen: ((previous, current) => previous.isMenuCollapsed != current.isMenuCollapsed),
|
||||
builder: (context, state) {
|
||||
if (state.isMenuCollapsed && Platform.isMacOS) {
|
||||
return const HSpace(80);
|
||||
}
|
||||
return const HSpace(0);
|
||||
}),
|
||||
const FlowyNavigation(),
|
||||
const HSpace(16),
|
||||
ChangeNotifierProvider.value(
|
||||
|
@ -26,7 +26,7 @@ class ViewSection extends StatelessWidget {
|
||||
listenWhen: (p, c) => p.selectedView != c.selectedView,
|
||||
listener: (context, state) {
|
||||
if (state.selectedView != null) {
|
||||
WidgetsBinding.instance?.addPostFrameCallback((_) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
getIt<HomeStackManager>().setPlugin(state.selectedView!.plugin());
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
export './app/header/header.dart';
|
||||
export './app/menu_app.dart';
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/trash/menu.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
@ -18,7 +20,9 @@ import 'package:expandable/expandable.dart';
|
||||
import 'package:flowy_infra/time/duration.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/core/frameless_window.dart';
|
||||
// import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
|
||||
@ -59,10 +63,10 @@ class HomeMenu extends StatelessWidget {
|
||||
getIt<HomeStackManager>().setPlugin(state.plugin);
|
||||
},
|
||||
),
|
||||
BlocListener<MenuBloc, MenuState>(
|
||||
listenWhen: (p, c) => p.isCollapse != c.isCollapse,
|
||||
BlocListener<HomeBloc, HomeState>(
|
||||
listenWhen: (p, c) => p.isMenuCollapsed != c.isMenuCollapsed,
|
||||
listener: (context, state) {
|
||||
_collapsedNotifier.value = state.isCollapse;
|
||||
_collapsedNotifier.value = state.isMenuCollapsed;
|
||||
},
|
||||
)
|
||||
],
|
||||
@ -179,6 +183,17 @@ class MenuSharedState {
|
||||
|
||||
class MenuTopBar extends StatelessWidget {
|
||||
const MenuTopBar({Key? key}) : super(key: key);
|
||||
|
||||
Widget renderIcon(BuildContext context) {
|
||||
if (Platform.isMacOS) {
|
||||
return Container();
|
||||
}
|
||||
final theme = context.watch<AppTheme>();
|
||||
return (theme.isDark
|
||||
? svgWithSize("flowy_logo_dark_mode", const Size(92, 17))
|
||||
: svgWithSize("flowy_logo_with_text", const Size(92, 17)));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
@ -186,20 +201,19 @@ class MenuTopBar extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: HomeSizes.topBarHeight,
|
||||
child: Row(
|
||||
child: MoveWindowDetector(
|
||||
child: Row(
|
||||
children: [
|
||||
(theme.isDark
|
||||
? svgWithSize("flowy_logo_dark_mode", const Size(92, 17))
|
||||
: svgWithSize("flowy_logo_with_text", const Size(92, 17))),
|
||||
renderIcon(context),
|
||||
const Spacer(),
|
||||
FlowyIconButton(
|
||||
width: 28,
|
||||
onPressed: () => context.read<MenuBloc>().add(const MenuEvent.collapse()),
|
||||
onPressed: () => context.read<HomeBloc>().add(const HomeEvent.collapseMenu()),
|
||||
iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
||||
icon: svgWidget("home/hide_menu", color: theme.iconColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
@ -95,6 +96,7 @@ class FlowyNavigation extends StatelessWidget {
|
||||
width: 24,
|
||||
onPressed: () {
|
||||
notifier.value = false;
|
||||
ctx.read<HomeBloc>().add(const HomeEvent.collapseMenu());
|
||||
},
|
||||
iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2),
|
||||
icon: svgWidget("home/hide_menu", color: theme.iconColor),
|
||||
|
@ -184,7 +184,7 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
|
||||
// Listening to the WidgetsBinding instance is necessary so that we can
|
||||
// hide the arrows when the window gets a new size and thus the toolbar
|
||||
// becomes scrollable/unscrollable.
|
||||
WidgetsBinding.instance!.addObserver(this);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
// Workaround to allow the scroll controller attach to our ListView so that
|
||||
// we can detect if overflow arrows need to be shown on init.
|
||||
@ -226,7 +226,7 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
WidgetsBinding.instance!.removeObserver(this);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import 'date_cell/date_cell.dart';
|
||||
import 'number_cell.dart';
|
||||
import 'select_option_cell/select_option_cell.dart';
|
||||
import 'text_cell.dart';
|
||||
import 'url_cell/url_cell.dart';
|
||||
|
||||
GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) {
|
||||
final key = ValueKey(gridCell.cellId());
|
||||
@ -32,10 +33,10 @@ GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {
|
||||
return NumberCell(cellContextBuilder: cellContextBuilder, key: key);
|
||||
case FieldType.RichText:
|
||||
return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
|
||||
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
case FieldType.URL:
|
||||
return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
class BlankCell extends StatelessWidget {
|
||||
@ -47,13 +48,11 @@ class BlankCell extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GridCellWidget extends HoverWidget {
|
||||
abstract class GridCellWidget implements FlowyHoverWidget {
|
||||
@override
|
||||
final ValueNotifier<bool> onFocus = ValueNotifier<bool>(false);
|
||||
|
||||
final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier();
|
||||
|
||||
GridCellWidget({Key? key}) : super(key: key);
|
||||
}
|
||||
|
||||
class GridCellRequestFocusNotifier extends ChangeNotifier {
|
||||
@ -151,7 +150,7 @@ class CellContainer extends StatelessWidget {
|
||||
});
|
||||
|
||||
if (expander != null) {
|
||||
container = _CellEnterRegion(child: container, expander: expander!);
|
||||
container = CellEnterRegion(child: container, expander: expander!);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
@ -181,10 +180,10 @@ class CellContainer extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _CellEnterRegion extends StatelessWidget {
|
||||
class CellEnterRegion extends StatelessWidget {
|
||||
final Widget child;
|
||||
final Widget expander;
|
||||
const _CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key);
|
||||
const CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class CheckboxCell extends GridCellWidget {
|
||||
class CheckboxCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
CheckboxCell({
|
||||
required this.cellContextBuilder,
|
||||
@ -41,7 +41,7 @@ class _CheckboxCellState extends State<CheckboxCell> {
|
||||
onPressed: () => context.read<CheckboxCellBloc>().add(const CheckboxCellEvent.select()),
|
||||
iconPadding: EdgeInsets.zero,
|
||||
icon: icon,
|
||||
width: 23,
|
||||
width: 20,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -18,7 +18,7 @@ abstract class GridCellDelegate {
|
||||
GridCellDelegate get delegate;
|
||||
}
|
||||
|
||||
class DateCell extends GridCellWidget {
|
||||
class DateCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
late final DateCellStyle? cellStyle;
|
||||
|
||||
|
@ -7,7 +7,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class NumberCell extends GridCellWidget {
|
||||
class NumberCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
|
||||
NumberCell({
|
||||
|
@ -87,7 +87,7 @@ class SelectOptionTag extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ChoiceChip(
|
||||
pressElevation: 1,
|
||||
label: FlowyText.medium(name, fontSize: 12),
|
||||
label: FlowyText.medium(name, fontSize: 12, overflow: TextOverflow.ellipsis),
|
||||
selectedColor: color,
|
||||
backgroundColor: color,
|
||||
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
@ -130,11 +130,18 @@ class SelectOptionTagCell extends StatelessWidget {
|
||||
child: InkWell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Row(children: [
|
||||
SelectOptionTag.fromSelectOption(context: context, option: option),
|
||||
const Spacer(),
|
||||
...children,
|
||||
]),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
flex: 2,
|
||||
child: SelectOptionTag.fromSelectOption(context: context, option: option),
|
||||
),
|
||||
const Spacer(),
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () => onSelected(option),
|
||||
),
|
||||
|
@ -20,7 +20,7 @@ class SelectOptionCellStyle extends GridCellStyle {
|
||||
});
|
||||
}
|
||||
|
||||
class SingleSelectCell extends GridCellWidget {
|
||||
class SingleSelectCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
late final SelectOptionCellStyle? cellStyle;
|
||||
|
||||
@ -74,7 +74,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
class MultiSelectCell extends GridCellWidget {
|
||||
class MultiSelectCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
late final SelectOptionCellStyle? cellStyle;
|
||||
|
||||
@ -160,7 +160,7 @@ class _SelectOptionCell extends StatelessWidget {
|
||||
.toList();
|
||||
child = Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Wrap(children: tags, spacing: 4, runSpacing: 4),
|
||||
child: Wrap(children: tags, spacing: 4, runSpacing: 2),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,18 @@ class _SelectOptionCell extends StatelessWidget {
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: _body(theme, context)),
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: SelectOptionTagCell(
|
||||
option: option,
|
||||
onSelected: (option) {
|
||||
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.selectOption(option.id));
|
||||
},
|
||||
children: [
|
||||
if (isSelected) svgWidget("grid/checkmark"),
|
||||
],
|
||||
),
|
||||
),
|
||||
FlowyIconButton(
|
||||
width: 30,
|
||||
onPressed: () => _showEditPannel(context),
|
||||
@ -237,18 +248,6 @@ class _SelectOptionCell extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _body(AppTheme theme, BuildContext context) {
|
||||
return SelectOptionTagCell(
|
||||
option: option,
|
||||
onSelected: (option) {
|
||||
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.selectOption(option.id));
|
||||
},
|
||||
children: [
|
||||
if (isSelected) svgWidget("grid/checkmark"),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showEditPannel(BuildContext context) {
|
||||
final pannel = SelectOptionTypeOptionEditor(
|
||||
option: option,
|
||||
|
@ -13,7 +13,7 @@ class GridTextCellStyle extends GridCellStyle {
|
||||
});
|
||||
}
|
||||
|
||||
class GridTextCell extends GridCellWidget {
|
||||
class GridTextCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
late final GridTextCellStyle? cellStyle;
|
||||
GridTextCell({
|
||||
|
@ -0,0 +1,96 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/url_cell_editor_bloc.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class URLCellEditor extends StatefulWidget {
|
||||
final GridURLCellContext cellContext;
|
||||
const URLCellEditor({required this.cellContext, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<URLCellEditor> createState() => _URLCellEditorState();
|
||||
|
||||
static void show(
|
||||
BuildContext context,
|
||||
GridURLCellContext cellContext,
|
||||
) {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
final editor = URLCellEditor(
|
||||
cellContext: cellContext,
|
||||
);
|
||||
|
||||
//
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: SizedBox(width: 200, child: editor),
|
||||
constraints: BoxConstraints.loose(const Size(300, 160)),
|
||||
),
|
||||
identifier: URLCellEditor.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomWithCenterAligned,
|
||||
);
|
||||
}
|
||||
|
||||
static String identifier() {
|
||||
return (URLCellEditor).toString();
|
||||
}
|
||||
}
|
||||
|
||||
class _URLCellEditorState extends State<URLCellEditor> {
|
||||
late URLCellEditorBloc _cellBloc;
|
||||
late TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = URLCellEditorBloc(cellContext: widget.cellContext);
|
||||
_cellBloc.add(const URLCellEditorEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocListener<URLCellEditorBloc, URLCellEditorState>(
|
||||
listener: (context, state) {
|
||||
if (_controller.text != state.content) {
|
||||
_controller.text = state.content;
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
controller: _controller,
|
||||
onChanged: (value) => focusChanged(),
|
||||
maxLines: null,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
hintText: "",
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_cellBloc.close();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> focusChanged() async {
|
||||
if (mounted) {
|
||||
if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) {
|
||||
_cellBloc.add(URLCellEditorEvent.updateText(_controller.text));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
import 'dart:async';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../cell_builder.dart';
|
||||
import 'cell_editor.dart';
|
||||
|
||||
class GridURLCellStyle extends GridCellStyle {
|
||||
String? placeholder;
|
||||
|
||||
GridURLCellStyle({
|
||||
this.placeholder,
|
||||
});
|
||||
}
|
||||
|
||||
class GridURLCell extends StatefulWidget with GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
late final GridURLCellStyle? cellStyle;
|
||||
GridURLCell({
|
||||
required this.cellContextBuilder,
|
||||
GridCellStyle? style,
|
||||
Key? key,
|
||||
}) : super(key: key) {
|
||||
if (style != null) {
|
||||
cellStyle = (style as GridURLCellStyle);
|
||||
} else {
|
||||
cellStyle = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
State<GridURLCell> createState() => _GridURLCellState();
|
||||
}
|
||||
|
||||
class _GridURLCellState extends State<GridURLCell> {
|
||||
late URLCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
||||
_cellBloc = URLCellBloc(cellContext: cellContext);
|
||||
_cellBloc.add(const URLCellEvent.initial());
|
||||
_listenRequestFocus(context);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<URLCellBloc, URLCellState>(
|
||||
builder: (context, state) {
|
||||
final richText = RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
text: state.content,
|
||||
style: TextStyle(
|
||||
color: theme.main2,
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: _tapGesture(context),
|
||||
),
|
||||
);
|
||||
|
||||
return CellEnterRegion(
|
||||
child: Align(alignment: Alignment.centerLeft, child: richText),
|
||||
expander: _EditCellIndicator(onTap: () {}),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
widget.requestFocus.removeAllListener();
|
||||
_cellBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
TapGestureRecognizer _tapGesture(BuildContext context) {
|
||||
final gesture = TapGestureRecognizer();
|
||||
gesture.onTap = () async {
|
||||
final url = context.read<URLCellBloc>().state.url;
|
||||
await _openUrlOrEdit(url);
|
||||
};
|
||||
return gesture;
|
||||
}
|
||||
|
||||
Future<void> _openUrlOrEdit(String url) async {
|
||||
final uri = Uri.parse(url);
|
||||
if (url.isNotEmpty && await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
||||
URLCellEditor.show(context, cellContext);
|
||||
}
|
||||
}
|
||||
|
||||
void _listenRequestFocus(BuildContext context) {
|
||||
widget.requestFocus.addListener(() {
|
||||
_openUrlOrEdit(_cellBloc.state.url);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _EditCellIndicator extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
const _EditCellIndicator({required this.onTap, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return FlowyIconButton(
|
||||
width: 26,
|
||||
onPressed: onTap,
|
||||
hoverColor: theme.hover,
|
||||
radius: BorderRadius.circular(4),
|
||||
iconPadding: const EdgeInsets.all(5),
|
||||
icon: svgWidget("editor/edit", color: theme.iconColor),
|
||||
);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import 'type_option/multi_select.dart';
|
||||
import 'type_option/number.dart';
|
||||
import 'type_option/rich_text.dart';
|
||||
import 'type_option/single_select.dart';
|
||||
import 'type_option/url.dart';
|
||||
|
||||
typedef UpdateFieldCallback = void Function(Field, Uint8List);
|
||||
typedef SwitchToFieldCallback = Future<Either<FieldTypeOptionData, FlowyError>> Function(
|
||||
@ -168,9 +169,12 @@ TypeOptionBuilder _makeTypeOptionBuild({
|
||||
typeOptionContext as RichTextTypeOptionContext,
|
||||
);
|
||||
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
case FieldType.URL:
|
||||
return URLTypeOptionBuilder(
|
||||
typeOptionContext as URLTypeOptionContext,
|
||||
);
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
TypeOptionContext _makeTypeOptionContext(GridFieldContext fieldContext) {
|
||||
@ -205,9 +209,15 @@ TypeOptionContext _makeTypeOptionContext(GridFieldContext fieldContext) {
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: SingleSelectTypeOptionDataBuilder(),
|
||||
);
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
|
||||
case FieldType.URL:
|
||||
return URLTypeOptionContext(
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: URLTypeOptionDataBuilder(),
|
||||
);
|
||||
}
|
||||
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
abstract class TypeOptionWidget extends StatelessWidget {
|
||||
|
@ -17,9 +17,10 @@ extension FieldTypeListExtension on FieldType {
|
||||
return "grid/field/text";
|
||||
case FieldType.SingleSelect:
|
||||
return "grid/field/single_select";
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
case FieldType.URL:
|
||||
return "grid/field/url";
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
String title() {
|
||||
@ -36,8 +37,9 @@ extension FieldTypeListExtension on FieldType {
|
||||
return LocaleKeys.grid_field_textFieldName.tr();
|
||||
case FieldType.SingleSelect:
|
||||
return LocaleKeys.grid_field_singleSelectFieldName.tr();
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
case FieldType.URL:
|
||||
return LocaleKeys.grid_field_urlFieldName.tr();
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef URLTypeOptionContext = TypeOptionContext<URLTypeOption>;
|
||||
|
||||
class URLTypeOptionDataBuilder extends TypeOptionDataBuilder<URLTypeOption> {
|
||||
@override
|
||||
URLTypeOption fromBuffer(List<int> buffer) {
|
||||
return URLTypeOption.fromBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
class URLTypeOptionBuilder extends TypeOptionBuilder {
|
||||
URLTypeOptionBuilder(URLTypeOptionContext typeOptionContext);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => null;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class NumberCell extends StatefulWidget {
|
||||
final GridCell cellData;
|
||||
|
||||
const NumberCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<NumberCell> createState() => _NumberCellState();
|
||||
}
|
||||
|
||||
class _NumberCellState extends State<NumberCell> {
|
||||
late NumberCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: widget.cellData);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<NumberCellBloc, NumberCellState>(
|
||||
builder: (context, state) {
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_cellBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
@ -209,9 +209,10 @@ class _CellExpander extends StatelessWidget {
|
||||
return FittedBox(
|
||||
fit: BoxFit.contain,
|
||||
child: FlowyIconButton(
|
||||
width: 26,
|
||||
onPressed: onExpand,
|
||||
iconPadding: const EdgeInsets.fromLTRB(6, 6, 6, 6),
|
||||
fillColor: theme.surface,
|
||||
iconPadding: const EdgeInsets.all(5),
|
||||
radius: BorderRadius.circular(4),
|
||||
icon: svgWidget("grid/expander", color: theme.main1),
|
||||
),
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
@ -67,14 +68,15 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
||||
return bloc;
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 40),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: const [Spacer(), _CloseButton()],
|
||||
)),
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: const [Spacer(), _CloseButton()],
|
||||
),
|
||||
),
|
||||
Expanded(child: _PropertyList(cellCache: widget.cellCache)),
|
||||
],
|
||||
),
|
||||
@ -153,24 +155,26 @@ class _RowDetailCell extends StatelessWidget {
|
||||
cellCache,
|
||||
style: _buildCellStyle(theme, gridCell.field.fieldType),
|
||||
);
|
||||
return SizedBox(
|
||||
height: 36,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)),
|
||||
),
|
||||
const HSpace(10),
|
||||
Expanded(
|
||||
child: FlowyHover2(
|
||||
child: cell,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 40),
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)),
|
||||
),
|
||||
),
|
||||
],
|
||||
const HSpace(10),
|
||||
Expanded(
|
||||
child: FlowyHover2(
|
||||
child: cell,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -209,7 +213,11 @@ GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) {
|
||||
return SelectOptionCellStyle(
|
||||
placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
|
||||
case FieldType.URL:
|
||||
return GridURLCellStyle(
|
||||
placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ class _EmojiPickerState extends State<EmojiPicker> {
|
||||
if (!loaded) {
|
||||
// Load emojis
|
||||
updateEmojiFuture.then(
|
||||
(value) => WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||
(value) => WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
loaded = true;
|
||||
|
@ -62,8 +62,9 @@ class QuestionBubble extends StatelessWidget {
|
||||
}
|
||||
|
||||
_launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
final uri = Uri.parse(url);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
window_size
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
@ -16,3 +19,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
|
@ -421,6 +421,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
EXCLUDED_ARCHS = arm64;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -552,6 +553,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
EXCLUDED_ARCHS = arm64;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -575,6 +577,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
EXCLUDED_ARCHS = arm64;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -1,12 +1,82 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
private let kTrafficLightOffetTop = 22
|
||||
|
||||
class MainFlutterWindow: NSWindow {
|
||||
func registerMethodChannel(flutterViewController: FlutterViewController) {
|
||||
let cocoaWindowChannel = FlutterMethodChannel(name: "flutter/cocoaWindow", binaryMessenger: flutterViewController.engine.binaryMessenger)
|
||||
cocoaWindowChannel.setMethodCallHandler({
|
||||
(call: FlutterMethodCall, result: FlutterResult) -> Void in
|
||||
if call.method == "setWindowPosition" {
|
||||
guard let position = call.arguments as? NSArray else {
|
||||
result(nil)
|
||||
return
|
||||
}
|
||||
let nX = position[0] as! NSNumber
|
||||
let nY = position[1] as! NSNumber
|
||||
let x = nX.doubleValue
|
||||
let y = nY.doubleValue
|
||||
|
||||
self.setFrameOrigin(NSPoint(x: x, y: y))
|
||||
result(nil)
|
||||
return
|
||||
} else if call.method == "getWindowPosition" {
|
||||
let frame = self.frame
|
||||
result([frame.origin.x, frame.origin.y])
|
||||
return
|
||||
} else if call.method == "zoom" {
|
||||
self.zoom(self)
|
||||
result(nil)
|
||||
return
|
||||
}
|
||||
|
||||
result(FlutterMethodNotImplemented)
|
||||
})
|
||||
}
|
||||
|
||||
func layoutTrafficLightButton(titlebarView: NSView, button: NSButton, offsetTop: CGFloat, offsetLeft: CGFloat) {
|
||||
button.translatesAutoresizingMaskIntoConstraints = false;
|
||||
titlebarView.addConstraint(NSLayoutConstraint.init(
|
||||
item: button,
|
||||
attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: titlebarView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1, constant: offsetTop))
|
||||
titlebarView.addConstraint(NSLayoutConstraint.init(
|
||||
item: button,
|
||||
attribute: NSLayoutConstraint.Attribute.left, relatedBy: NSLayoutConstraint.Relation.equal, toItem: titlebarView, attribute: NSLayoutConstraint.Attribute.left, multiplier: 1, constant: offsetLeft))
|
||||
}
|
||||
|
||||
func layoutTrafficLights() {
|
||||
let closeButton = self.standardWindowButton(ButtonType.closeButton)!
|
||||
let minButton = self.standardWindowButton(ButtonType.miniaturizeButton)!
|
||||
let zoomButton = self.standardWindowButton(ButtonType.zoomButton)!
|
||||
let titlebarView = closeButton.superview!
|
||||
|
||||
self.layoutTrafficLightButton(titlebarView: titlebarView, button: closeButton, offsetTop: CGFloat(kTrafficLightOffetTop), offsetLeft: 20)
|
||||
self.layoutTrafficLightButton(titlebarView: titlebarView, button: minButton, offsetTop: CGFloat(kTrafficLightOffetTop), offsetLeft: 38)
|
||||
self.layoutTrafficLightButton(titlebarView: titlebarView, button: zoomButton, offsetTop: CGFloat(kTrafficLightOffetTop), offsetLeft: 56)
|
||||
|
||||
let customToolbar = NSTitlebarAccessoryViewController()
|
||||
let newView = NSView()
|
||||
newView.frame = NSRect(origin: CGPoint(), size: CGSize(width: 0, height: 40)) // only the height is cared
|
||||
customToolbar.view = newView
|
||||
self.addTitlebarAccessoryViewController(customToolbar)
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
let flutterViewController = FlutterViewController.init()
|
||||
let windowFrame = self.frame
|
||||
self.contentViewController = flutterViewController
|
||||
|
||||
self.registerMethodChannel(flutterViewController: flutterViewController)
|
||||
|
||||
self.setFrame(windowFrame, display: true)
|
||||
self.titlebarAppearsTransparent = true
|
||||
self.titleVisibility = .hidden
|
||||
self.styleMask.insert(StyleMask.fullSizeContentView)
|
||||
self.isMovableByWindowBackground = true
|
||||
self.isMovable = false
|
||||
|
||||
self.layoutTrafficLights()
|
||||
|
||||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||||
|
||||
|
@ -102,14 +102,14 @@ class FlowyHoverContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
//
|
||||
abstract class HoverWidget extends StatefulWidget {
|
||||
const HoverWidget({Key? key}) : super(key: key);
|
||||
abstract class FlowyHoverWidget extends Widget {
|
||||
const FlowyHoverWidget({Key? key}) : super(key: key);
|
||||
|
||||
ValueNotifier<bool> get onFocus;
|
||||
ValueNotifier<bool>? get onFocus;
|
||||
}
|
||||
|
||||
class FlowyHover2 extends StatefulWidget {
|
||||
final Widget child;
|
||||
final FlowyHoverWidget child;
|
||||
final EdgeInsets contentPadding;
|
||||
const FlowyHover2({
|
||||
required this.child,
|
||||
@ -123,24 +123,30 @@ class FlowyHover2 extends StatefulWidget {
|
||||
|
||||
class _FlowyHover2State extends State<FlowyHover2> {
|
||||
late FlowyHoverState _hoverState;
|
||||
VoidCallback? _listenerFn;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_hoverState = FlowyHoverState();
|
||||
|
||||
if (widget.child is HoverWidget) {
|
||||
final hoverWidget = widget.child as HoverWidget;
|
||||
hoverWidget.onFocus.addListener(() {
|
||||
_hoverState.onFocus = hoverWidget.onFocus.value;
|
||||
});
|
||||
listener() {
|
||||
_hoverState.onFocus = widget.child.onFocus?.value ?? false;
|
||||
}
|
||||
|
||||
_listenerFn = listener;
|
||||
widget.child.onFocus?.addListener(listener);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hoverState.dispose();
|
||||
|
||||
if (_listenerFn != null) {
|
||||
widget.child.onFocus?.removeListener(_listenerFn!);
|
||||
_listenerFn = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -179,10 +185,7 @@ class _HoverBackground extends StatelessWidget {
|
||||
builder: (context, state, child) {
|
||||
if (state.onHover || state.onFocus) {
|
||||
return FlowyHoverContainer(
|
||||
style: HoverStyle(
|
||||
borderRadius: Corners.s6Border,
|
||||
hoverColor: theme.shader6,
|
||||
),
|
||||
style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
|
@ -132,9 +132,12 @@ class _RoundedInputFieldState extends State<RoundedInputField> {
|
||||
children.add(
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
widget.errorText,
|
||||
style: widget.style,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Text(
|
||||
widget.errorText,
|
||||
style: widget.style,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -392,20 +392,3 @@ class GridEventUpdateDateCell {
|
||||
}
|
||||
}
|
||||
|
||||
class GridEventGetDateCellData {
|
||||
CellIdentifierPayload request;
|
||||
GridEventGetDateCellData(this.request);
|
||||
|
||||
Future<Either<DateCellData, FlowyError>> send() {
|
||||
final request = FFIRequest.create()
|
||||
..event = GridEvent.GetDateCellData.toString()
|
||||
..payload = requestToBytes(this.request);
|
||||
|
||||
return Dispatch.asyncRequest(request)
|
||||
.then((bytesResult) => bytesResult.fold(
|
||||
(okBytes) => left(DateCellData.fromBuffer(okBytes)),
|
||||
(errBytes) => right(FlowyError.fromBuffer(errBytes)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1366,24 +1366,19 @@ class GridBlock extends $pb.GeneratedMessage {
|
||||
class Cell extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Cell', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
|
||||
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content')
|
||||
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
|
||||
..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
Cell._() : super();
|
||||
factory Cell({
|
||||
$core.String? fieldId,
|
||||
$core.String? content,
|
||||
$core.String? data,
|
||||
$core.List<$core.int>? data,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (fieldId != null) {
|
||||
_result.fieldId = fieldId;
|
||||
}
|
||||
if (content != null) {
|
||||
_result.content = content;
|
||||
}
|
||||
if (data != null) {
|
||||
_result.data = data;
|
||||
}
|
||||
@ -1420,22 +1415,13 @@ class Cell extends $pb.GeneratedMessage {
|
||||
void clearFieldId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get content => $_getSZ(1);
|
||||
$core.List<$core.int> get data => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set content($core.String v) { $_setString(1, v); }
|
||||
set data($core.List<$core.int> v) { $_setBytes(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasContent() => $_has(1);
|
||||
$core.bool hasData() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearContent() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.String get data => $_getSZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set data($core.String v) { $_setString(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasData() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearData() => clearField(3);
|
||||
void clearData() => clearField(2);
|
||||
}
|
||||
|
||||
class RepeatedCell extends $pb.GeneratedMessage {
|
||||
|
@ -31,6 +31,7 @@ class FieldType extends $pb.ProtobufEnum {
|
||||
static const FieldType SingleSelect = FieldType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SingleSelect');
|
||||
static const FieldType MultiSelect = FieldType._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MultiSelect');
|
||||
static const FieldType Checkbox = FieldType._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Checkbox');
|
||||
static const FieldType URL = FieldType._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'URL');
|
||||
|
||||
static const $core.List<FieldType> values = <FieldType> [
|
||||
RichText,
|
||||
@ -39,6 +40,7 @@ class FieldType extends $pb.ProtobufEnum {
|
||||
SingleSelect,
|
||||
MultiSelect,
|
||||
Checkbox,
|
||||
URL,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, FieldType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
|
@ -29,11 +29,12 @@ const FieldType$json = const {
|
||||
const {'1': 'SingleSelect', '2': 3},
|
||||
const {'1': 'MultiSelect', '2': 4},
|
||||
const {'1': 'Checkbox', '2': 5},
|
||||
const {'1': 'URL', '2': 6},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `FieldType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List fieldTypeDescriptor = $convert.base64Decode('CglGaWVsZFR5cGUSDAoIUmljaFRleHQQABIKCgZOdW1iZXIQARIMCghEYXRlVGltZRACEhAKDFNpbmdsZVNlbGVjdBADEg8KC011bHRpU2VsZWN0EAQSDAoIQ2hlY2tib3gQBQ==');
|
||||
final $typed_data.Uint8List fieldTypeDescriptor = $convert.base64Decode('CglGaWVsZFR5cGUSDAoIUmljaFRleHQQABIKCgZOdW1iZXIQARIMCghEYXRlVGltZRACEhAKDFNpbmdsZVNlbGVjdBADEg8KC011bHRpU2VsZWN0EAQSDAoIQ2hlY2tib3gQBRIHCgNVUkwQBg==');
|
||||
@$core.Deprecated('Use gridDescriptor instead')
|
||||
const Grid$json = const {
|
||||
'1': 'Grid',
|
||||
@ -289,13 +290,12 @@ const Cell$json = const {
|
||||
'1': 'Cell',
|
||||
'2': const [
|
||||
const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
|
||||
const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'},
|
||||
const {'1': 'data', '3': 3, '4': 1, '5': 9, '10': 'data'},
|
||||
const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSEgoEZGF0YRgDIAEoCVIEZGF0YQ==');
|
||||
final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhIKBGRhdGEYAiABKAxSBGRhdGE=');
|
||||
@$core.Deprecated('Use repeatedCellDescriptor instead')
|
||||
const RepeatedCell$json = const {
|
||||
'1': 'RepeatedCell',
|
||||
|
@ -33,7 +33,6 @@ class GridEvent extends $pb.ProtobufEnum {
|
||||
static const GridEvent UpdateCell = GridEvent._(71, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell');
|
||||
static const GridEvent UpdateSelectOptionCell = GridEvent._(72, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOptionCell');
|
||||
static const GridEvent UpdateDateCell = GridEvent._(80, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateDateCell');
|
||||
static const GridEvent GetDateCellData = GridEvent._(90, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetDateCellData');
|
||||
|
||||
static const $core.List<GridEvent> values = <GridEvent> [
|
||||
GetGridData,
|
||||
@ -59,7 +58,6 @@ class GridEvent extends $pb.ProtobufEnum {
|
||||
UpdateCell,
|
||||
UpdateSelectOptionCell,
|
||||
UpdateDateCell,
|
||||
GetDateCellData,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, GridEvent> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
|
@ -35,9 +35,8 @@ const GridEvent$json = const {
|
||||
const {'1': 'UpdateCell', '2': 71},
|
||||
const {'1': 'UpdateSelectOptionCell', '2': 72},
|
||||
const {'1': 'UpdateDateCell', '2': 80},
|
||||
const {'1': 'GetDateCellData', '2': 90},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFASEwoPR2V0RGF0ZUNlbGxEYXRhEFo=');
|
||||
final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFA=');
|
||||
|
@ -5,6 +5,7 @@ export './dart_notification.pb.dart';
|
||||
export './selection_type_option.pb.dart';
|
||||
export './row_entities.pb.dart';
|
||||
export './cell_entities.pb.dart';
|
||||
export './url_type_option.pb.dart';
|
||||
export './checkbox_type_option.pb.dart';
|
||||
export './event_map.pb.dart';
|
||||
export './text_type_option.pb.dart';
|
||||
|
@ -11,17 +11,17 @@ import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class RichTextTypeOption extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RichTextTypeOption', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'format')
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
RichTextTypeOption._() : super();
|
||||
factory RichTextTypeOption({
|
||||
$core.String? format,
|
||||
$core.String? data,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (format != null) {
|
||||
_result.format = format;
|
||||
if (data != null) {
|
||||
_result.data = data;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
@ -47,12 +47,12 @@ class RichTextTypeOption extends $pb.GeneratedMessage {
|
||||
static RichTextTypeOption? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get format => $_getSZ(0);
|
||||
$core.String get data => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set format($core.String v) { $_setString(0, v); }
|
||||
set data($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasFormat() => $_has(0);
|
||||
$core.bool hasData() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearFormat() => clearField(1);
|
||||
void clearData() => clearField(1);
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,9 @@ import 'dart:typed_data' as $typed_data;
|
||||
const RichTextTypeOption$json = const {
|
||||
'1': 'RichTextTypeOption',
|
||||
'2': const [
|
||||
const {'1': 'format', '3': 1, '4': 1, '5': 9, '10': 'format'},
|
||||
const {'1': 'data', '3': 1, '4': 1, '5': 9, '10': 'data'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `RichTextTypeOption`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List richTextTypeOptionDescriptor = $convert.base64Decode('ChJSaWNoVGV4dFR5cGVPcHRpb24SFgoGZm9ybWF0GAEgASgJUgZmb3JtYXQ=');
|
||||
final $typed_data.Uint8List richTextTypeOptionDescriptor = $convert.base64Decode('ChJSaWNoVGV4dFR5cGVPcHRpb24SEgoEZGF0YRgBIAEoCVIEZGF0YQ==');
|
||||
|
@ -0,0 +1,119 @@
|
||||
///
|
||||
// Generated code. Do not modify.
|
||||
// source: url_type_option.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class URLTypeOption extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'URLTypeOption', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
URLTypeOption._() : super();
|
||||
factory URLTypeOption({
|
||||
$core.String? data,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (data != null) {
|
||||
_result.data = data;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory URLTypeOption.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory URLTypeOption.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
URLTypeOption clone() => URLTypeOption()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
URLTypeOption copyWith(void Function(URLTypeOption) updates) => super.copyWith((message) => updates(message as URLTypeOption)) as URLTypeOption; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static URLTypeOption create() => URLTypeOption._();
|
||||
URLTypeOption createEmptyInstance() => create();
|
||||
static $pb.PbList<URLTypeOption> createRepeated() => $pb.PbList<URLTypeOption>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static URLTypeOption getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<URLTypeOption>(create);
|
||||
static URLTypeOption? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get data => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set data($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasData() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearData() => clearField(1);
|
||||
}
|
||||
|
||||
class URLCellData extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'URLCellData', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'url')
|
||||
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
URLCellData._() : super();
|
||||
factory URLCellData({
|
||||
$core.String? url,
|
||||
$core.String? content,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (url != null) {
|
||||
_result.url = url;
|
||||
}
|
||||
if (content != null) {
|
||||
_result.content = content;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory URLCellData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory URLCellData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
URLCellData clone() => URLCellData()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
URLCellData copyWith(void Function(URLCellData) updates) => super.copyWith((message) => updates(message as URLCellData)) as URLCellData; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static URLCellData create() => URLCellData._();
|
||||
URLCellData createEmptyInstance() => create();
|
||||
static $pb.PbList<URLCellData> createRepeated() => $pb.PbList<URLCellData>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static URLCellData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<URLCellData>(create);
|
||||
static URLCellData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get url => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set url($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasUrl() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearUrl() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get content => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set content($core.String v) { $_setString(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasContent() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearContent() => clearField(2);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
///
|
||||
// Generated code. Do not modify.
|
||||
// source: url_type_option.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
@ -0,0 +1,31 @@
|
||||
///
|
||||
// Generated code. Do not modify.
|
||||
// source: url_type_option.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
|
||||
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:convert' as $convert;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
@$core.Deprecated('Use uRLTypeOptionDescriptor instead')
|
||||
const URLTypeOption$json = const {
|
||||
'1': 'URLTypeOption',
|
||||
'2': const [
|
||||
const {'1': 'data', '3': 1, '4': 1, '5': 9, '10': 'data'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `URLTypeOption`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List uRLTypeOptionDescriptor = $convert.base64Decode('Cg1VUkxUeXBlT3B0aW9uEhIKBGRhdGEYASABKAlSBGRhdGE=');
|
||||
@$core.Deprecated('Use uRLCellDataDescriptor instead')
|
||||
const URLCellData$json = const {
|
||||
'1': 'URLCellData',
|
||||
'2': const [
|
||||
const {'1': 'url', '3': 1, '4': 1, '5': 9, '10': 'url'},
|
||||
const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `URLCellData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List uRLCellDataDescriptor = $convert.base64Decode('CgtVUkxDZWxsRGF0YRIQCgN1cmwYASABKAlSA3VybBIYCgdjb250ZW50GAIgASgJUgdjb250ZW50');
|
@ -0,0 +1,9 @@
|
||||
///
|
||||
// Generated code. Do not modify.
|
||||
// source: url_type_option.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
|
||||
|
||||
export 'url_type_option.pb.dart';
|
||||
|
@ -7,28 +7,28 @@ packages:
|
||||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "31.0.0"
|
||||
version: "38.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.8.0"
|
||||
version: "3.4.1"
|
||||
animations:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: animations
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.3"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.3.1"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -42,14 +42,14 @@ packages:
|
||||
name: bloc
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.0.2"
|
||||
version: "8.0.3"
|
||||
bloc_test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: bloc_test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.0.2"
|
||||
version: "9.0.3"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -63,7 +63,7 @@ packages:
|
||||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.3.0"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -77,21 +77,21 @@ packages:
|
||||
name: build_daemon
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.1.0"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.0.8"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
version: "2.1.11"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -112,7 +112,7 @@ packages:
|
||||
name: built_value
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
version: "8.3.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -134,13 +134,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
clipboard:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -168,7 +161,7 @@ packages:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
version: "1.16.0"
|
||||
connectivity_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -182,14 +175,14 @@ packages:
|
||||
name: connectivity_plus_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
connectivity_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_plus_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.2"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -224,21 +217,21 @@ packages:
|
||||
name: coverage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.3.2"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.2"
|
||||
version: "0.3.3+1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -259,7 +252,7 @@ packages:
|
||||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.2.3"
|
||||
dartz:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -273,14 +266,14 @@ packages:
|
||||
name: dbus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.8"
|
||||
version: "0.7.3"
|
||||
device_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
version: "3.2.3"
|
||||
device_info_plus_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -294,7 +287,7 @@ packages:
|
||||
name: device_info_plus_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.2.3"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -329,7 +322,7 @@ packages:
|
||||
name: easy_localization
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1-dev"
|
||||
version: "3.0.1"
|
||||
easy_logger:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -357,14 +350,14 @@ packages:
|
||||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.2.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -378,7 +371,7 @@ packages:
|
||||
name: fixnum
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.0.1"
|
||||
flowy_infra:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -439,14 +432,14 @@ packages:
|
||||
name: flutter_inappwebview
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.3.2"
|
||||
version: "5.4.3+7"
|
||||
flutter_keyboard_visibility:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_keyboard_visibility
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
version: "5.2.0"
|
||||
flutter_keyboard_visibility_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -479,13 +472,13 @@ packages:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.6"
|
||||
flutter_quill:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: dbc309f5e382963fa0122409a0aaf8e1417d51c1
|
||||
resolved-ref: dbc309f5e382963fa0122409a0aaf8e1417d51c1
|
||||
ref: "306fd78b7a134abdde0fed6be67f59e8a6068509"
|
||||
resolved-ref: "306fd78b7a134abdde0fed6be67f59e8a6068509"
|
||||
url: "https://github.com/appflowy/flutter-quill.git"
|
||||
source: git
|
||||
version: "2.0.13"
|
||||
@ -519,21 +512,21 @@ packages:
|
||||
name: freezed
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.14.5"
|
||||
version: "2.0.3+1"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freezed_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.14.3"
|
||||
version: "2.0.3"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
get_it:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -582,42 +575,56 @@ packages:
|
||||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.2.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "4.0.1"
|
||||
i18n_extension:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: i18n_extension
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.1"
|
||||
image_picker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.4+4"
|
||||
version: "0.8.5+3"
|
||||
image_picker_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.4+13"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_for_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.1.8"
|
||||
image_picker_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.5+5"
|
||||
image_picker_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
version: "2.5.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -645,14 +652,14 @@ packages:
|
||||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.3"
|
||||
version: "0.6.4"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
version: "4.5.0"
|
||||
linked_scroll_controller:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -666,7 +673,7 @@ packages:
|
||||
name: lint
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
version: "1.8.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -680,7 +687,7 @@ packages:
|
||||
name: loading_indicator
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.1.0"
|
||||
logger:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -708,7 +715,7 @@ packages:
|
||||
name: material_color_utilities
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
version: "0.1.4"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -722,14 +729,14 @@ packages:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.0.2"
|
||||
mocktail:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mocktail
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.3.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -743,7 +750,7 @@ packages:
|
||||
name: nm
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
version: "0.5.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -764,14 +771,14 @@ packages:
|
||||
name: package_info_plus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.4.2"
|
||||
package_info_plus_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.0.5"
|
||||
package_info_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -792,21 +799,21 @@ packages:
|
||||
name: package_info_plus_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.0.5"
|
||||
package_info_plus_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.0.5"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
version: "1.8.1"
|
||||
path_drawing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -827,49 +834,49 @@ packages:
|
||||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
version: "2.0.10"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.11"
|
||||
version: "2.0.14"
|
||||
path_provider_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
version: "2.0.9"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.1.6"
|
||||
path_provider_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.6"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.6"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -883,7 +890,7 @@ packages:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
version: "5.0.0"
|
||||
photo_view:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -932,14 +939,14 @@ packages:
|
||||
name: provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
version: "6.0.3"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.1"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -953,49 +960,49 @@ packages:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1+1"
|
||||
version: "3.1.0"
|
||||
reorderables:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: reorderables
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.3"
|
||||
version: "0.5.0"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.12"
|
||||
version: "2.0.15"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
version: "2.0.12"
|
||||
shared_preferences_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.9"
|
||||
version: "2.1.1"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
version: "2.1.1"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.4"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1009,21 +1016,21 @@ packages:
|
||||
name: shared_preferences_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
version: "2.1.1"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1070,7 +1077,7 @@ packages:
|
||||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.2"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1091,7 +1098,7 @@ packages:
|
||||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
version: "1.8.2"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1161,28 +1168,28 @@ packages:
|
||||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.19.5"
|
||||
version: "1.21.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.8"
|
||||
version: "0.4.9"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.9"
|
||||
version: "0.4.13"
|
||||
textfield_tags:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: textfield_tags
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.0+1"
|
||||
textstyle_extensions:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1217,7 +1224,7 @@ packages:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.1"
|
||||
universal_platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1231,35 +1238,35 @@ packages:
|
||||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.18"
|
||||
version: "6.1.2"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.14"
|
||||
version: "6.0.17"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.14"
|
||||
version: "6.0.17"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "3.0.1"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "3.0.1"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1273,56 +1280,70 @@ packages:
|
||||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.0.11"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "3.0.1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.6"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
video_player:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.11"
|
||||
version: "2.4.2"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.4"
|
||||
video_player_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_avfoundation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.4"
|
||||
video_player_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
version: "5.1.2"
|
||||
video_player_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.0.10"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.5.0"
|
||||
version: "8.3.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1336,21 +1357,21 @@ packages:
|
||||
name: web_socket_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.2.0"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
version: "2.6.1"
|
||||
window_size:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1366,28 +1387,28 @@ packages:
|
||||
name: xdg_directories
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.2.0+1"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
version: "5.4.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.1.1"
|
||||
youtube_player_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: youtube_player_flutter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
version: "8.1.0"
|
||||
sdks:
|
||||
dart: ">=2.15.0-116.0.dev <3.0.0"
|
||||
flutter: ">=2.5.0"
|
||||
dart: ">=2.17.0 <3.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
|
@ -18,7 +18,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.15.0-116.0.dev <3.0.0"
|
||||
sdk: ">=2.16.2 <3.0.0"
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
@ -40,7 +40,7 @@ dependencies:
|
||||
flutter_quill:
|
||||
git:
|
||||
url: https://github.com/appflowy/flutter-quill.git
|
||||
ref: dbc309f5e382963fa0122409a0aaf8e1417d51c1
|
||||
ref: 306fd78b7a134abdde0fed6be67f59e8a6068509
|
||||
|
||||
# third party packages
|
||||
intl: ^0.17.0
|
||||
@ -74,7 +74,7 @@ dependencies:
|
||||
device_info_plus: ^3.2.1
|
||||
fluttertoast: ^8.0.8
|
||||
table_calendar: ^3.0.5
|
||||
reorderables:
|
||||
reorderables: ^0.5.0
|
||||
linked_scroll_controller: ^0.2.0
|
||||
|
||||
dev_dependencies:
|
||||
|
2
frontend/rust-lib/Cargo.lock
generated
2
frontend/rust-lib/Cargo.lock
generated
@ -928,6 +928,7 @@ dependencies = [
|
||||
"dart-notify",
|
||||
"dashmap",
|
||||
"diesel",
|
||||
"fancy-regex",
|
||||
"flowy-database",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
@ -953,6 +954,7 @@ dependencies = [
|
||||
"strum_macros",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
errors::FlowyResult,
|
||||
event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
|
||||
services::{
|
||||
folder_editor::ClientFolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
|
||||
folder_editor::FolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
|
||||
TrashController, ViewController, WorkspaceController,
|
||||
},
|
||||
};
|
||||
@ -61,7 +61,7 @@ pub struct FolderManager {
|
||||
pub(crate) view_controller: Arc<ViewController>,
|
||||
pub(crate) trash_controller: Arc<TrashController>,
|
||||
web_socket: Arc<dyn RevisionWebSocket>,
|
||||
folder_editor: Arc<TokioRwLock<Option<Arc<ClientFolderEditor>>>>,
|
||||
folder_editor: Arc<TokioRwLock<Option<Arc<FolderEditor>>>>,
|
||||
data_processors: ViewDataProcessorMap,
|
||||
}
|
||||
|
||||
@ -166,8 +166,7 @@ impl FolderManager {
|
||||
let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), disk_cache));
|
||||
let rev_manager = RevisionManager::new(user_id, folder_id.as_ref(), rev_persistence);
|
||||
|
||||
let folder_editor =
|
||||
ClientFolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?;
|
||||
let folder_editor = FolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?;
|
||||
*self.folder_editor.write().await = Some(Arc::new(folder_editor));
|
||||
|
||||
let _ = self.app_controller.initialize()?;
|
||||
@ -228,7 +227,7 @@ impl DefaultFolderBuilder {
|
||||
|
||||
#[cfg(feature = "flowy_unit_test")]
|
||||
impl FolderManager {
|
||||
pub async fn folder_editor(&self) -> Arc<ClientFolderEditor> {
|
||||
pub async fn folder_editor(&self) -> Arc<FolderEditor> {
|
||||
self.folder_editor.read().await.clone().unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ pub(crate) async fn update_app_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, app_controller, view_controller))]
|
||||
#[tracing::instrument(level = "trace", skip(data, app_controller, view_controller))]
|
||||
pub(crate) async fn read_app_handler(
|
||||
data: Data<AppId>,
|
||||
app_controller: AppData<Arc<AppController>>,
|
||||
|
@ -17,7 +17,7 @@ use lib_ot::core::PlainTextAttributes;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct ClientFolderEditor {
|
||||
pub struct FolderEditor {
|
||||
user_id: String,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) folder_id: FolderId,
|
||||
@ -27,7 +27,7 @@ pub struct ClientFolderEditor {
|
||||
ws_manager: Arc<flowy_revision::RevisionWebSocketManager>,
|
||||
}
|
||||
|
||||
impl ClientFolderEditor {
|
||||
impl FolderEditor {
|
||||
#[allow(unused_variables)]
|
||||
pub async fn new(
|
||||
user_id: &str,
|
||||
@ -129,7 +129,7 @@ impl RevisionCloudService for FolderRevisionCloudService {
|
||||
}
|
||||
|
||||
#[cfg(feature = "flowy_unit_test")]
|
||||
impl ClientFolderEditor {
|
||||
impl FolderEditor {
|
||||
pub fn rev_manager(&self) -> Arc<RevisionManager> {
|
||||
self.rev_manager.clone()
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ mod version_2;
|
||||
use crate::{
|
||||
event_map::WorkspaceDatabase,
|
||||
manager::FolderId,
|
||||
services::{folder_editor::ClientFolderEditor, persistence::migration::FolderMigration},
|
||||
services::{folder_editor::FolderEditor, persistence::migration::FolderMigration},
|
||||
};
|
||||
use flowy_database::ConnectionPool;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
@ -50,14 +50,11 @@ pub trait FolderPersistenceTransaction {
|
||||
|
||||
pub struct FolderPersistence {
|
||||
database: Arc<dyn WorkspaceDatabase>,
|
||||
folder_editor: Arc<RwLock<Option<Arc<ClientFolderEditor>>>>,
|
||||
folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>,
|
||||
}
|
||||
|
||||
impl FolderPersistence {
|
||||
pub fn new(
|
||||
database: Arc<dyn WorkspaceDatabase>,
|
||||
folder_editor: Arc<RwLock<Option<Arc<ClientFolderEditor>>>>,
|
||||
) -> Self {
|
||||
pub fn new(database: Arc<dyn WorkspaceDatabase>, folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>) -> Self {
|
||||
Self {
|
||||
database,
|
||||
folder_editor,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::services::{
|
||||
folder_editor::ClientFolderEditor,
|
||||
folder_editor::FolderEditor,
|
||||
persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset},
|
||||
};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
@ -11,7 +11,7 @@ use flowy_folder_data_model::entities::{
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
impl FolderPersistenceTransaction for ClientFolderEditor {
|
||||
impl FolderPersistenceTransaction for FolderEditor {
|
||||
fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
|
||||
if let Some(change) = self.folder.write().create_workspace(workspace)? {
|
||||
let _ = self.apply_change(change)?;
|
||||
|
@ -129,7 +129,7 @@ impl ViewController {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub(crate) fn set_latest_view(&self, view_id: &str) -> Result<(), FlowyError> {
|
||||
KV::set_str(LATEST_VIEW_ID, view_id.to_owned());
|
||||
Ok(())
|
||||
@ -193,7 +193,7 @@ impl ViewController {
|
||||
}
|
||||
|
||||
// belong_to_id will be the app_id or view_id.
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, FlowyError> {
|
||||
self.persistence
|
||||
.begin_transaction(|transaction| {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use flowy_folder::event_map::FolderEvent::*;
|
||||
use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEditor};
|
||||
use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor};
|
||||
use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId};
|
||||
use flowy_folder_data_model::entities::workspace::WorkspaceId;
|
||||
use flowy_folder_data_model::entities::{
|
||||
@ -125,7 +125,7 @@ impl FolderTest {
|
||||
|
||||
pub async fn run_script(&mut self, script: FolderScript) {
|
||||
let sdk = &self.sdk;
|
||||
let folder_editor: Arc<ClientFolderEditor> = sdk.folder_manager.folder_editor().await;
|
||||
let folder_editor: Arc<FolderEditor> = sdk.folder_manager.folder_editor().await;
|
||||
let rev_manager = folder_editor.rev_manager();
|
||||
let cache = rev_manager.revision_cache().await;
|
||||
|
||||
|
@ -35,6 +35,8 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = {version = "1.0"}
|
||||
serde_repr = "0.1"
|
||||
indexmap = {version = "1.8.1", features = ["serde"]}
|
||||
fancy-regex = "0.10.0"
|
||||
url = { version = "2"}
|
||||
|
||||
[dev-dependencies]
|
||||
flowy-test = { path = "../flowy-test" }
|
||||
|
@ -2,7 +2,7 @@
|
||||
proto_crates = [
|
||||
"src/event_map.rs",
|
||||
"src/services/field/type_options",
|
||||
"src/services/entities",
|
||||
"src/entities",
|
||||
"src/dart_notification.rs"
|
||||
]
|
||||
event_files = ["src/event_map.rs"]
|
@ -1,4 +1,4 @@
|
||||
use crate::services::entities::{FieldIdentifier, FieldIdentifierPayload};
|
||||
use crate::entities::{FieldIdentifier, FieldIdentifierPayload};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::*;
|
||||
use crate::manager::GridManager;
|
||||
use crate::services::entities::*;
|
||||
use crate::services::field::type_options::*;
|
||||
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_json_str};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
@ -7,7 +7,7 @@ use flowy_grid_data_model::entities::*;
|
||||
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn get_grid_data_handler(
|
||||
data: Data<GridId>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -34,7 +34,7 @@ pub(crate) async fn get_grid_blocks_handler(
|
||||
data_result(repeated_grid_block)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn get_fields_handler(
|
||||
data: Data<QueryFieldPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -47,7 +47,7 @@ pub(crate) async fn get_fields_handler(
|
||||
data_result(repeated_field)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn update_field_handler(
|
||||
data: Data<FieldChangesetPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -58,7 +58,7 @@ pub(crate) async fn update_field_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn insert_field_handler(
|
||||
data: Data<InsertFieldPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -69,7 +69,7 @@ pub(crate) async fn insert_field_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn update_field_type_option_handler(
|
||||
data: Data<UpdateFieldTypeOptionPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -82,7 +82,7 @@ pub(crate) async fn update_field_type_option_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn delete_field_handler(
|
||||
data: Data<FieldIdentifierPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -93,7 +93,7 @@ pub(crate) async fn delete_field_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn switch_to_field_handler(
|
||||
data: Data<EditFieldPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -120,7 +120,7 @@ pub(crate) async fn switch_to_field_handler(
|
||||
data_result(data)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn duplicate_field_handler(
|
||||
data: Data<FieldIdentifierPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -132,7 +132,7 @@ pub(crate) async fn duplicate_field_handler(
|
||||
}
|
||||
|
||||
/// Return the FieldTypeOptionData if the Field exists otherwise return record not found error.
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn get_field_type_option_data_handler(
|
||||
data: Data<EditFieldPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -154,7 +154,7 @@ pub(crate) async fn get_field_type_option_data_handler(
|
||||
}
|
||||
|
||||
/// Create FieldMeta and save it. Return the FieldTypeOptionData.
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn create_field_type_option_data_handler(
|
||||
data: Data<EditFieldPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -171,7 +171,7 @@ pub(crate) async fn create_field_type_option_data_handler(
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn move_item_handler(
|
||||
data: Data<MoveItemPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -252,7 +252,7 @@ pub(crate) async fn get_cell_handler(
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn update_cell_handler(
|
||||
data: Data<CellChangeset>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -263,28 +263,7 @@ pub(crate) async fn update_cell_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn get_date_cell_data_handler(
|
||||
data: Data<CellIdentifierPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> DataResult<DateCellData, FlowyError> {
|
||||
let params: CellIdentifier = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(¶ms.grid_id)?;
|
||||
match editor.get_field_meta(¶ms.field_id).await {
|
||||
None => {
|
||||
tracing::error!("Can't find the corresponding field with id: {}", params.field_id);
|
||||
data_result(DateCellData::default())
|
||||
}
|
||||
Some(field_meta) => {
|
||||
let cell_meta = editor.get_cell_meta(¶ms.row_id, ¶ms.field_id).await?;
|
||||
let type_option = DateTypeOption::from(&field_meta);
|
||||
let date_cell_data = type_option.make_date_cell_data(&cell_meta)?;
|
||||
data_result(date_cell_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn new_select_option_handler(
|
||||
data: Data<CreateSelectOptionPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -301,7 +280,7 @@ pub(crate) async fn new_select_option_handler(
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn update_select_option_handler(
|
||||
data: Data<SelectOptionChangesetPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -341,7 +320,7 @@ pub(crate) async fn update_select_option_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn get_select_option_handler(
|
||||
data: Data<CellIdentifierPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -350,7 +329,7 @@ pub(crate) async fn get_select_option_handler(
|
||||
let editor = manager.get_grid_editor(¶ms.grid_id)?;
|
||||
match editor.get_field_meta(¶ms.field_id).await {
|
||||
None => {
|
||||
tracing::error!("Can't find the corresponding field with id: {}", params.field_id);
|
||||
tracing::error!("Can't find the select option field with id: {}", params.field_id);
|
||||
data_result(SelectOptionCellData::default())
|
||||
}
|
||||
Some(field_meta) => {
|
||||
@ -362,7 +341,7 @@ pub(crate) async fn get_select_option_handler(
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn update_select_option_cell_handler(
|
||||
data: Data<SelectOptionCellChangesetPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
@ -373,7 +352,7 @@ pub(crate) async fn update_select_option_cell_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn update_date_cell_handler(
|
||||
data: Data<DateChangesetPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
|
@ -29,7 +29,6 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
// Cell
|
||||
.event(GridEvent::GetCell, get_cell_handler)
|
||||
.event(GridEvent::UpdateCell, update_cell_handler)
|
||||
.event(GridEvent::GetDateCellData, get_date_cell_data_handler)
|
||||
// SelectOption
|
||||
.event(GridEvent::NewSelectOption, new_select_option_handler)
|
||||
.event(GridEvent::UpdateSelectOption, update_select_option_handler)
|
||||
@ -112,7 +111,4 @@ pub enum GridEvent {
|
||||
|
||||
#[event(input = "DateChangesetPayload")]
|
||||
UpdateDateCell = 80,
|
||||
|
||||
#[event(input = "CellIdentifierPayload", output = "DateCellData")]
|
||||
GetDateCellData = 90,
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ pub mod event_map;
|
||||
pub mod manager;
|
||||
|
||||
mod dart_notification;
|
||||
pub mod entities;
|
||||
mod protobuf;
|
||||
pub mod services;
|
||||
pub mod util;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::services::grid_editor::ClientGridEditor;
|
||||
use crate::services::persistence::block_index::BlockIndexPersistence;
|
||||
use crate::services::grid_editor::GridMetaEditor;
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::persistence::kv::GridKVPersistence;
|
||||
use crate::services::persistence::GridDatabase;
|
||||
use bytes::Bytes;
|
||||
@ -20,9 +20,9 @@ pub trait GridUser: Send + Sync {
|
||||
}
|
||||
|
||||
pub struct GridManager {
|
||||
editor_map: Arc<GridEditorMap>,
|
||||
editor_map: Arc<DashMap<String, Arc<GridMetaEditor>>>,
|
||||
grid_user: Arc<dyn GridUser>,
|
||||
block_index_persistence: Arc<BlockIndexPersistence>,
|
||||
block_index_cache: Arc<BlockIndexCache>,
|
||||
#[allow(dead_code)]
|
||||
kv_persistence: Arc<GridKVPersistence>,
|
||||
}
|
||||
@ -33,14 +33,14 @@ impl GridManager {
|
||||
_rev_web_socket: Arc<dyn RevisionWebSocket>,
|
||||
database: Arc<dyn GridDatabase>,
|
||||
) -> Self {
|
||||
let grid_editors = Arc::new(GridEditorMap::new());
|
||||
let grid_editors = Arc::new(DashMap::new());
|
||||
let kv_persistence = Arc::new(GridKVPersistence::new(database.clone()));
|
||||
let block_index_persistence = Arc::new(BlockIndexPersistence::new(database));
|
||||
let block_index_persistence = Arc::new(BlockIndexCache::new(database));
|
||||
Self {
|
||||
editor_map: grid_editors,
|
||||
grid_user,
|
||||
kv_persistence,
|
||||
block_index_persistence,
|
||||
block_index_cache: block_index_persistence,
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ impl GridManager {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)]
|
||||
pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<ClientGridEditor>> {
|
||||
pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<GridMetaEditor>> {
|
||||
let grid_id = grid_id.as_ref();
|
||||
tracing::Span::current().record("grid_id", &grid_id);
|
||||
self.get_or_create_grid_editor(grid_id).await
|
||||
@ -90,23 +90,27 @@ impl GridManager {
|
||||
}
|
||||
|
||||
// #[tracing::instrument(level = "debug", skip(self), err)]
|
||||
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
|
||||
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
|
||||
match self.editor_map.get(grid_id) {
|
||||
None => Err(FlowyError::internal().context("Should call open_grid function first")),
|
||||
Some(editor) => Ok(editor),
|
||||
Some(editor) => Ok(editor.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
|
||||
async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
|
||||
match self.editor_map.get(grid_id) {
|
||||
None => {
|
||||
tracing::trace!("Create grid editor with id: {}", grid_id);
|
||||
let db_pool = self.grid_user.db_pool()?;
|
||||
let editor = self.make_grid_editor(grid_id, db_pool).await?;
|
||||
self.editor_map.insert(grid_id, &editor);
|
||||
|
||||
if self.editor_map.contains_key(grid_id) {
|
||||
tracing::warn!("Grid:{} already exists in cache", grid_id);
|
||||
}
|
||||
self.editor_map.insert(grid_id.to_string(), editor.clone());
|
||||
Ok(editor)
|
||||
}
|
||||
Some(editor) => Ok(editor),
|
||||
Some(editor) => Ok(editor.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,11 +119,10 @@ impl GridManager {
|
||||
&self,
|
||||
grid_id: &str,
|
||||
pool: Arc<ConnectionPool>,
|
||||
) -> Result<Arc<ClientGridEditor>, FlowyError> {
|
||||
) -> Result<Arc<GridMetaEditor>, FlowyError> {
|
||||
let user = self.grid_user.clone();
|
||||
let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
|
||||
let grid_editor =
|
||||
ClientGridEditor::new(grid_id, user, rev_manager, self.block_index_persistence.clone()).await?;
|
||||
let grid_editor = GridMetaEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?;
|
||||
Ok(grid_editor)
|
||||
}
|
||||
|
||||
@ -145,31 +148,6 @@ impl GridManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GridEditorMap {
|
||||
inner: DashMap<String, Arc<ClientGridEditor>>,
|
||||
}
|
||||
|
||||
impl GridEditorMap {
|
||||
fn new() -> Self {
|
||||
Self { inner: DashMap::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&self, grid_id: &str, grid_editor: &Arc<ClientGridEditor>) {
|
||||
if self.inner.contains_key(grid_id) {
|
||||
tracing::warn!("Grid:{} already exists in cache", grid_id);
|
||||
}
|
||||
self.inner.insert(grid_id.to_string(), grid_editor.clone());
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, grid_id: &str) -> Option<Arc<ClientGridEditor>> {
|
||||
Some(self.inner.get(grid_id)?.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&self, grid_id: &str) {
|
||||
self.inner.remove(grid_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn make_grid_view_data(
|
||||
user_id: &str,
|
||||
view_id: &str,
|
||||
@ -192,9 +170,7 @@ pub async fn make_grid_view_data(
|
||||
|
||||
// Indexing the block's rows
|
||||
build_context.block_meta_data.rows.iter().for_each(|row| {
|
||||
let _ = grid_manager
|
||||
.block_index_persistence
|
||||
.insert_or_update(&row.block_id, &row.id);
|
||||
let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id);
|
||||
});
|
||||
|
||||
// Create grid's block
|
||||
|
@ -48,7 +48,6 @@ pub enum GridEvent {
|
||||
UpdateCell = 71,
|
||||
UpdateSelectOptionCell = 72,
|
||||
UpdateDateCell = 80,
|
||||
GetDateCellData = 90,
|
||||
}
|
||||
|
||||
impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
@ -81,7 +80,6 @@ impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
71 => ::std::option::Option::Some(GridEvent::UpdateCell),
|
||||
72 => ::std::option::Option::Some(GridEvent::UpdateSelectOptionCell),
|
||||
80 => ::std::option::Option::Some(GridEvent::UpdateDateCell),
|
||||
90 => ::std::option::Option::Some(GridEvent::GetDateCellData),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
@ -111,7 +109,6 @@ impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
GridEvent::UpdateCell,
|
||||
GridEvent::UpdateSelectOptionCell,
|
||||
GridEvent::UpdateDateCell,
|
||||
GridEvent::GetDateCellData,
|
||||
];
|
||||
values
|
||||
}
|
||||
@ -140,7 +137,7 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0fevent_map.proto*\xdc\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
|
||||
\n\x0fevent_map.proto*\xc7\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
|
||||
\0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
|
||||
\x0bUpdateField\x10\x0b\x12\x19\n\x15UpdateFieldTypeOption\x10\x0c\x12\
|
||||
\x0f\n\x0bInsertField\x10\r\x12\x0f\n\x0bDeleteField\x10\x0e\x12\x11\n\r\
|
||||
@ -151,7 +148,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteR\
|
||||
ow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\
|
||||
\n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSelectOptionCell\x10H\x12\x12\n\
|
||||
\x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDateCellData\x10Zb\x06proto3\
|
||||
\x0eUpdateDateCell\x10Pb\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -19,6 +19,9 @@ pub use row_entities::*;
|
||||
mod cell_entities;
|
||||
pub use cell_entities::*;
|
||||
|
||||
mod url_type_option;
|
||||
pub use url_type_option::*;
|
||||
|
||||
mod checkbox_type_option;
|
||||
pub use checkbox_type_option::*;
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
#[derive(PartialEq,Clone,Default)]
|
||||
pub struct RichTextTypeOption {
|
||||
// message fields
|
||||
pub format: ::std::string::String,
|
||||
pub data: ::std::string::String,
|
||||
// special fields
|
||||
pub unknown_fields: ::protobuf::UnknownFields,
|
||||
pub cached_size: ::protobuf::CachedSize,
|
||||
@ -43,30 +43,30 @@ impl RichTextTypeOption {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
// string format = 1;
|
||||
// string data = 1;
|
||||
|
||||
|
||||
pub fn get_format(&self) -> &str {
|
||||
&self.format
|
||||
pub fn get_data(&self) -> &str {
|
||||
&self.data
|
||||
}
|
||||
pub fn clear_format(&mut self) {
|
||||
self.format.clear();
|
||||
pub fn clear_data(&mut self) {
|
||||
self.data.clear();
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_format(&mut self, v: ::std::string::String) {
|
||||
self.format = v;
|
||||
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_format(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.format
|
||||
pub fn mut_data(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.data
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_format(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.format, ::std::string::String::new())
|
||||
pub fn take_data(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.data, ::std::string::String::new())
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ impl ::protobuf::Message for RichTextTypeOption {
|
||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
||||
match field_number {
|
||||
1 => {
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.format)?;
|
||||
::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())?;
|
||||
@ -94,8 +94,8 @@ impl ::protobuf::Message for RichTextTypeOption {
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u32 {
|
||||
let mut my_size = 0;
|
||||
if !self.format.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.format);
|
||||
if !self.data.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.data);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
||||
self.cached_size.set(my_size);
|
||||
@ -103,8 +103,8 @@ impl ::protobuf::Message for RichTextTypeOption {
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
||||
if !self.format.is_empty() {
|
||||
os.write_string(1, &self.format)?;
|
||||
if !self.data.is_empty() {
|
||||
os.write_string(1, &self.data)?;
|
||||
}
|
||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
@ -145,9 +145,9 @@ impl ::protobuf::Message for RichTextTypeOption {
|
||||
descriptor.get(|| {
|
||||
let mut fields = ::std::vec::Vec::new();
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"format",
|
||||
|m: &RichTextTypeOption| { &m.format },
|
||||
|m: &mut RichTextTypeOption| { &mut m.format },
|
||||
"data",
|
||||
|m: &RichTextTypeOption| { &m.data },
|
||||
|m: &mut RichTextTypeOption| { &mut m.data },
|
||||
));
|
||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<RichTextTypeOption>(
|
||||
"RichTextTypeOption",
|
||||
@ -165,7 +165,7 @@ impl ::protobuf::Message for RichTextTypeOption {
|
||||
|
||||
impl ::protobuf::Clear for RichTextTypeOption {
|
||||
fn clear(&mut self) {
|
||||
self.format.clear();
|
||||
self.data.clear();
|
||||
self.unknown_fields.clear();
|
||||
}
|
||||
}
|
||||
@ -183,8 +183,8 @@ impl ::protobuf::reflect::ProtobufValue for RichTextTypeOption {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x16text_type_option.proto\",\n\x12RichTextTypeOption\x12\x16\n\x06for\
|
||||
mat\x18\x01\x20\x01(\tR\x06formatb\x06proto3\
|
||||
\n\x16text_type_option.proto\"(\n\x12RichTextTypeOption\x12\x12\n\x04dat\
|
||||
a\x18\x01\x20\x01(\tR\x04datab\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -0,0 +1,403 @@
|
||||
// This file is generated by rust-protobuf 2.25.2. Do not edit
|
||||
// @generated
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/702
|
||||
#![allow(unknown_lints)]
|
||||
#![allow(clippy::all)]
|
||||
|
||||
#![allow(unused_attributes)]
|
||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||
|
||||
#![allow(box_pointers)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(trivial_casts)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_results)]
|
||||
//! Generated file from `url_type_option.proto`
|
||||
|
||||
/// Generated files are compatible only with the same version
|
||||
/// of protobuf runtime.
|
||||
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
|
||||
|
||||
#[derive(PartialEq,Clone,Default)]
|
||||
pub struct URLTypeOption {
|
||||
// message fields
|
||||
pub data: ::std::string::String,
|
||||
// special fields
|
||||
pub unknown_fields: ::protobuf::UnknownFields,
|
||||
pub cached_size: ::protobuf::CachedSize,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a URLTypeOption {
|
||||
fn default() -> &'a URLTypeOption {
|
||||
<URLTypeOption as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl URLTypeOption {
|
||||
pub fn new() -> URLTypeOption {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
// string data = 1;
|
||||
|
||||
|
||||
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 URLTypeOption {
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
||||
while !is.eof()? {
|
||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
||||
match field_number {
|
||||
1 => {
|
||||
::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())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u32 {
|
||||
let mut my_size = 0;
|
||||
if !self.data.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.data);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
||||
self.cached_size.set(my_size);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
||||
if !self.data.is_empty() {
|
||||
os.write_string(1, &self.data)?;
|
||||
}
|
||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn get_cached_size(&self) -> u32 {
|
||||
self.cached_size.get()
|
||||
}
|
||||
|
||||
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
|
||||
&self.unknown_fields
|
||||
}
|
||||
|
||||
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
|
||||
&mut self.unknown_fields
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn (::std::any::Any) {
|
||||
self as &dyn (::std::any::Any)
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
|
||||
self as &mut dyn (::std::any::Any)
|
||||
}
|
||||
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
|
||||
self
|
||||
}
|
||||
|
||||
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
|
||||
Self::descriptor_static()
|
||||
}
|
||||
|
||||
fn new() -> URLTypeOption {
|
||||
URLTypeOption::new()
|
||||
}
|
||||
|
||||
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
||||
descriptor.get(|| {
|
||||
let mut fields = ::std::vec::Vec::new();
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"data",
|
||||
|m: &URLTypeOption| { &m.data },
|
||||
|m: &mut URLTypeOption| { &mut m.data },
|
||||
));
|
||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<URLTypeOption>(
|
||||
"URLTypeOption",
|
||||
fields,
|
||||
file_descriptor_proto()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static URLTypeOption {
|
||||
static instance: ::protobuf::rt::LazyV2<URLTypeOption> = ::protobuf::rt::LazyV2::INIT;
|
||||
instance.get(URLTypeOption::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Clear for URLTypeOption {
|
||||
fn clear(&mut self) {
|
||||
self.data.clear();
|
||||
self.unknown_fields.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for URLTypeOption {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for URLTypeOption {
|
||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
||||
::protobuf::reflect::ReflectValueRef::Message(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default)]
|
||||
pub struct URLCellData {
|
||||
// message fields
|
||||
pub url: ::std::string::String,
|
||||
pub content: ::std::string::String,
|
||||
// special fields
|
||||
pub unknown_fields: ::protobuf::UnknownFields,
|
||||
pub cached_size: ::protobuf::CachedSize,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a URLCellData {
|
||||
fn default() -> &'a URLCellData {
|
||||
<URLCellData as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl URLCellData {
|
||||
pub fn new() -> URLCellData {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
// string url = 1;
|
||||
|
||||
|
||||
pub fn get_url(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
pub fn clear_url(&mut self) {
|
||||
self.url.clear();
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_url(&mut self, v: ::std::string::String) {
|
||||
self.url = v;
|
||||
}
|
||||
|
||||
// Mutable pointer to the field.
|
||||
// If field is not initialized, it is initialized with default value first.
|
||||
pub fn mut_url(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.url
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_url(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.url, ::std::string::String::new())
|
||||
}
|
||||
|
||||
// string content = 2;
|
||||
|
||||
|
||||
pub fn get_content(&self) -> &str {
|
||||
&self.content
|
||||
}
|
||||
pub fn clear_content(&mut self) {
|
||||
self.content.clear();
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_content(&mut self, v: ::std::string::String) {
|
||||
self.content = v;
|
||||
}
|
||||
|
||||
// Mutable pointer to the field.
|
||||
// If field is not initialized, it is initialized with default value first.
|
||||
pub fn mut_content(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.content
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_content(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.content, ::std::string::String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for URLCellData {
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
||||
while !is.eof()? {
|
||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
||||
match field_number {
|
||||
1 => {
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.url)?;
|
||||
},
|
||||
2 => {
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?;
|
||||
},
|
||||
_ => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u32 {
|
||||
let mut my_size = 0;
|
||||
if !self.url.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.url);
|
||||
}
|
||||
if !self.content.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(2, &self.content);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
||||
self.cached_size.set(my_size);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
||||
if !self.url.is_empty() {
|
||||
os.write_string(1, &self.url)?;
|
||||
}
|
||||
if !self.content.is_empty() {
|
||||
os.write_string(2, &self.content)?;
|
||||
}
|
||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn get_cached_size(&self) -> u32 {
|
||||
self.cached_size.get()
|
||||
}
|
||||
|
||||
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
|
||||
&self.unknown_fields
|
||||
}
|
||||
|
||||
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
|
||||
&mut self.unknown_fields
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn (::std::any::Any) {
|
||||
self as &dyn (::std::any::Any)
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
|
||||
self as &mut dyn (::std::any::Any)
|
||||
}
|
||||
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
|
||||
self
|
||||
}
|
||||
|
||||
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
|
||||
Self::descriptor_static()
|
||||
}
|
||||
|
||||
fn new() -> URLCellData {
|
||||
URLCellData::new()
|
||||
}
|
||||
|
||||
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
||||
descriptor.get(|| {
|
||||
let mut fields = ::std::vec::Vec::new();
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"url",
|
||||
|m: &URLCellData| { &m.url },
|
||||
|m: &mut URLCellData| { &mut m.url },
|
||||
));
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"content",
|
||||
|m: &URLCellData| { &m.content },
|
||||
|m: &mut URLCellData| { &mut m.content },
|
||||
));
|
||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<URLCellData>(
|
||||
"URLCellData",
|
||||
fields,
|
||||
file_descriptor_proto()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static URLCellData {
|
||||
static instance: ::protobuf::rt::LazyV2<URLCellData> = ::protobuf::rt::LazyV2::INIT;
|
||||
instance.get(URLCellData::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Clear for URLCellData {
|
||||
fn clear(&mut self) {
|
||||
self.url.clear();
|
||||
self.content.clear();
|
||||
self.unknown_fields.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for URLCellData {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for URLCellData {
|
||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
||||
::protobuf::reflect::ReflectValueRef::Message(self)
|
||||
}
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x15url_type_option.proto\"#\n\rURLTypeOption\x12\x12\n\x04data\x18\
|
||||
\x01\x20\x01(\tR\x04data\"9\n\x0bURLCellData\x12\x10\n\x03url\x18\x01\
|
||||
\x20\x01(\tR\x03url\x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07contentb\
|
||||
\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
||||
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
|
||||
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
|
||||
}
|
||||
|
||||
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
|
||||
file_descriptor_proto_lazy.get(|| {
|
||||
parse_descriptor_proto()
|
||||
})
|
||||
}
|
@ -24,5 +24,4 @@ enum GridEvent {
|
||||
UpdateCell = 71;
|
||||
UpdateSelectOptionCell = 72;
|
||||
UpdateDateCell = 80;
|
||||
GetDateCellData = 90;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message RichTextTypeOption {
|
||||
string format = 1;
|
||||
string data = 1;
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message URLTypeOption {
|
||||
string data = 1;
|
||||
}
|
||||
message URLCellData {
|
||||
string url = 1;
|
||||
string content = 2;
|
||||
}
|
@ -8,18 +8,17 @@ use flowy_sync::util::make_delta_from_revisions;
|
||||
use lib_infra::future::FutureResult;
|
||||
use lib_ot::core::PlainTextAttributes;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub struct ClientGridBlockMetaEditor {
|
||||
pub struct GridBlockMetaEditor {
|
||||
user_id: String,
|
||||
pub block_id: String,
|
||||
pad: Arc<RwLock<GridBlockMetaPad>>,
|
||||
rev_manager: Arc<RevisionManager>,
|
||||
}
|
||||
|
||||
impl ClientGridBlockMetaEditor {
|
||||
impl GridBlockMetaEditor {
|
||||
pub async fn new(
|
||||
user_id: &str,
|
||||
token: &str,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::block_meta_editor::ClientGridBlockMetaEditor;
|
||||
use crate::services::persistence::block_index::BlockIndexPersistence;
|
||||
use crate::services::block_meta_editor::GridBlockMetaEditor;
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::row::{group_row_orders, GridBlockSnapshot};
|
||||
use dashmap::DashMap;
|
||||
use flowy_error::FlowyResult;
|
||||
@ -15,20 +15,20 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) struct GridBlockMetaEditorManager {
|
||||
type BlockId = String;
|
||||
pub(crate) struct GridBlockManager {
|
||||
grid_id: String,
|
||||
user: Arc<dyn GridUser>,
|
||||
// Key: block_id
|
||||
editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
|
||||
persistence: Arc<BlockIndexPersistence>,
|
||||
persistence: Arc<BlockIndexCache>,
|
||||
block_editor_map: DashMap<BlockId, Arc<GridBlockMetaEditor>>,
|
||||
}
|
||||
|
||||
impl GridBlockMetaEditorManager {
|
||||
impl GridBlockManager {
|
||||
pub(crate) async fn new(
|
||||
grid_id: &str,
|
||||
user: &Arc<dyn GridUser>,
|
||||
blocks: Vec<GridBlockMeta>,
|
||||
persistence: Arc<BlockIndexPersistence>,
|
||||
persistence: Arc<BlockIndexCache>,
|
||||
) -> FlowyResult<Self> {
|
||||
let editor_map = make_block_meta_editor_map(user, blocks).await?;
|
||||
let user = user.clone();
|
||||
@ -36,27 +36,27 @@ impl GridBlockMetaEditorManager {
|
||||
let manager = Self {
|
||||
grid_id,
|
||||
user,
|
||||
editor_map,
|
||||
block_editor_map: editor_map,
|
||||
persistence,
|
||||
};
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
// #[tracing::instrument(level = "trace", skip(self))]
|
||||
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
|
||||
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockMetaEditor>> {
|
||||
debug_assert!(!block_id.is_empty());
|
||||
match self.editor_map.get(block_id) {
|
||||
match self.block_editor_map.get(block_id) {
|
||||
None => {
|
||||
tracing::error!("The is a fatal error, block is not exist");
|
||||
let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
|
||||
self.editor_map.insert(block_id.to_owned(), editor.clone());
|
||||
self.block_editor_map.insert(block_id.to_owned(), editor.clone());
|
||||
Ok(editor)
|
||||
}
|
||||
Some(editor) => Ok(editor.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
|
||||
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockMetaEditor>> {
|
||||
let block_id = self.persistence.get_block_id(row_id)?;
|
||||
Ok(self.get_editor(&block_id).await?)
|
||||
}
|
||||
@ -67,7 +67,7 @@ impl GridBlockMetaEditorManager {
|
||||
row_meta: RowMeta,
|
||||
start_row_id: Option<String>,
|
||||
) -> FlowyResult<i32> {
|
||||
let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
|
||||
let _ = self.persistence.insert(&row_meta.block_id, &row_meta.id)?;
|
||||
let editor = self.get_editor(&row_meta.block_id).await?;
|
||||
|
||||
let mut index_row_order = IndexRowOrder::from(&row_meta);
|
||||
@ -90,7 +90,7 @@ impl GridBlockMetaEditorManager {
|
||||
let editor = self.get_editor(&block_id).await?;
|
||||
let mut row_count = 0;
|
||||
for row in row_metas {
|
||||
let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?;
|
||||
let _ = self.persistence.insert(&row.block_id, &row.id)?;
|
||||
let mut row_order = IndexRowOrder::from(&row);
|
||||
let (count, index) = editor.create_row(row, None).await?;
|
||||
row_count = count;
|
||||
@ -256,7 +256,7 @@ impl GridBlockMetaEditorManager {
|
||||
async fn make_block_meta_editor_map(
|
||||
user: &Arc<dyn GridUser>,
|
||||
blocks: Vec<GridBlockMeta>,
|
||||
) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
|
||||
) -> FlowyResult<DashMap<String, Arc<GridBlockMetaEditor>>> {
|
||||
let editor_map = DashMap::new();
|
||||
for block in blocks {
|
||||
let editor = make_block_meta_editor(user, &block.block_id).await?;
|
||||
@ -266,7 +266,7 @@ async fn make_block_meta_editor_map(
|
||||
Ok(editor_map)
|
||||
}
|
||||
|
||||
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
|
||||
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockMetaEditor> {
|
||||
let token = user.token()?;
|
||||
let user_id = user.user_id()?;
|
||||
let pool = user.db_pool()?;
|
||||
@ -274,5 +274,5 @@ async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> Flo
|
||||
let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool));
|
||||
let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache));
|
||||
let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence);
|
||||
ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
|
||||
GridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
|
||||
}
|
||||
|
@ -94,6 +94,7 @@ pub fn default_type_option_builder_from_type(field_type: &FieldType) -> Box<dyn
|
||||
FieldType::SingleSelect => SingleSelectTypeOption::default().into(),
|
||||
FieldType::MultiSelect => MultiSelectTypeOption::default().into(),
|
||||
FieldType::Checkbox => CheckboxTypeOption::default().into(),
|
||||
FieldType::URL => URLTypeOption::default().into(),
|
||||
};
|
||||
|
||||
type_option_builder_from_json_str(&s, field_type)
|
||||
@ -107,6 +108,7 @@ pub fn type_option_builder_from_json_str(s: &str, field_type: &FieldType) -> Box
|
||||
FieldType::SingleSelect => Box::new(SingleSelectTypeOptionBuilder::from_json_str(s)),
|
||||
FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_json_str(s)),
|
||||
FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_json_str(s)),
|
||||
FieldType::URL => Box::new(URLTypeOptionBuilder::from_json_str(s)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,5 +121,6 @@ pub fn type_option_builder_from_bytes<T: Into<Bytes>>(bytes: T, field_type: &Fie
|
||||
FieldType::SingleSelect => Box::new(SingleSelectTypeOptionBuilder::from_protobuf_bytes(bytes)),
|
||||
FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_protobuf_bytes(bytes)),
|
||||
FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_protobuf_bytes(bytes)),
|
||||
FieldType::URL => Box::new(URLTypeOptionBuilder::from_protobuf_bytes(bytes)),
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
use crate::impl_type_option;
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
|
||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData};
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{
|
||||
CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption);
|
||||
@ -43,32 +42,38 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
|
||||
const YES: &str = "Yes";
|
||||
const NO: &str = "No";
|
||||
|
||||
impl CellDataOperation for CheckboxTypeOption {
|
||||
fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData {
|
||||
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
|
||||
if !type_option_cell_data.is_checkbox() {
|
||||
return DecodedCellData::default();
|
||||
}
|
||||
let cell_data = type_option_cell_data.data;
|
||||
if cell_data == YES || cell_data == NO {
|
||||
return DecodedCellData::from_content(cell_data);
|
||||
}
|
||||
impl CellDataOperation<String, String> for CheckboxTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
decoded_field_type: &FieldType,
|
||||
_field_meta: &FieldMeta,
|
||||
) -> FlowyResult<DecodedCellData>
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
if !decoded_field_type.is_checkbox() {
|
||||
return Ok(DecodedCellData::default());
|
||||
}
|
||||
|
||||
DecodedCellData::default()
|
||||
let encoded_data = encoded_data.into();
|
||||
if encoded_data == YES || encoded_data == NO {
|
||||
return Ok(DecodedCellData::new(encoded_data));
|
||||
}
|
||||
|
||||
Ok(DecodedCellData::default())
|
||||
}
|
||||
|
||||
fn apply_changeset<T: Into<CellContentChangeset>>(
|
||||
&self,
|
||||
changeset: T,
|
||||
_cell_meta: Option<CellMeta>,
|
||||
) -> Result<String, FlowyError> {
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
{
|
||||
let changeset = changeset.into();
|
||||
let s = match string_to_bool(&changeset) {
|
||||
true => YES,
|
||||
false => NO,
|
||||
};
|
||||
Ok(TypeOptionCellData::new(s, self.field_type()).json())
|
||||
Ok(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,32 +93,49 @@ fn string_to_bool(bool_str: &str) -> bool {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::services::field::type_options::checkbox_type_option::{NO, YES};
|
||||
use crate::services::field::CheckboxTypeOption;
|
||||
|
||||
use crate::services::field::FieldBuilder;
|
||||
use crate::services::row::CellDataOperation;
|
||||
use crate::services::row::{apply_cell_data_changeset, decode_cell_data_from_type_option_cell_data};
|
||||
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
|
||||
#[test]
|
||||
fn checkout_box_description_test() {
|
||||
let type_option = CheckboxTypeOption::default();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Checkbox).build();
|
||||
let data = apply_cell_data_changeset("true", None, &field_meta).unwrap();
|
||||
assert_eq!(
|
||||
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
|
||||
YES
|
||||
);
|
||||
|
||||
let data = type_option.apply_changeset("true", None).unwrap();
|
||||
assert_eq!(type_option.decode_cell_data(data, &field_meta).content, YES);
|
||||
let data = apply_cell_data_changeset("1", None, &field_meta).unwrap();
|
||||
assert_eq!(
|
||||
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
|
||||
YES
|
||||
);
|
||||
|
||||
let data = type_option.apply_changeset("1", None).unwrap();
|
||||
assert_eq!(type_option.decode_cell_data(data, &field_meta).content, YES);
|
||||
let data = apply_cell_data_changeset("yes", None, &field_meta).unwrap();
|
||||
assert_eq!(
|
||||
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
|
||||
YES
|
||||
);
|
||||
|
||||
let data = type_option.apply_changeset("yes", None).unwrap();
|
||||
assert_eq!(type_option.decode_cell_data(data, &field_meta).content, YES);
|
||||
let data = apply_cell_data_changeset("false", None, &field_meta).unwrap();
|
||||
assert_eq!(
|
||||
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
|
||||
NO
|
||||
);
|
||||
|
||||
let data = type_option.apply_changeset("false", None).unwrap();
|
||||
assert_eq!(type_option.decode_cell_data(data, &field_meta).content, NO);
|
||||
let data = apply_cell_data_changeset("no", None, &field_meta).unwrap();
|
||||
assert_eq!(
|
||||
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
|
||||
NO
|
||||
);
|
||||
|
||||
let data = type_option.apply_changeset("no", None).unwrap();
|
||||
assert_eq!(type_option.decode_cell_data(data, &field_meta).content, NO);
|
||||
|
||||
let data = type_option.apply_changeset("123", None).unwrap();
|
||||
assert_eq!(type_option.decode_cell_data(data, &field_meta).content, NO);
|
||||
let data = apply_cell_data_changeset("12", None, &field_meta).unwrap();
|
||||
assert_eq!(
|
||||
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
|
||||
NO
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::entities::{CellIdentifier, CellIdentifierPayload};
|
||||
use crate::impl_type_option;
|
||||
use crate::services::entities::{CellIdentifier, CellIdentifierPayload};
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
|
||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
|
||||
use bytes::Bytes;
|
||||
use chrono::format::strftime::StrftimeItems;
|
||||
use chrono::NaiveDateTime;
|
||||
@ -77,32 +77,12 @@ impl DateTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_date_cell_data(&self, cell_meta: &Option<CellMeta>) -> FlowyResult<DateCellData> {
|
||||
if cell_meta.is_none() {
|
||||
return Ok(DateCellData::default());
|
||||
}
|
||||
|
||||
let json = &cell_meta.as_ref().unwrap().data;
|
||||
let result = TypeOptionCellData::from_str(json);
|
||||
if result.is_err() {
|
||||
return Ok(DateCellData::default());
|
||||
}
|
||||
|
||||
let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?;
|
||||
let date = self.decode_cell_data_from_timestamp(&serde_cell_data).content;
|
||||
let time = serde_cell_data.time.unwrap_or("".to_owned());
|
||||
let timestamp = serde_cell_data.timestamp;
|
||||
|
||||
return Ok(DateCellData { date, time, timestamp });
|
||||
}
|
||||
|
||||
fn decode_cell_data_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> DecodedCellData {
|
||||
fn date_desc_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> String {
|
||||
if serde_cell_data.timestamp == 0 {
|
||||
return DecodedCellData::default();
|
||||
return "".to_owned();
|
||||
}
|
||||
|
||||
let cell_content = self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time);
|
||||
return DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content);
|
||||
self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time)
|
||||
}
|
||||
|
||||
fn timestamp_from_utc_with_time(
|
||||
@ -131,34 +111,40 @@ impl DateTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(utc.timestamp());
|
||||
Ok(utc.timestamp())
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataOperation for DateTypeOption {
|
||||
fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData {
|
||||
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
|
||||
// Return default data if the type_option_cell_data is not FieldType::DateTime.
|
||||
// It happens when switching from one field to another.
|
||||
// For example:
|
||||
// FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen.
|
||||
if !type_option_cell_data.is_date() {
|
||||
return DecodedCellData::default();
|
||||
}
|
||||
return match DateCellDataSerde::from_str(&type_option_cell_data.data) {
|
||||
Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(&serde_cell_data),
|
||||
Err(_) => DecodedCellData::default(),
|
||||
};
|
||||
impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> for DateTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
decoded_field_type: &FieldType,
|
||||
_field_meta: &FieldMeta,
|
||||
) -> FlowyResult<DecodedCellData>
|
||||
where
|
||||
T: Into<EncodedCellData<DateCellDataSerde>>,
|
||||
{
|
||||
// Return default data if the type_option_cell_data is not FieldType::DateTime.
|
||||
// It happens when switching from one field to another.
|
||||
// For example:
|
||||
// FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen.
|
||||
if !decoded_field_type.is_date() {
|
||||
return Ok(DecodedCellData::default());
|
||||
}
|
||||
|
||||
DecodedCellData::default()
|
||||
let encoded_data = encoded_data.into().try_into_inner()?;
|
||||
let date = self.date_desc_from_timestamp(&encoded_data);
|
||||
let time = encoded_data.time.unwrap_or_else(|| "".to_owned());
|
||||
let timestamp = encoded_data.timestamp;
|
||||
|
||||
DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp })
|
||||
}
|
||||
|
||||
fn apply_changeset<T: Into<CellContentChangeset>>(
|
||||
&self,
|
||||
changeset: T,
|
||||
_cell_meta: Option<CellMeta>,
|
||||
) -> Result<String, FlowyError> {
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<DateCellDataSerde, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
{
|
||||
let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?;
|
||||
let cell_data = match content_changeset.date_timestamp() {
|
||||
None => DateCellDataSerde::default(),
|
||||
@ -173,7 +159,7 @@ impl CellDataOperation for DateTypeOption {
|
||||
},
|
||||
};
|
||||
|
||||
Ok(TypeOptionCellData::new(cell_data.to_string(), self.field_type()).json())
|
||||
Ok(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,23 +293,29 @@ impl DateCellDataSerde {
|
||||
fn new(timestamp: i64, time: Option<String>, time_format: &TimeFormat) -> Self {
|
||||
Self {
|
||||
timestamp,
|
||||
time: Some(time.unwrap_or(default_time_str(time_format))),
|
||||
time: Some(time.unwrap_or_else(|| default_time_str(time_format))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_timestamp(timestamp: i64, time: Option<String>) -> Self {
|
||||
Self { timestamp, time }
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string(self) -> String {
|
||||
serde_json::to_string(&self).unwrap_or("".to_string())
|
||||
}
|
||||
impl FromStr for DateCellDataSerde {
|
||||
type Err = FlowyError;
|
||||
|
||||
fn from_str(s: &str) -> FlowyResult<Self> {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str::<DateCellDataSerde>(s).map_err(internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for DateCellDataSerde {
|
||||
fn to_string(&self) -> String {
|
||||
serde_json::to_string(&self).unwrap_or_else(|_| "".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn default_time_str(time_format: &TimeFormat) -> String {
|
||||
match time_format {
|
||||
TimeFormat::TwelveHour => "12:00 AM".to_string(),
|
||||
@ -410,50 +402,45 @@ mod tests {
|
||||
use crate::services::field::{
|
||||
DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat,
|
||||
};
|
||||
use crate::services::row::{CellDataOperation, TypeOptionCellData};
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
use crate::services::row::{CellDataOperation, EncodedCellData};
|
||||
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
#[test]
|
||||
fn date_description_invalid_input_test() {
|
||||
let type_option = DateTypeOption::default();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
||||
assert_eq!(
|
||||
"".to_owned(),
|
||||
type_option.decode_cell_data("1e".to_owned(), &field_meta).content
|
||||
let field_type = FieldType::DateTime;
|
||||
let field_meta = FieldBuilder::from_field_type(&field_type).build();
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some("1e".to_string()),
|
||||
time: Some("23:00".to_owned()),
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_description_date_format_test() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
|
||||
for date_format in DateFormat::iter() {
|
||||
type_option.date_format = date_format;
|
||||
match date_format {
|
||||
DateFormat::Friendly => {
|
||||
assert_eq!(
|
||||
"Mar 14,2022".to_owned(),
|
||||
type_option.decode_cell_data(data(1647251762), &field_meta).content
|
||||
);
|
||||
assert_decode_timestamp(1647251762, &type_option, &field_meta, "Mar 14,2022");
|
||||
}
|
||||
DateFormat::US => {
|
||||
assert_eq!(
|
||||
"2022/03/14".to_owned(),
|
||||
type_option.decode_cell_data(data(1647251762), &field_meta).content
|
||||
);
|
||||
assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14");
|
||||
}
|
||||
DateFormat::ISO => {
|
||||
assert_eq!(
|
||||
"2022-03-14".to_owned(),
|
||||
type_option.decode_cell_data(data(1647251762), &field_meta).content
|
||||
);
|
||||
assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022-03-14");
|
||||
}
|
||||
DateFormat::Local => {
|
||||
assert_eq!(
|
||||
"2022/03/14".to_owned(),
|
||||
type_option.decode_cell_data(data(1647251762), &field_meta).content
|
||||
);
|
||||
assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -462,84 +449,68 @@ mod tests {
|
||||
#[test]
|
||||
fn date_description_time_format_test() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
||||
for time_format in TimeFormat::iter() {
|
||||
type_option.time_format = time_format;
|
||||
match time_format {
|
||||
TimeFormat::TwentyFourHour => {
|
||||
assert_eq!(
|
||||
"Mar 14,2022".to_owned(),
|
||||
type_option.today_desc_from_timestamp(1647251762, &None)
|
||||
);
|
||||
assert_eq!(
|
||||
"Mar 14,2022".to_owned(),
|
||||
type_option.decode_cell_data(data(1647251762), &field_meta).content
|
||||
);
|
||||
}
|
||||
TimeFormat::TwelveHour => {
|
||||
assert_eq!(
|
||||
"Mar 14,2022".to_owned(),
|
||||
type_option.today_desc_from_timestamp(1647251762, &None)
|
||||
);
|
||||
assert_eq!(
|
||||
"Mar 14,2022".to_owned(),
|
||||
type_option.decode_cell_data(data(1647251762), &field_meta).content
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let field_type = FieldType::DateTime;
|
||||
let field_meta = FieldBuilder::from_field_type(&field_type).build();
|
||||
|
||||
#[test]
|
||||
fn date_description_time_format_test2() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
||||
for time_format in TimeFormat::iter() {
|
||||
type_option.time_format = time_format;
|
||||
type_option.include_time = true;
|
||||
match time_format {
|
||||
TimeFormat::TwentyFourHour => {
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: None,
|
||||
};
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!("May 27,2022 00:00".to_owned(), content);
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: Some("23:00".to_owned()),
|
||||
};
|
||||
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!("May 27,2022 23:00".to_owned(), content);
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: None,
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 00:00",
|
||||
);
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: Some("23:00".to_owned()),
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 23:00",
|
||||
);
|
||||
}
|
||||
TimeFormat::TwelveHour => {
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: None,
|
||||
};
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!("May 27,2022 12:00 AM".to_owned(), content);
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: None,
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 12:00 AM",
|
||||
);
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: Some("".to_owned()),
|
||||
};
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!("May 27,2022".to_owned(), content);
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: Some("".to_owned()),
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022",
|
||||
);
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: Some("11:23 pm".to_owned()),
|
||||
};
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!("May 27,2022 11:23 PM".to_owned(), content);
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(1653609600.to_string()),
|
||||
time: Some("11:23 pm".to_owned()),
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 11:23 PM",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -548,37 +519,55 @@ mod tests {
|
||||
#[test]
|
||||
fn date_description_apply_changeset_test() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
||||
let field_type = FieldType::DateTime;
|
||||
let field_meta = FieldBuilder::from_field_type(&field_type).build();
|
||||
let date_timestamp = "1653609600".to_owned();
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(date_timestamp.clone()),
|
||||
time: None,
|
||||
};
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result.clone(), &field_meta).content;
|
||||
assert_eq!(content, "May 27,2022".to_owned());
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(date_timestamp.clone()),
|
||||
time: None,
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022",
|
||||
);
|
||||
|
||||
type_option.include_time = true;
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!(content, "May 27,2022 00:00".to_owned());
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(date_timestamp.clone()),
|
||||
time: None,
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 00:00",
|
||||
);
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(date_timestamp.clone()),
|
||||
time: Some("1:00".to_owned()),
|
||||
};
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!(content, "May 27,2022 01:00".to_owned());
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(date_timestamp.clone()),
|
||||
time: Some("1:00".to_owned()),
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 01:00",
|
||||
);
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(date_timestamp),
|
||||
time: Some("1:00 am".to_owned()),
|
||||
};
|
||||
type_option.time_format = TimeFormat::TwelveHour;
|
||||
let result = type_option.apply_changeset(changeset, None).unwrap();
|
||||
let content = type_option.decode_cell_data(result, &field_meta).content;
|
||||
assert_eq!(content, "May 27,2022 01:00 AM".to_owned());
|
||||
assert_changeset_result(
|
||||
&type_option,
|
||||
DateCellContentChangeset {
|
||||
date: Some(date_timestamp),
|
||||
time: Some("1:00 am".to_owned()),
|
||||
},
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"May 27,2022 01:00 AM",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -586,7 +575,7 @@ mod tests {
|
||||
fn date_description_apply_changeset_error_test() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
type_option.include_time = true;
|
||||
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
||||
let _field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
|
||||
let date_timestamp = "1653609600".to_owned();
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
@ -596,7 +585,7 @@ mod tests {
|
||||
let _ = type_option.apply_changeset(changeset, None).unwrap();
|
||||
|
||||
let changeset = DateCellContentChangeset {
|
||||
date: Some(date_timestamp.clone()),
|
||||
date: Some(date_timestamp),
|
||||
time: Some("1:".to_owned()),
|
||||
};
|
||||
let _ = type_option.apply_changeset(changeset, None).unwrap();
|
||||
@ -609,8 +598,39 @@ mod tests {
|
||||
type_option.apply_changeset("he", None).unwrap();
|
||||
}
|
||||
|
||||
fn data(s: i64) -> String {
|
||||
let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(s, None)).unwrap();
|
||||
TypeOptionCellData::new(&json, FieldType::DateTime).json()
|
||||
fn assert_changeset_result(
|
||||
type_option: &DateTypeOption,
|
||||
changeset: DateCellContentChangeset,
|
||||
_field_type: &FieldType,
|
||||
field_meta: &FieldMeta,
|
||||
expected: &str,
|
||||
) {
|
||||
let encoded_data = EncodedCellData(Some(type_option.apply_changeset(changeset, None).unwrap()));
|
||||
assert_eq!(
|
||||
expected.to_owned(),
|
||||
decode_cell_data(encoded_data, type_option, field_meta)
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_decode_timestamp(timestamp: i64, type_option: &DateTypeOption, field_meta: &FieldMeta, expected: &str) {
|
||||
let serde_json = DateCellDataSerde { timestamp, time: None }.to_string();
|
||||
|
||||
assert_eq!(
|
||||
expected.to_owned(),
|
||||
decode_cell_data(serde_json, type_option, field_meta)
|
||||
);
|
||||
}
|
||||
|
||||
fn decode_cell_data<T: Into<EncodedCellData<DateCellDataSerde>>>(
|
||||
encoded_data: T,
|
||||
type_option: &DateTypeOption,
|
||||
field_meta: &FieldMeta,
|
||||
) -> String {
|
||||
type_option
|
||||
.decode_cell_data(encoded_data, &FieldType::DateTime, field_meta)
|
||||
.unwrap()
|
||||
.parse::<DateCellData>()
|
||||
.unwrap()
|
||||
.date
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ mod date_type_option;
|
||||
mod number_type_option;
|
||||
mod selection_type_option;
|
||||
mod text_type_option;
|
||||
mod url_type_option;
|
||||
mod util;
|
||||
|
||||
pub use checkbox_type_option::*;
|
||||
@ -10,3 +11,4 @@ pub use date_type_option::*;
|
||||
pub use number_type_option::*;
|
||||
pub use selection_type_option::*;
|
||||
pub use text_type_option::*;
|
||||
pub use url_type_option::*;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user