Skip to content

Commit d3ce59f

Browse files
authored
fix: v0.9.2 launch review issues (AppFlowy-IO#7898)
* fix: remove empty paragraphs after inserting file block * fix: chat page animation * fix: remove opacity from locked page icon * feat: open the space after opening the view * fix: chat page loading issue * fix: view title assertion * fix: cover title issue * fix: flutter analyze * fix: list state issue * fix: reverse animation list issue * fix: integration test
1 parent 0f89b6f commit d3ce59f

File tree

11 files changed

+156
-58
lines changed

11 files changed

+156
-58
lines changed

frontend/appflowy_flutter/ios/Podfile.lock

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ PODS:
7171
- SDWebImage (5.21.0):
7272
- SDWebImage/Core (= 5.21.0)
7373
- SDWebImage/Core (5.21.0)
74-
- Sentry/HybridSDK (8.46.0)
75-
- sentry_flutter (8.14.2):
76-
- Flutter
77-
- FlutterMacOS
78-
- Sentry/HybridSDK (= 8.46.0)
7974
- share_plus (0.0.1):
8075
- Flutter
8176
- shared_preferences_foundation (0.0.1):
@@ -112,7 +107,6 @@ DEPENDENCIES:
112107
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
113108
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
114109
- saver_gallery (from `.symlinks/plugins/saver_gallery/ios`)
115-
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
116110
- share_plus (from `.symlinks/plugins/share_plus/ios`)
117111
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
118112
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
@@ -126,7 +120,6 @@ SPEC REPOS:
126120
- DKPhotoGallery
127121
- ReachabilitySwift
128122
- SDWebImage
129-
- Sentry
130123
- SwiftyGif
131124
- Toast
132125

@@ -165,8 +158,6 @@ EXTERNAL SOURCES:
165158
:path: ".symlinks/plugins/permission_handler_apple/ios"
166159
saver_gallery:
167160
:path: ".symlinks/plugins/saver_gallery/ios"
168-
sentry_flutter:
169-
:path: ".symlinks/plugins/sentry_flutter/ios"
170161
share_plus:
171162
:path: ".symlinks/plugins/share_plus/ios"
172163
shared_preferences_foundation:
@@ -202,8 +193,6 @@ SPEC CHECKSUMS:
202193
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
203194
saver_gallery: af2d0c762dafda254e0ad025ef0dabd6506cd490
204195
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
205-
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
206-
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
207196
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
208197
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
209198
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0

frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/animated_chat_list.dart

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import 'dart:async';
44
import 'dart:math';
55

6-
import 'package:appflowy_backend/log.dart';
6+
import 'package:appflowy/util/debounce.dart';
77
import 'package:diffutil_dart/diffutil.dart' as diffutil;
88
import 'package:flowy_infra_ui/widget/spacing.dart';
99
import 'package:flutter/material.dart';
@@ -43,7 +43,6 @@ class ChatAnimatedList extends StatefulWidget {
4343

4444
class ChatAnimatedListState extends State<ChatAnimatedList>
4545
with SingleTickerProviderStateMixin {
46-
final GlobalKey<SliverAnimatedListState> _listKey = GlobalKey();
4746
late final ChatController _chatController = Provider.of<ChatController>(
4847
context,
4948
listen: false,
@@ -69,6 +68,10 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
6968
int _lastUserMessageIndex = 0;
7069
bool _isScrollingToBottom = false;
7170

71+
final _loadPreviousMessagesDebounce = Debounce(
72+
duration: const Duration(milliseconds: 200),
73+
);
74+
7275
@override
7376
void initState() {
7477
super.initState();
@@ -134,29 +137,41 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
134137
itemPositionsListener.itemPositions.addListener(() {
135138
_handleToggleScrollToBottom();
136139
});
140+
141+
itemPositionsListener.itemPositions.addListener(() {
142+
_handleLoadPreviousMessages();
143+
});
137144
}
138145

139146
@override
140147
void dispose() {
141-
super.dispose();
142-
143148
_scrollToBottomShowTimer?.cancel();
144149
_scrollToBottomController.dispose();
145150
_operationsSubscription.cancel();
151+
152+
super.dispose();
146153
}
147154

148155
@override
149156
Widget build(BuildContext context) {
150157
final builders = context.watch<Builders>();
151158
final height = MediaQuery.of(context).size.height;
152159

153-
return Stack(
160+
// A trick to avoid the first message being scrolled to the top
161+
int initialScrollIndex = _chatController.messages.length;
162+
double initialAlignment = 1.0;
163+
if (_chatController.messages.length <= 2) {
164+
initialScrollIndex = 0;
165+
initialAlignment = 0.0;
166+
}
167+
168+
final Widget child = Stack(
154169
children: [
155170
ScrollablePositionedList.builder(
156171
scrollOffsetController: scrollOffsetController,
157172
itemScrollController: itemScrollController,
158-
initialScrollIndex: _chatController.messages.length,
159-
initialAlignment: 1,
173+
initialScrollIndex: initialScrollIndex,
174+
initialAlignment: initialAlignment,
160175
scrollOffsetListener: scrollOffsetListener,
161176
itemPositionsListener: itemPositionsListener,
162177
physics: ClampingScrollPhysics(),
@@ -165,7 +180,7 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
165180
itemCount: _chatController.messages.length + 1,
166181
itemBuilder: (context, index) {
167182
if (index == _chatController.messages.length) {
168-
return VSpace(height - 400);
183+
return VSpace(height - 360);
169184
}
170185

171186
final message = _chatController.messages[index];
@@ -192,6 +207,8 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
192207
),
193208
],
194209
);
210+
211+
return child;
195212
}
196213

197214
void _scrollLastMessageToTop(Message data) {
@@ -202,7 +219,6 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
202219

203220
if (_lastUserMessageIndex != lastUserMessageIndex) {
204221
// scroll the current message to the top
205-
Log.info('scrolling the last message to the top');
206222
itemScrollController.scrollTo(
207223
index: lastUserMessageIndex,
208224
duration: const Duration(milliseconds: 300),
@@ -238,9 +254,15 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
238254
// get the max item
239255
final sortedItems = itemPositionsListener.itemPositions.value.toList()
240256
..sort((a, b) => a.index.compareTo(b.index));
241-
final maxItem = sortedItems.last;
257+
final maxItem = sortedItems.lastOrNull;
258+
259+
if (maxItem == null) {
260+
return;
261+
}
242262

243-
if (maxItem.index >= _chatController.messages.length - 1) {
263+
if (maxItem.index > _chatController.messages.length - 1 ||
264+
(maxItem.index == _chatController.messages.length - 1 &&
265+
maxItem.itemTrailingEdge <= 1.01)) {
244266
_scrollToBottomShowTimer?.cancel();
245267
_scrollToBottomController.reverse();
246268
return;
@@ -254,33 +276,31 @@ class ChatAnimatedListState extends State<ChatAnimatedList>
254276
});
255277
}
256278

279+
void _handleLoadPreviousMessages() {
280+
final sortedItems = itemPositionsListener.itemPositions.value.toList()
281+
..sort((a, b) => a.index.compareTo(b.index));
282+
final minItem = sortedItems.firstOrNull;
283+
284+
if (minItem == null || minItem.index > 0 || minItem.itemLeadingEdge < 0) {
285+
return;
286+
}
287+
288+
_loadPreviousMessagesDebounce.call(
289+
() {
290+
widget.onLoadPreviousMessages?.call();
291+
},
292+
);
293+
}
294+
257295
void _onInserted(final int position, final Message data) {
258296
if (position == _oldList.length) {
259297
_scrollLastMessageToTop(data);
260298
}
261299
}
262300

263-
void _onRemoved(final int position, final Message data) {
264-
final visualPosition = max(_oldList.length - position - 1, 0);
265-
_listKey.currentState!.removeItem(
266-
visualPosition,
267-
(context, animation) => widget.itemBuilder(
268-
context,
269-
animation,
270-
data,
271-
isRemoved: true,
272-
),
273-
duration: widget.removeAnimationDuration,
274-
);
275-
}
301+
void _onRemoved(final int position, final Message data) {}
276302

277-
void _onChanged(int position, Message oldData, Message newData) {
278-
_onRemoved(position, oldData);
279-
_listKey.currentState!.insertItem(
280-
max(_oldList.length - position - 1, 0),
281-
duration: widget.insertAnimationDuration,
282-
);
283-
}
303+
void _onChanged(int position, Message oldData, Message newData) {}
284304

285305
void _onDiffUpdate(diffutil.DataDiffUpdate<Message> update) {
286306
update.when<void>(

frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/animated_chat_list_reversed.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class ChatAnimatedListReversedState extends State<ChatAnimatedListReversed>
168168
slivers: <Widget>[
169169
SliverPadding(
170170
padding: EdgeInsets.only(
171-
top: 400,
171+
top: widget.bottomPadding ?? 0,
172172
),
173173
),
174174
SliverAnimatedList(

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_selection_menu.dart

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import 'package:flutter/material.dart';
2-
31
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
42
import 'package:appflowy_editor/appflowy_editor.dart';
3+
import 'package:flutter/material.dart';
54

65
extension InsertFile on EditorState {
76
Future<void> insertEmptyFileBlock(GlobalKey key) async {
@@ -17,11 +16,18 @@ extension InsertFile on EditorState {
1716
}
1817
final file = fileNode(url: '')..extraInfos = {'global_key': key};
1918

20-
final insertedPath = delta.isEmpty ? path : path.next;
21-
final transaction = this.transaction
22-
..insertNode(insertedPath, file)
23-
..insertNode(insertedPath, paragraphNode())
24-
..afterSelection = Selection.collapsed(Position(path: insertedPath.next));
19+
final transaction = this.transaction;
20+
21+
// if current node is a paragraph and empty, replace it with the file block
22+
if (delta.isEmpty && node.type == ParagraphBlockKeys.type) {
23+
final insertedPath = path;
24+
transaction.insertNode(insertedPath, file);
25+
transaction.deleteNode(node);
26+
} else {
27+
// otherwise, insert the file block after the current node
28+
final insertedPath = path.next;
29+
transaction.insertNode(insertedPath, file);
30+
}
2531

2632
return apply(transaction);
2733
}

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/cover_title.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class _InnerCoverTitleState extends State<_InnerCoverTitle> {
5252
late final titleFocusNode = editorContext.coverTitleFocusNode;
5353
int lineCount = 1;
5454

55+
bool updatingViewName = false;
56+
5557
@override
5658
void initState() {
5759
super.initState();
@@ -87,7 +89,7 @@ class _InnerCoverTitleState extends State<_InnerCoverTitle> {
8789
final width = context.read<DocumentAppearanceCubit>().state.width;
8890
return BlocConsumer<ViewBloc, ViewState>(
8991
listenWhen: (previous, current) =>
90-
previous.view.name != current.view.name,
92+
previous.view.name != current.view.name && !updatingViewName,
9193
listener: _onListen,
9294
builder: (context, state) {
9395
final appearance = context.read<DocumentAppearanceCubit>().state;
@@ -206,6 +208,8 @@ class _InnerCoverTitleState extends State<_InnerCoverTitle> {
206208
}
207209

208210
void _onViewNameChanged() {
211+
updatingViewName = true;
212+
209213
Debounce.debounce(
210214
'update view name',
211215
const Duration(milliseconds: 250),
@@ -222,6 +226,8 @@ class _InnerCoverTitleState extends State<_InnerCoverTitle> {
222226
context
223227
.read<ViewInfoBloc?>()
224228
?.add(ViewInfoEvent.titleChanged(titleTextController.text));
229+
230+
updatingViewName = false;
225231
},
226232
);
227233
}

frontend/appflowy_flutter/lib/workspace/presentation/home/desktop_home_screen.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'package:appflowy_backend/log.dart';
2323
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
2424
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
2525
show UserProfilePB;
26+
import 'package:collection/collection.dart';
2627
import 'package:flowy_infra_ui/style_widget/container.dart';
2728
import 'package:flutter/material.dart';
2829
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -116,6 +117,9 @@ class DesktopHomeScreen extends StatelessWidget {
116117
TabsEvent.openPlugin(plugin: view.plugin()),
117118
);
118119
}
120+
121+
// switch to the space that contains the last opened view
122+
_switchToSpace(view);
119123
}
120124
},
121125
child: BlocBuilder<HomeSettingBloc, HomeSettingState>(
@@ -290,6 +294,16 @@ class DesktopHomeScreen extends StatelessWidget {
290294
],
291295
);
292296
}
297+
298+
Future<void> _switchToSpace(ViewPB view) async {
299+
final ancestors = await ViewBackendService.getViewAncestors(view.id);
300+
final space = ancestors.fold(
301+
(ancestors) =>
302+
ancestors.items.firstWhereOrNull((ancestor) => ancestor.isSpace),
303+
(error) => null,
304+
);
305+
switchToSpaceIdNotifier.value = space;
306+
}
293307
}
294308

295309
class DesktopHomeScreenStackAdaptor extends HomeStackDelegate {

frontend/appflowy_flutter/lib/workspace/presentation/home/hotkeys.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:appflowy/workspace/application/sidebar/rename_view/rename_view_b
88
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
99
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart';
1010
import 'package:appflowy_backend/log.dart';
11+
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
1112
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
1213
import 'package:flutter/material.dart';
1314
import 'package:hotkey_manager/hotkey_manager.dart';
@@ -18,6 +19,7 @@ typedef KeyDownHandler = void Function(HotKey hotKey);
1819

1920
ValueNotifier<int> switchToTheNextSpace = ValueNotifier(0);
2021
ValueNotifier<int> createNewPageNotifier = ValueNotifier(0);
22+
ValueNotifier<ViewPB?> switchToSpaceIdNotifier = ValueNotifier(null);
2123

2224
@visibleForTesting
2325
final zoomInKeyCodes = [KeyCode.equal, KeyCode.numpadAdd, KeyCode.add];

frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,17 @@ class _SpaceState extends State<_Space> {
7373
@override
7474
void initState() {
7575
super.initState();
76+
7677
switchToTheNextSpace.addListener(_switchToNextSpace);
78+
switchToSpaceIdNotifier.addListener(_switchToSpace);
7779
}
7880

7981
@override
8082
void dispose() {
8183
switchToTheNextSpace.removeListener(_switchToNextSpace);
8284
isHovered.dispose();
8385
isExpandedNotifier.dispose();
86+
8487
super.dispose();
8588
}
8689

@@ -175,4 +178,17 @@ class _SpaceState extends State<_Space> {
175178
void _switchToNextSpace() {
176179
context.read<SpaceBloc>().add(const SpaceEvent.switchToNextSpace());
177180
}
181+
182+
void _switchToSpace() {
183+
if (!mounted || !context.mounted) {
184+
return;
185+
}
186+
187+
final space = switchToSpaceIdNotifier.value;
188+
if (space == null) {
189+
return;
190+
}
191+
192+
context.read<SpaceBloc>().add(SpaceEvent.open(space));
193+
}
178194
}

frontend/appflowy_flutter/lib/workspace/presentation/widgets/more_view_actions/widgets/lock_page_action.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,7 @@ class LockPageButtonWrapper extends StatelessWidget {
109109
return FlowyTooltip(
110110
message: LocaleKeys.lockPage_lockedOperationTooltip.tr(),
111111
child: IgnorePointer(
112-
child: Opacity(
113-
opacity: 0.5,
114-
child: child,
115-
),
112+
child: child,
116113
),
117114
);
118115
}

0 commit comments

Comments
 (0)