chore: show document icon or emoji (#6144)
Some checks are pending
Commit messages lint / commitlint (push) Waiting to run
Docker-CI / build-app (push) Waiting to run
Flutter-CI / prepare-linux (development-linux-x86_64, ubuntu-latest, x86_64-unknown-linux-gnu) (push) Waiting to run
Flutter-CI / prepare-windows (development-windows-x86, windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Flutter-CI / prepare-macos (development-mac-x86_64, macos-latest, x86_64-apple-darwin) (push) Waiting to run
Flutter-CI / unit_test (development-linux-x86_64, ubuntu-latest, x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Flutter-CI / cloud_integration_test (development-linux-x86_64, ubuntu-latest, x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Flutter-CI / integration_test_1 (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Flutter-CI / integration_test_2 (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Flutter-CI / integration_test_3 (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Blocked by required conditions
iOS CI / build-self-hosted (push) Waiting to run
iOS CI / build-macos (push) Waiting to run
Rust-CI / self-hosted-job (push) Waiting to run
Rust-CI / ubuntu-job (push) Waiting to run
Rust code coverage / tests (push) Waiting to run

* chore: show icon

* chore: rename

* chore: adjust UI

* chore: remove unused property

* chore: update test

* chore: fix test

* chore: fix test
This commit is contained in:
Nathan.fooo 2024-08-31 23:54:37 +08:00 committed by GitHub
parent d264f3dbde
commit 01d7d6e900
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 216 additions and 173 deletions

View File

@ -246,7 +246,7 @@ jobs:
- name: Run Docker-Compose
working-directory: AppFlowy-Cloud
env:
BACKEND_VERSION: 0.3.24-amd64
BACKEND_VERSION: 0.6.4-amd64
run: |
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
docker compose pull

View File

@ -106,6 +106,8 @@ jobs:
- name: Run Docker-Compose
working-directory: AppFlowy-Cloud
env:
BACKEND_VERSION: 0.6.4-amd64
run: |
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
docker compose pull

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -42,8 +43,8 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
emit(state.copyWith(wrap: wrap));
}
},
didUpdateEmoji: (String emoji) {
emit(state.copyWith(emoji: emoji));
didUpdateEmoji: (String emoji, bool hasDocument) {
// emit(state.copyWith(emoji: emoji, hasDocument: hasDocument));
},
updateText: (String text) {
if (state.content != text) {
@ -66,13 +67,6 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
}
},
onFieldChanged: _onFieldChangedListener,
onRowMetaChanged: cellController.fieldInfo.isPrimary
? () {
if (!isClosed) {
add(TextCellEvent.didUpdateEmoji(cellController.icon ?? ""));
}
}
: null,
);
}
@ -91,14 +85,18 @@ class TextCellEvent with _$TextCellEvent {
_DidUpdateField;
const factory TextCellEvent.updateText(String text) = _UpdateText;
const factory TextCellEvent.enableEdit(bool enabled) = _EnableEdit;
const factory TextCellEvent.didUpdateEmoji(String emoji) = _UpdateEmoji;
const factory TextCellEvent.didUpdateEmoji(
String emoji,
bool hasDocument,
) = _UpdateEmoji;
}
@freezed
class TextCellState with _$TextCellState {
const factory TextCellState({
required String content,
required String emoji,
required ValueNotifier<String>? emoji,
required ValueNotifier<bool>? hasDocument,
required bool enableEdit,
required bool wrap,
}) = _TextCellState;
@ -106,13 +104,16 @@ class TextCellState with _$TextCellState {
factory TextCellState.initial(TextCellController cellController) {
final cellData = cellController.getCellData() ?? "";
final wrap = cellController.fieldInfo.wrapCellContent ?? true;
final emoji =
cellController.fieldInfo.isPrimary ? cellController.icon ?? "" : "";
ValueNotifier<String>? emoji;
if (cellController.fieldInfo.isPrimary) {
emoji = cellController.icon;
}
return TextCellState(
content: cellData,
emoji: emoji,
enableEdit: false,
hasDocument: cellController.hasDocument,
wrap: wrap,
);
}

View File

@ -5,7 +5,6 @@ import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy/plugins/database/domain/cell_listener.dart';
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
import 'package:appflowy/plugins/database/domain/row_meta_listener.dart';
import 'package:appflowy/plugins/database/application/row/row_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
@ -61,10 +60,8 @@ class CellController<T, D> {
final CellDataPersistence<D> _cellDataPersistence;
CellListener? _cellListener;
RowMetaListener? _rowMetaListener;
CellDataNotifier<T?>? _cellDataNotifier;
VoidCallback? _onRowMetaChanged;
Timer? _loadDataOperation;
Timer? _saveDataOperation;
@ -75,8 +72,9 @@ class CellController<T, D> {
FieldInfo get fieldInfo => _fieldController.getField(_cellContext.fieldId)!;
FieldType get fieldType =>
_fieldController.getField(_cellContext.fieldId)!.fieldType;
RowMetaPB? get rowMeta => _rowCache.getRow(rowId)?.rowMeta;
String? get icon => rowMeta?.icon;
ValueNotifier<String>? get icon => _rowCache.getRow(rowId)?.rowIconNotifier;
ValueNotifier<bool>? get hasDocument =>
_rowCache.getRow(rowId)?.rowDocumentNotifier;
CellMemCache get _cellCache => _rowCache.cellCache;
/// casting method for painless type coersion
@ -89,15 +87,6 @@ class CellController<T, D> {
fieldId: _cellContext.fieldId,
);
_rowCache.addListener(
rowId: rowId,
onRowChanged: (context, reason) {
if (reason == const ChangedReason.didFetchRow()) {
_onRowMetaChanged?.call();
}
},
);
// 1. Listen on user edit event and load the new cell data if needed.
// For example:
// user input: 12
@ -116,23 +105,12 @@ class CellController<T, D> {
fieldId,
onFieldChanged: _onFieldChangedListener,
);
// 3. If the field is primary listen to row meta changes.
if (fieldInfo.field.isPrimary) {
_rowMetaListener = RowMetaListener(_cellContext.rowId);
_rowMetaListener?.start(
callback: (newRowMeta) {
_onRowMetaChanged?.call();
},
);
}
}
/// Add a new listener
VoidCallback? addListener({
required void Function(T?) onCellChanged,
void Function(FieldInfo fieldInfo)? onFieldChanged,
VoidCallback? onRowMetaChanged,
}) {
/// an adaptor for the onCellChanged listener
void onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
@ -145,8 +123,6 @@ class CellController<T, D> {
);
}
_onRowMetaChanged = onRowMetaChanged;
// Return the function pointer that can be used when calling removeListener.
return onCellChangedFn;
}
@ -242,9 +218,6 @@ class CellController<T, D> {
}
Future<void> dispose() async {
await _rowMetaListener?.stop();
_rowMetaListener = null;
await _cellListener?.stop();
_cellListener = null;
@ -258,7 +231,6 @@ class CellController<T, D> {
_saveDataOperation?.cancel();
_cellDataNotifier?.dispose();
_cellDataNotifier = null;
_onRowMetaChanged = null;
}
}

View File

@ -92,12 +92,19 @@ class RowCache {
}
void setRowMeta(RowMetaPB rowMeta) {
final rowInfo = buildGridRow(rowMeta);
_rowList.add(rowInfo);
var rowInfo = _rowList.get(rowMeta.id);
if (rowInfo != null) {
rowInfo.updateRowMeta(rowMeta);
} else {
rowInfo = buildGridRow(rowMeta);
_rowList.add(rowInfo);
}
_changedNotifier?.receive(const ChangedReason.didFetchRow());
}
void dispose() {
_rowList.dispose();
_rowLifeCycle.onRowDisposed();
_changedNotifier?.dispose();
_changedNotifier = null;
@ -176,8 +183,10 @@ class RowCache {
}
}
final updatedIndexs =
_rowList.updateRows(updatedList, (rowId) => buildGridRow(rowId));
final updatedIndexs = _rowList.updateRows(
rowMetas: updatedList,
builder: (rowId) => buildGridRow(rowId),
);
if (updatedIndexs.isNotEmpty) {
_changedNotifier?.receive(ChangedReason.update(updatedIndexs));
@ -251,9 +260,7 @@ class RowCache {
final rowInfo = _rowList.get(rowMetaPB.id);
final rowIndex = _rowList.indexOfRow(rowMetaPB.id);
if (rowInfo != null && rowIndex != null) {
final updatedRowInfo = rowInfo.copyWith(rowMeta: rowMetaPB);
_rowList.remove(rowMetaPB.id);
_rowList.insert(rowIndex, updatedRowInfo);
rowInfo.rowMetaNotifier.value = rowMetaPB;
final UpdatedIndexMap updatedIndexs = UpdatedIndexMap();
updatedIndexs[rowMetaPB.id] = UpdatedIndex(
@ -308,15 +315,38 @@ class RowChangesetNotifier extends ChangeNotifier {
}
}
@unfreezed
class RowInfo with _$RowInfo {
const RowInfo._();
factory RowInfo({
required UnmodifiableListView<FieldInfo> fields,
class RowInfo {
RowInfo({
required this.fields,
required RowMetaPB rowMeta,
}) = _RowInfo;
}) : rowMetaNotifier = ValueNotifier<RowMetaPB>(rowMeta),
rowIconNotifier = ValueNotifier<String>(rowMeta.icon),
rowDocumentNotifier = ValueNotifier<bool>(
!(rowMeta.hasIsDocumentEmpty() ? rowMeta.isDocumentEmpty : true),
);
String get rowId => rowMeta.id;
final UnmodifiableListView<FieldInfo> fields;
final ValueNotifier<RowMetaPB> rowMetaNotifier;
final ValueNotifier<String> rowIconNotifier;
final ValueNotifier<bool> rowDocumentNotifier;
String get rowId => rowMetaNotifier.value.id;
RowMetaPB get rowMeta => rowMetaNotifier.value;
/// Updates the RowMeta and automatically updates the related notifiers.
void updateRowMeta(RowMetaPB newMeta) {
rowMetaNotifier.value = newMeta;
rowIconNotifier.value = newMeta.icon;
rowDocumentNotifier.value = !newMeta.isDocumentEmpty;
}
/// Dispose of the notifiers when they are no longer needed.
void dispose() {
rowMetaNotifier.dispose();
rowIconNotifier.dispose();
rowDocumentNotifier.dispose();
}
}
typedef InsertedIndexs = List<InsertedIndex>;

View File

@ -1,4 +1,5 @@
import 'dart:collection';
import 'dart:math';
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
@ -117,20 +118,23 @@ class RowList {
return deletedIndex;
}
UpdatedIndexMap updateRows(
List<RowMetaPB> rowMetas,
RowInfo Function(RowMetaPB) builder,
) {
UpdatedIndexMap updateRows({
required List<RowMetaPB> rowMetas,
required RowInfo Function(RowMetaPB) builder,
}) {
final UpdatedIndexMap updatedIndexs = UpdatedIndexMap();
for (final rowMeta in rowMetas) {
final index = _rowInfos.indexWhere(
(rowInfo) => rowInfo.rowId == rowMeta.id,
);
if (index != -1) {
rowInfoByRowId[rowMeta.id]?.updateRowMeta(rowMeta);
} else {
final insertIndex = max(index, _rowInfos.length);
final rowInfo = builder(rowMeta);
insert(index, rowInfo);
insert(insertIndex, rowInfo);
updatedIndexs[rowMeta.id] = UpdatedIndex(
index: index,
index: insertIndex,
rowId: rowMeta.id,
);
}
@ -162,4 +166,11 @@ class RowList {
bool contains(RowId rowId) {
return rowInfoByRowId[rowId] != null;
}
void dispose() {
for (final rowInfo in _rowInfos) {
rowInfo.dispose();
}
_rowInfos.clear();
}
}

View File

@ -38,7 +38,7 @@ class RowBackendService {
}
Future<FlowyResult<void, FlowyError>> initRow(RowId rowId) async {
final payload = RowIdPB()
final payload = DatabaseViewRowIdPB()
..viewId = viewId
..rowId = rowId;
@ -65,7 +65,7 @@ class RowBackendService {
required String viewId,
required String rowId,
}) {
final payload = RowIdPB()
final payload = DatabaseViewRowIdPB()
..viewId = viewId
..rowId = rowId;
@ -73,7 +73,7 @@ class RowBackendService {
}
Future<FlowyResult<RowMetaPB, FlowyError>> getRowMeta(RowId rowId) {
final payload = RowIdPB.create()
final payload = DatabaseViewRowIdPB.create()
..viewId = viewId
..rowId = rowId;
@ -119,7 +119,7 @@ class RowBackendService {
String viewId,
RowId rowId,
) {
final payload = RowIdPB(
final payload = DatabaseViewRowIdPB(
viewId: viewId,
rowId: rowId,
);

View File

@ -220,7 +220,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
}
Future<CalendarEventData<CalendarDayEvent>?> _loadEvent(RowId rowId) async {
final payload = RowIdPB(viewId: viewId, rowId: rowId);
final payload = DatabaseViewRowIdPB(viewId: viewId, rowId: rowId);
return DatabaseEventGetCalendarEvent(payload).send().then((result) {
return result.fold(
(eventPB) => _calendarEventDataFromEventPB(eventPB),

View File

@ -77,7 +77,7 @@ class UnscheduleEventsBloc
Future<CalendarEventPB?> _loadEvent(
RowId rowId,
) async {
final payload = RowIdPB(viewId: viewId, rowId: rowId);
final payload = DatabaseViewRowIdPB(viewId: viewId, rowId: rowId);
return DatabaseEventGetCalendarEvent(payload).send().then(
(result) => result.fold(
(eventPB) => eventPB,

View File

@ -140,12 +140,13 @@ class _TextCellState extends State<TextCardCell> {
}
Widget? _buildIcon(TextCellState state) {
if (state.emoji.isNotEmpty) {
if (state.emoji?.value.isNotEmpty ?? false) {
return Text(
state.emoji,
state.emoji?.value ?? '',
style: widget.style.titleTextStyle,
);
}
if (widget.showNotes) {
return FlowyTooltip(
message: LocaleKeys.board_notesTooltip.tr(),

View File

@ -1,3 +1,4 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
@ -20,26 +21,7 @@ class DesktopGridTextCellSkin extends IEditableTextCellSkin {
padding: GridSize.cellContentInsets,
child: Row(
children: [
BlocBuilder<TextCellBloc, TextCellState>(
buildWhen: (p, c) => p.emoji != c.emoji,
builder: (context, state) {
if (state.emoji.isEmpty) {
return const SizedBox.shrink();
}
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
FlowyText(
state.emoji,
fontSize: 16,
),
const HSpace(6),
],
),
);
},
),
const _IconOrEmoji(),
Expanded(
child: TextField(
controller: textEditingController,
@ -62,3 +44,49 @@ class DesktopGridTextCellSkin extends IEditableTextCellSkin {
);
}
}
class _IconOrEmoji extends StatelessWidget {
const _IconOrEmoji();
@override
Widget build(BuildContext context) {
return BlocBuilder<TextCellBloc, TextCellState>(
builder: (context, state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (state.emoji != null)
ValueListenableBuilder<String>(
valueListenable: state.emoji!,
builder: (context, value, child) {
if (value.isEmpty) {
return const SizedBox.shrink();
} else {
return FlowyText(
value,
fontSize: 16,
);
}
},
),
if (state.hasDocument != null)
ValueListenableBuilder<bool>(
valueListenable: state.hasDocument!,
builder: (context, hasDocument, child) {
if ((state.emoji?.value.isEmpty ?? true) && hasDocument) {
return FlowySvg(
FlowySvgs.notes_s,
color: Theme.of(context).hintColor,
);
} else {
return const SizedBox.shrink();
}
},
),
const HSpace(6),
],
);
},
);
}
}

View File

@ -22,7 +22,7 @@ class MobileGridTextCellSkin extends IEditableTextCellSkin {
buildWhen: (p, c) => p.emoji != c.emoji,
builder: (context, state) => Center(
child: FlowyText.emoji(
state.emoji,
state.emoji?.value ?? "",
fontSize: 15,
optimizeEmojiAlign: true,
),

View File

@ -3,12 +3,13 @@ import {
MoveGroupRowPayloadPB,
MoveRowPayloadPB,
OrderObjectPositionTypePB,
RepeatedRowIdPB,
RowIdPB,
UpdateRowMetaChangesetPB,
} from '@/services/backend';
import {
DatabaseEventCreateRow,
DatabaseEventDeleteRow,
DatabaseEventDeleteRows,
DatabaseEventDuplicateRow,
DatabaseEventGetRowMeta,
DatabaseEventMoveGroupRow,
@ -51,13 +52,12 @@ export async function duplicateRow(viewId: string, rowId: string, groupId?: stri
}
export async function deleteRow(viewId: string, rowId: string, groupId?: string): Promise<void> {
const payload = RowIdPB.fromObject({
const payload = RepeatedRowIdPB.fromObject({
view_id: viewId,
row_id: rowId,
group_id: groupId,
row_ids: [rowId],
});
const result = await DatabaseEventDeleteRow(payload);
const result = await DatabaseEventDeleteRows(payload);
return result.unwrap();
}

View File

@ -262,7 +262,7 @@ impl EventIntegrationTest {
pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB {
EventBuilder::new(self.clone())
.event(DatabaseEvent::GetRow)
.payload(RowIdPB {
.payload(DatabaseViewRowIdPB {
view_id: view_id.to_string(),
row_id: row_id.to_string(),
group_id: None,
@ -275,7 +275,7 @@ impl EventIntegrationTest {
pub async fn get_row_meta(&self, view_id: &str, row_id: &str) -> RowMetaPB {
EventBuilder::new(self.clone())
.event(DatabaseEvent::GetRowMeta)
.payload(RowIdPB {
.payload(DatabaseViewRowIdPB {
view_id: view_id.to_string(),
row_id: row_id.to_string(),
group_id: None,
@ -297,7 +297,7 @@ impl EventIntegrationTest {
pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(DatabaseEvent::DuplicateRow)
.payload(RowIdPB {
.payload(DatabaseViewRowIdPB {
view_id: view_id.to_string(),
row_id: row_id.to_string(),
group_id: None,

View File

@ -288,14 +288,14 @@ async fn update_row_meta_event_with_cover_test() {
// By default the row icon is None.
let row = test.get_row_meta(&grid_view.id, &database.rows[0].id).await;
assert_eq!(row.cover, None);
assert_eq!(row.icon, None);
// Insert cover to the row.
let changeset = UpdateRowMetaChangesetPB {
id: database.rows[0].id.clone(),
view_id: grid_view.id.clone(),
cover_url: Some("cover url".to_owned()),
icon_url: None,
icon_url: Some("cover url".to_owned()),
cover_url: None,
is_document_empty: None,
};
let error = test.update_row_meta(changeset).await;
@ -303,7 +303,7 @@ async fn update_row_meta_event_with_cover_test() {
// Check if the icon is updated.
let row = test.get_row_meta(&grid_view.id, &database.rows[0].id).await;
assert_eq!(row.cover, Some("cover url".to_owned()));
assert_eq!(row.icon, Some("cover url".to_owned()));
}
#[tokio::test]

View File

@ -55,8 +55,8 @@ async fn af_cloud_sync_anon_user_document_test() {
// workspace:
// view: SyncDocument
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 2);
let document_id = views[1].id.clone();
assert_eq!(views.len(), 3);
let document_id = views[2].id.clone();
test.open_document(document_id.clone()).await;
// wait all update are send to the remote

View File

@ -77,11 +77,11 @@ async fn migrate_anon_user_data_to_af_cloud_test() {
assert_eq!(user.authenticator, AuthenticatorPB::AppFlowyCloud);
let user_first_level_views = test.get_all_workspace_views().await;
assert_eq!(user_first_level_views.len(), 2);
assert_eq!(user_first_level_views.len(), 3);
println!("user first level views: {:?}", user_first_level_views);
let user_second_level_views = test
.get_view(&user_first_level_views[1].id)
.get_view(&user_first_level_views[2].id)
.await
.child_views;
println!("user second level views: {:?}", user_second_level_views);
@ -95,11 +95,11 @@ async fn migrate_anon_user_data_to_af_cloud_test() {
assert_eq!(anon_first_level_views.len(), 1);
// the first view of user_first_level_views is the default get started view
assert_eq!(user_first_level_views.len(), 2);
assert_eq!(user_first_level_views.len(), 3);
assert_ne!(anon_first_level_views[0].id, user_first_level_views[1].id);
assert_eq!(
anon_first_level_views[0].name,
user_first_level_views[1].name
user_first_level_views[2].name
);
// check second level

View File

@ -28,17 +28,18 @@ async fn import_appflowy_data_need_migration_test() {
.unwrap();
// after import, the structure is:
// workspace:
// view: Getting Started
// view: Generate
// view: Shared
// view: 037_local
// view: Getting Started
// view: Document1
// view: Document2
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 2);
assert_eq!(views[1].name, import_container_name);
assert_eq!(views.len(), 3);
assert_eq!(views[2].name, import_container_name);
let child_views = test.get_view(&views[1].id).await.child_views;
let child_views = test.get_view(&views[2].id).await.child_views;
assert_eq!(child_views.len(), 1);
let child_views = test.get_view(&child_views[0].id).await.child_views;
@ -81,13 +82,13 @@ async fn import_appflowy_data_folder_into_new_view_test() {
// view: Grid1
// view: Grid2
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 2);
assert_eq!(views[1].name, import_container_name);
assert_eq!(views.len(), 3);
assert_eq!(views[2].name, import_container_name);
// the 040_local should be an empty document, so try to get the document data
let _ = test.get_document_data(&views[1].id).await;
let _ = test.get_document_data(&views[2].id).await;
let local_child_views = test.get_view(&views[1].id).await.child_views;
let local_child_views = test.get_view(&views[2].id).await.child_views;
assert_eq!(local_child_views.len(), 1);
assert_eq!(local_child_views[0].name, "Document1");
@ -139,16 +140,17 @@ async fn import_appflowy_data_folder_into_current_workspace_test() {
.unwrap();
// after import, the structure is:
// workspace:
// view: Getting Started
// view: General
// view: Shared
// view: Document1
// view: Document2
// view: Grid1
// view: Grid2
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 2);
assert_eq!(views[1].name, "Document1");
assert_eq!(views.len(), 3);
assert_eq!(views[2].name, "Document1");
let document_1_child_views = test.get_view(&views[1].id).await.child_views;
let document_1_child_views = test.get_view(&views[2].id).await.child_views;
assert_eq!(document_1_child_views.len(), 1);
assert_eq!(document_1_child_views[0].name, "Document2");
@ -179,9 +181,9 @@ async fn import_appflowy_data_folder_into_new_view_test2() {
.unwrap();
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 2);
assert_eq!(views[1].name, import_container_name);
assert_040_local_2_import_content(&test, &views[1].id).await;
assert_eq!(views.len(), 3);
assert_eq!(views[2].name, import_container_name);
assert_040_local_2_import_content(&test, &views[2].id).await;
drop(cleaner);
}
@ -226,13 +228,14 @@ async fn import_appflowy_data_folder_multiple_times_test() {
.await
.unwrap();
// after import, the structure is:
// Getting Started
// General
// Shared
// 040_local_2
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 2);
assert_eq!(views[1].name, import_container_name);
assert_040_local_2_import_content(&test, &views[1].id).await;
assert_eq!(views.len(), 3);
assert_eq!(views[2].name, import_container_name);
assert_040_local_2_import_content(&test, &views[2].id).await;
test
.import_appflowy_data(
@ -242,16 +245,17 @@ async fn import_appflowy_data_folder_multiple_times_test() {
.await
.unwrap();
// after import, the structure is:
// Getting Started
// Generate
// Shared
// 040_local_2
// Getting started
// 040_local_2
// Getting started
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3);
assert_eq!(views[2].name, import_container_name);
assert_040_local_2_import_content(&test, &views[1].id).await;
assert_eq!(views.len(), 4);
assert_eq!(views[3].name, import_container_name);
assert_040_local_2_import_content(&test, &views[2].id).await;
assert_040_local_2_import_content(&test, &views[3].id).await;
drop(cleaner);
}

View File

@ -101,16 +101,17 @@ async fn af_cloud_open_workspace_test() {
user_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
let _ = test.af_cloud_sign_up().await;
let default_document_name = "Getting started";
let default_document_name = "General";
test.create_document("A").await;
test.create_document("B").await;
let first_workspace = test.get_current_workspace().await;
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3);
assert_eq!(views.len(), 4);
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "A");
assert_eq!(views[2].name, "B");
assert_eq!(views[1].name, "Shared");
assert_eq!(views[2].name, "A");
assert_eq!(views[3].name, "B");
let user_workspace = test.create_workspace("second workspace").await;
test.open_workspace(&user_workspace.workspace_id).await;
@ -119,10 +120,11 @@ async fn af_cloud_open_workspace_test() {
test.create_document("D").await;
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3);
assert_eq!(views.len(), 4);
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "C");
assert_eq!(views[2].name, "D");
assert_eq!(views[1].name, "Shared");
assert_eq!(views[2].name, "C");
assert_eq!(views[3].name, "D");
// simulate open workspace and check if the views are correct
for i in 0..10 {
@ -144,14 +146,16 @@ async fn af_cloud_open_workspace_test() {
test.open_workspace(&first_workspace.id).await;
let views_1 = test.get_all_workspace_views().await;
assert_eq!(views_1[0].name, default_document_name);
assert_eq!(views_1[1].name, "A");
assert_eq!(views_1[2].name, "B");
assert_eq!(views_1[1].name, "Shared");
assert_eq!(views_1[2].name, "A");
assert_eq!(views_1[3].name, "B");
test.open_workspace(&second_workspace.id).await;
let views_2 = test.get_all_workspace_views().await;
assert_eq!(views_2[0].name, default_document_name);
assert_eq!(views_2[1].name, "C");
assert_eq!(views_2[2].name, "D");
assert_eq!(views_2[1].name, "Shared");
assert_eq!(views_2[2].name, "C");
assert_eq!(views_2[3].name, "D");
}
#[tokio::test]
@ -174,7 +178,7 @@ async fn af_cloud_different_open_same_workspace_test() {
let views = client.get_all_workspace_views().await;
// only the getting started view should be present
assert_eq!(views.len(), 1);
assert_eq!(views.len(), 2);
for view in views {
client.delete_view(&view.id).await;
}
@ -205,7 +209,7 @@ async fn af_cloud_different_open_same_workspace_test() {
client.open_workspace(iter_workspace_id).await;
if iter_workspace_id == &cloned_shared_workspace_id {
let views = client.get_all_workspace_views().await;
assert_eq!(views.len(), 1);
assert_eq!(views.len(), 2);
sleep(Duration::from_millis(300)).await;
} else {
let views = client.get_all_workspace_views().await;
@ -242,6 +246,6 @@ async fn af_cloud_different_open_same_workspace_test() {
let folder_workspace_id = folder.get_workspace_id();
assert_eq!(folder_workspace_id, Some(shared_workspace_id));
assert_eq!(views.len(), 1, "only get: {:?}", views); // Expecting two views.
assert_eq!(views[0].name, "Getting started");
assert_eq!(views.len(), 2, "only get: {:?}", views); // Expecting two views.
assert_eq!(views[0].name, "General");
}

View File

@ -62,9 +62,6 @@ pub struct RowMetaPB {
pub icon: Option<String>,
#[pb(index = 4, one_of)]
pub cover: Option<String>,
#[pb(index = 5, one_of)]
pub is_document_empty: Option<bool>,
}
@ -80,7 +77,6 @@ impl From<RowOrder> for RowMetaPB {
id: data.id.into_inner(),
document_id: None,
icon: None,
cover: None,
is_document_empty: None,
}
}
@ -92,7 +88,6 @@ impl From<Row> for RowMetaPB {
id: data.id.into_inner(),
document_id: None,
icon: None,
cover: None,
is_document_empty: None,
}
}
@ -104,7 +99,6 @@ impl From<RowDetail> for RowMetaPB {
id: row_detail.row.id.to_string(),
document_id: Some(row_detail.document_id),
icon: row_detail.meta.icon_url,
cover: row_detail.meta.cover_url,
is_document_empty: Some(row_detail.meta.is_document_empty),
}
}
@ -305,7 +299,7 @@ impl From<UpdatedRow> for UpdatedRowPB {
}
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct RowIdPB {
pub struct DatabaseViewRowIdPB {
#[pb(index = 1)]
pub view_id: String,
@ -322,7 +316,7 @@ pub struct RowIdParams {
pub group_id: Option<String>,
}
impl TryInto<RowIdParams> for RowIdPB {
impl TryInto<RowIdParams> for DatabaseViewRowIdPB {
type Error = ErrorCode;
fn try_into(self) -> Result<RowIdParams, Self::Error> {

View File

@ -404,7 +404,7 @@ pub(crate) async fn move_field_handler(
// #[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn get_row_handler(
data: AFPluginData<RowIdPB>,
data: AFPluginData<DatabaseViewRowIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> DataResult<OptionalRowPB, FlowyError> {
let manager = upgrade_manager(manager)?;
@ -420,7 +420,7 @@ pub(crate) async fn get_row_handler(
}
pub(crate) async fn init_row_handler(
data: AFPluginData<RowIdPB>,
data: AFPluginData<DatabaseViewRowIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
@ -433,7 +433,7 @@ pub(crate) async fn init_row_handler(
}
pub(crate) async fn get_row_meta_handler(
data: AFPluginData<RowIdPB>,
data: AFPluginData<DatabaseViewRowIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> DataResult<RowMetaPB, FlowyError> {
let manager = upgrade_manager(manager)?;
@ -487,7 +487,7 @@ pub(crate) async fn delete_rows_handler(
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn duplicate_row_handler(
data: AFPluginData<RowIdPB>,
data: AFPluginData<DatabaseViewRowIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
@ -960,7 +960,7 @@ pub(crate) async fn get_no_date_calendar_events_handler(
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn get_calendar_event_handler(
data: AFPluginData<RowIdPB>,
data: AFPluginData<DatabaseViewRowIdPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> DataResult<CalendarEventPB, FlowyError> {
let manager = upgrade_manager(manager)?;

View File

@ -14,7 +14,6 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
.state(database_manager);
plugin
.event(DatabaseEvent::GetDatabase, get_database_data_handler)
.event(DatabaseEvent::GetAllRows, get_all_rows_handler)
.event(DatabaseEvent::GetDatabaseData, get_database_data_handler)
.event(DatabaseEvent::GetDatabaseId, get_database_id_handler)
.event(DatabaseEvent::GetDatabaseSetting, get_database_setting_handler)
@ -224,19 +223,19 @@ pub enum DatabaseEvent {
/// [GetRow] event is used to get the row data,[RowPB]. [OptionalRowPB] is a wrapper that enables
/// to return a nullable row data.
#[event(input = "RowIdPB", output = "OptionalRowPB")]
#[event(input = "DatabaseViewRowIdPB", output = "OptionalRowPB")]
GetRow = 51,
#[event(input = "RepeatedRowIdPB")]
DeleteRows = 52,
#[event(input = "RowIdPB")]
#[event(input = "DatabaseViewRowIdPB")]
DuplicateRow = 53,
#[event(input = "MoveRowPayloadPB")]
MoveRow = 54,
#[event(input = "RowIdPB", output = "RowMetaPB")]
#[event(input = "DatabaseViewRowIdPB", output = "RowMetaPB")]
GetRowMeta = 55,
#[event(input = "UpdateRowMetaChangesetPB")]
@ -321,7 +320,7 @@ pub enum DatabaseEvent {
)]
GetNoDateCalendarEvents = 124,
#[event(input = "RowIdPB", output = "CalendarEventPB")]
#[event(input = "DatabaseViewRowIdPB", output = "CalendarEventPB")]
GetCalendarEvent = 125,
#[event(input = "MoveCalendarEventPB")]
@ -381,12 +380,9 @@ pub enum DatabaseEvent {
#[event(input = "TranslateRowPB")]
TranslateRow = 175,
#[event(input = "RowIdPB")]
#[event(input = "DatabaseViewRowIdPB")]
InitRow = 176,
#[event(input = "DatabaseViewIdPB", output = "RepeatedRowMetaPB")]
GetAllRows = 177,
#[event(input = "DatabaseViewIdPB", output = "DatabaseExportDataPB")]
ExportRawDatabaseData = 178,
}

View File

@ -750,7 +750,6 @@ impl DatabaseEditor {
id: row_id.clone().into_inner(),
document_id: Some(row_document_id),
icon: row_meta.icon_url,
cover: row_meta.cover_url,
is_document_empty: Some(row_meta.is_document_empty),
})
} else {
@ -1522,6 +1521,7 @@ impl DatabaseEditor {
Ok(type_option.database_id)
}
/// TODO(nathan): lazy load database rows
pub async fn get_related_rows(
&self,
row_ids: Option<&Vec<String>>,