Skip to content

Commit

Permalink
fix: launch review 0.5.9 (#5443)
Browse files Browse the repository at this point in the history
* fix: lose focus in editor on open settings dialog

* fix: support CTRL+. for sidebar toggle

* fix: make notify method private

* fix: copy for video block

* fix: copy for notification setting

* fix: add libmpv to appimage builder

* fix: missing tabs bloc from context

* ci: add libmpv-dev to missing workflows

* fix: do not depend on inherited widget in dispose

* test: add media kit ensureInitialized to integration tests

* fix: use maybeOf for AFFocusManager

* fix: use pattern matching for youtube error

* fix: missed null-promise on convertion
  • Loading branch information
Xazin authored Jun 2, 2024
1 parent 2c0cdfa commit 4ad7c48
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .github/actions/flutter_build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ runs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
elif [ "$RUNNER_OS" == "Windows" ]; then
vcpkg integrate install
elif [ "$RUNNER_OS" == "macOS" ]; then
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/flutter_integration_test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ runs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager libmpv-dev mpv
shell: bash

- name: Enable Flutter Desktop
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,8 @@ jobs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo apt-get update
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0 libnotify-dev
sudo apt-get -y install alien
sudo apt-get install keybinder-3.0
sudo apt-get install -y alien libnotify-dev libmpv-dev mpv
source $HOME/.cargo/env
cargo install --force cargo-make
cargo install --force duckscript_cli
Expand Down
3 changes: 3 additions & 0 deletions frontend/appflowy_flutter/integration_test/shared/base.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';

import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';

Expand Down Expand Up @@ -36,6 +37,8 @@ extension AppFlowyTestBase on WidgetTester {
AuthenticatorType? cloudType,
String? email,
}) async {
VideoBlockKit.ensureInitialized();

if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
// Set the window size
await binding.setSurfaceSize(windowSize);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_configuration.dart';
Expand All @@ -20,15 +23,14 @@ import 'package:appflowy/plugins/inline_actions/inline_actions_service.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart';
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
import 'package:appflowy/workspace/presentation/home/af_focus_manager.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

final codeBlockLocalization = CodeBlockLocalizations(
Expand Down Expand Up @@ -211,6 +213,10 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
style: styleCustomizer.selectionMenuStyleBuilder(),
).handler(editorState);

AFFocusManager? focusManager;

void _loseFocus() => widget.editorState.selection = null;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -251,17 +257,35 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
// customize the dynamic theme color
_customizeBlockComponentBackgroundColorDecorator();

if (widget.initialSelection != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.editorState.updateSelectionWithReason(
widget.initialSelection,
);
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) {
return;
}

focusManager = AFFocusManager.maybeOf(context);
focusManager?.loseFocusNotifier.addListener(_loseFocus);

if (widget.initialSelection != null) {
widget.editorState.updateSelectionWithReason(widget.initialSelection);
}
});
}

@override
void didChangeDependencies() {
final currFocusManager = AFFocusManager.maybeOf(context);
if (focusManager != currFocusManager) {
focusManager?.loseFocusNotifier.removeListener(_loseFocus);
focusManager = currFocusManager;
focusManager?.loseFocusNotifier.addListener(_loseFocus);
}
super.didChangeDependencies();
}

@override
void dispose() {
focusManager?.loseFocusNotifier.removeListener(_loseFocus);

if (widget.useViewInfoBloc && !viewInfoBloc.isClosed) {
viewInfoBloc.add(const ViewInfoEvent.unregisterEditorState());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class _EmbedUrl extends StatefulWidget {

class _EmbedUrlState extends State<_EmbedUrl> {
bool isUrlValid = true;
bool isYouTubeError = false;
String inputText = '';

@override
Expand All @@ -60,11 +61,18 @@ class _EmbedUrlState extends State<_EmbedUrl> {
if (!isUrlValid) ...[
const VSpace(8),
FlowyText(
LocaleKeys.document_plugins_video_invalidVideoUrl.tr(),
isYouTubeError
? LocaleKeys.document_plugins_video_invalidVideoUrlYouTube.tr()
: LocaleKeys.document_plugins_video_invalidVideoUrl.tr(),
color: Theme.of(context).colorScheme.error,
),
],
const VSpace(8),
FlowyText(
LocaleKeys.document_plugins_video_supportedFormats.tr(),
color: Theme.of(context).hintColor,
),
const VSpace(8),
SizedBox(
width: 160,
child: FlowyButton(
Expand All @@ -86,6 +94,7 @@ class _EmbedUrlState extends State<_EmbedUrl> {
return widget.onSubmit(inputText);
}

isYouTubeError = youtubeUrlRegex.hasMatch(inputText);
setState(() => isUrlValid = false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ const _videoUrlPattern =
r'(https?:\/\/)([^\s(["<,>/]*)(\/)[^\s[",><]*(.mp4|.mov|.avi|.webm|.flv|.m4v|.mpeg|.h264)(\?[^\s[",><]*)?';
final videoUrlRegex = RegExp(_videoUrlPattern);

/// This pattern matches both youtube.com and shortened youtu.be urls.
///
const _youtubeUrlPattern = r'^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/';
final youtubeUrlRegex = RegExp(_youtubeUrlPattern);

const _appflowyCloudUrlPattern = r'^(https:\/\/)(.*)(\.appflowy\.cloud\/)(.*)';
final appflowyCloudUrlRegex = RegExp(_appflowyCloudUrlPattern);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';

/// Simple ChangeNotifier that can be listened to, notifies the
/// application on events that should trigger focus loss.
///
/// Eg. lose focus in AppFlowyEditor
///
abstract class ShouldLoseFocus with ChangeNotifier {}

/// Private implementation to allow the [AFFocusManager] to
/// call [notifyListeners] without being directly invokable.
///
class _ShouldLoseFocusImpl extends ShouldLoseFocus {
void notify() => notifyListeners();
}

class AFFocusManager extends InheritedWidget {
AFFocusManager({super.key, required super.child});

final ShouldLoseFocus loseFocusNotifier = _ShouldLoseFocusImpl();

void notifyLoseFocus() {
(loseFocusNotifier as _ShouldLoseFocusImpl).notify();
}

@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;

static AFFocusManager of(BuildContext context) {
final AFFocusManager? result =
context.dependOnInheritedWidgetOfExactType<AFFocusManager>();

assert(result != null, "AFFocusManager could not be found");
return result!;
}

static AFFocusManager? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<AFFocusManager>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy/workspace/presentation/home/af_focus_manager.dart';
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
import 'package:appflowy/workspace/presentation/home/hotkeys.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar.dart';
Expand Down Expand Up @@ -67,62 +68,68 @@ class DesktopHomeScreen extends StatelessWidget {
return const WorkspaceFailedScreen();
}

return MultiBlocProvider(
key: ValueKey(userProfile.id),
providers: [
BlocProvider<ReminderBloc>.value(value: getIt<ReminderBloc>()),
BlocProvider<TabsBloc>.value(value: getIt<TabsBloc>()),
BlocProvider<HomeBloc>(
create: (_) =>
HomeBloc(workspaceSetting)..add(const HomeEvent.initial()),
),
BlocProvider<HomeSettingBloc>(
create: (_) => HomeSettingBloc(
workspaceSetting,
context.read<AppearanceSettingsCubit>(),
context.widthPx,
)..add(const HomeSettingEvent.initial()),
),
BlocProvider<FavoriteBloc>(
create: (context) =>
FavoriteBloc()..add(const FavoriteEvent.initial()),
),
],
child: Scaffold(
floatingActionButton: enableMemoryLeakDetect
? const FloatingActionButton(
onPressed: dumpMemoryLeak,
child: Icon(Icons.memory),
)
: null,
body: BlocListener<HomeBloc, HomeState>(
listenWhen: (p, c) => p.latestView != c.latestView,
listener: (context, state) {
final view = state.latestView;
if (view != null) {
// Only open the last opened view if the [TabsState.currentPageManager] current opened plugin is blank and the last opened view is not null.
// All opened widgets that display on the home screen are in the form of plugins. There is a list of built-in plugins defined in the [PluginType] enum, including board, grid and trash.
final currentPageManager =
context.read<TabsBloc>().state.currentPageManager;
return AFFocusManager(
child: MultiBlocProvider(
key: ValueKey(userProfile.id),
providers: [
BlocProvider<ReminderBloc>.value(value: getIt<ReminderBloc>()),
BlocProvider<TabsBloc>.value(value: getIt<TabsBloc>()),
BlocProvider<HomeBloc>(
create: (_) =>
HomeBloc(workspaceSetting)..add(const HomeEvent.initial()),
),
BlocProvider<HomeSettingBloc>(
create: (_) => HomeSettingBloc(
workspaceSetting,
context.read<AppearanceSettingsCubit>(),
context.widthPx,
)..add(const HomeSettingEvent.initial()),
),
BlocProvider<FavoriteBloc>(
create: (context) =>
FavoriteBloc()..add(const FavoriteEvent.initial()),
),
],
child: Scaffold(
floatingActionButton: enableMemoryLeakDetect
? const FloatingActionButton(
onPressed: dumpMemoryLeak,
child: Icon(Icons.memory),
)
: null,
body: BlocListener<HomeBloc, HomeState>(
listenWhen: (p, c) => p.latestView != c.latestView,
listener: (context, state) {
final view = state.latestView;
if (view != null) {
// Only open the last opened view if the [TabsState.currentPageManager] current opened plugin is blank and the last opened view is not null.
// All opened widgets that display on the home screen are in the form of plugins. There is a list of built-in plugins defined in the [PluginType] enum, including board, grid and trash.
final currentPageManager =
context.read<TabsBloc>().state.currentPageManager;

if (currentPageManager.plugin.pluginType ==
PluginType.blank) {
getIt<TabsBloc>().add(
TabsEvent.openPlugin(plugin: view.plugin()),
);
if (currentPageManager.plugin.pluginType ==
PluginType.blank) {
getIt<TabsBloc>().add(
TabsEvent.openPlugin(plugin: view.plugin()),
);
}
}
}
},
child: BlocBuilder<HomeSettingBloc, HomeSettingState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) => BlocProvider(
create: (_) => UserWorkspaceBloc(userProfile: userProfile)
..add(const UserWorkspaceEvent.initial()),
child: HomeHotKeys(
userProfile: userProfile,
child: FlowyContainer(
Theme.of(context).colorScheme.surface,
child: _buildBody(context, userProfile, workspaceSetting),
},
child: BlocBuilder<HomeSettingBloc, HomeSettingState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) => BlocProvider(
create: (_) => UserWorkspaceBloc(userProfile: userProfile)
..add(const UserWorkspaceEvent.initial()),
child: HomeHotKeys(
userProfile: userProfile,
child: FlowyContainer(
Theme.of(context).colorScheme.surface,
child: _buildBody(
context,
userProfile,
workspaceSetting,
),
),
),
),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:io';

import 'package:flutter/material.dart';

import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
Expand All @@ -9,7 +11,6 @@ import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flutter/material.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:provider/provider.dart';
import 'package:scaled_app/scaled_app.dart';
Expand Down Expand Up @@ -54,13 +55,24 @@ class _HomeHotKeysState extends State<HomeHotKeys> {
final windowSizeManager = WindowSizeManager();

late final items = [
// Collapse sidebar menu
// Collapse sidebar menu (using slash)
HotKeyItem(
hotKey: HotKey(
KeyCode.backslash,
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
scope: HotKeyScope.inapp,
),
keyDownHandler: (_) => context
.read<HomeSettingBloc>()
.add(const HomeSettingEvent.collapseMenu()),
),

// Collapse sidebar menu (using .)
HotKeyItem(
hotKey: HotKey(
Platform.isMacOS ? KeyCode.period : KeyCode.backslash,
KeyCode.period,
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
// Set hotkey scope (default is HotKeyScope.system)
scope: HotKeyScope.inapp, // Set as inapp-wide hotkey.
scope: HotKeyScope.inapp,
),
keyDownHandler: (_) => context
.read<HomeSettingBloc>()
Expand Down
Loading

0 comments on commit 4ad7c48

Please sign in to comment.