Skip to content

Commit

Permalink
feat: integrate auto_updater for macOS (#7328)
Browse files Browse the repository at this point in the history
* feat: integrate auto_updater in macOS

* chore: update translations

* chore: bump auto_updater version

* feat: exclude linux platform in auto update task

* chore: disable auto updater

* fix: integration tests

* fix: integration tests
  • Loading branch information
LucasXu0 authored Feb 10, 2025
1 parent fc9c152 commit f53e9d6
Show file tree
Hide file tree
Showing 30 changed files with 949 additions and 206 deletions.
36 changes: 36 additions & 0 deletions frontend/appflowy_flutter/dsa_pub.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-----BEGIN PUBLIC KEY-----
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
p6Dfx6Mqf0+pSpyZcf+qW1vlk3q92g==
-----END PUBLIC KEY-----
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ void main() {
await tester.enterUserName('local_user');

// Scroll to sign-in
await tester.scrollUntilVisible(
find.byType(AccountSignInOutButton),
100,
scrollable: find.findSettingsScrollable(),
);

await tester.tapButton(find.byType(AccountSignInOutButton));

// sign up with Google
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,6 @@ void main() {
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);

// Scroll to sign-in
await tester.scrollUntilVisible(
find.byType(AccountSignInOutButton),
100,
scrollable: find.findSettingsScrollable(),
);
await tester.tapButton(find.byType(AccountSignInOutButton));

tester.expectToSeeGoogleLoginButton();
Expand Down
44 changes: 22 additions & 22 deletions frontend/appflowy_flutter/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -175,36 +175,36 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"

SPEC CHECKSUMS:
app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795
appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
app_links: c5161ac5ab5383ad046884568b4b91cb52df5d91
appflowy_backend: 78f6a053f756e6bc29bcc5a2106cbe77b756e97a
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
flowy_infra_ui: 931b73a18b54a392ab6152eebe29a63a30751f53
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
integration_test: d5929033778cc4991a187e4e1a85396fa4f59b3a
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
keyboard_height_plugin: ef70a8181b29f27670e9e2450814ca6b6dc05b05
open_filex: 432f3cd11432da3e39f47fcc0df2b1603854eff1
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1
sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
webview_flutter_wkwebview: 45a041c7831641076618876de3ba75c712860c6b

PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca

Expand Down
4 changes: 2 additions & 2 deletions frontend/appflowy_flutter/lib/startup/deps_resolver.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import 'package:appflowy/ai/service/ai_client.dart';
import 'package:appflowy/ai/service/appflowy_ai_service.dart';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/network_monitor.dart';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/plugins/document/application/prelude.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy/ai/service/ai_client.dart';
import 'package:appflowy/plugins/trash/application/prelude.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/shared/custom_image_cache_manager.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/startup/tasks/appflowy_cloud_task.dart';
import 'package:appflowy/ai/service/appflowy_ai_service.dart';
import 'package:appflowy/user/application/auth/af_cloud_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/prelude.dart';
Expand Down
3 changes: 2 additions & 1 deletion frontend/appflowy_flutter/lib/startup/startup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:io';

import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/startup/tasks/feature_flag_task.dart';
import 'package:appflowy/util/expand_views.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy_backend/appflowy_backend.dart';
Expand Down Expand Up @@ -139,6 +138,8 @@ class FlowyRunner {
// The DeviceOrApplicationInfoTask should be placed before the AppWidgetTask to fetch the app information.
// It is unable to get the device information from the test environment.
const ApplicationInfoTask(),
// The auto update task should be placed after the ApplicationInfoTask to fetch the latest version.
if (!mode.isIntegrationTest) AutoUpdateTask(),
const HotKeyTask(),
if (isAppFlowyCloudEnabled) InitAppFlowyCloudTask(),
const InitAppWidgetTask(),
Expand Down
188 changes: 188 additions & 0 deletions frontend/appflowy_flutter/lib/startup/tasks/auto_update_task.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/tasks/app_widget.dart';
import 'package:appflowy/startup/tasks/device_info_task.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/log.dart';
import 'package:auto_updater/auto_updater.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:universal_platform/universal_platform.dart';

import '../startup.dart';

class AutoUpdateTask extends LaunchTask {
AutoUpdateTask();

// static const _feedUrl =
// 'https://github.com/LucasXu0/AppFlowy/releases/latest/download/appcast-{os}-{arch}.xml';
final _listener = _AppFlowyAutoUpdaterListener();

@override
Future<void> initialize(LaunchContext context) async {
// Enable auto update when the integration of Windows and Linux is completed.
return;
// // the auto updater is not supported on mobile and linux
// if (UniversalPlatform.isMobile || UniversalPlatform.isLinux) {
// return;
// }

// Log.info(
// '[AutoUpdate] current version: ${ApplicationInfo.applicationVersion}, current cpu architecture: ${ApplicationInfo.architecture}',
// );

// autoUpdater.addListener(_listener);

// // Since the appcast.xml is not supported the arch, we separate the feed url by os and arch.
// final feedUrl = _feedUrl
// .replaceAll('{os}', ApplicationInfo.os)
// .replaceAll('{arch}', ApplicationInfo.architecture);
// Log.info('[AutoUpdate] feed url: $feedUrl');

// await autoUpdater.setFeedURL(feedUrl);
// await autoUpdater.checkForUpdateInformation();

// ApplicationInfo.isCriticalUpdateNotifier.addListener(
// _showCriticalUpdateDialog,
// );
}

@override
Future<void> dispose() async {
autoUpdater.removeListener(_listener);

ApplicationInfo.isCriticalUpdateNotifier.removeListener(
_showCriticalUpdateDialog,
);
}

void _showCriticalUpdateDialog() {
showCustomConfirmDialog(
context: AppGlobals.rootNavKey.currentContext!,
title: LocaleKeys.autoUpdate_criticalUpdateTitle.tr(),
description: LocaleKeys.autoUpdate_criticalUpdateDescription.tr(
namedArgs: {
'currentVersion': ApplicationInfo.applicationVersion,
'newVersion': ApplicationInfo.latestVersion,
},
),
builder: (context) => const SizedBox.shrink(),
// if the update is critical, dont allow the user to dismiss the dialog
barrierDismissible: false,
showCloseButton: false,
enableKeyboardListener: false,
closeOnConfirm: false,
confirmLabel: LocaleKeys.autoUpdate_criticalUpdateButton.tr(),
onConfirm: () async {
await autoUpdater.checkForUpdates();
},
);
}
}

class _AppFlowyAutoUpdaterListener extends UpdaterListener {
@override
void onUpdaterBeforeQuitForUpdate(AppcastItem? item) {}

@override
void onUpdaterCheckingForUpdate(Appcast? appcast) {
// Due to the reason documented in the following link, the update will not be found if the user has skipped the update.
// We have to check the skipped version manually.
// https://sparkle-project.org/documentation/api-reference/Classes/SPUUpdater.html#/c:objc(cs)SPUUpdater(im)checkForUpdateInformation
final items = appcast?.items;
if (items != null) {
final String? currentPlatform;
if (UniversalPlatform.isMacOS) {
currentPlatform = 'macos';
} else if (UniversalPlatform.isWindows) {
currentPlatform = 'windows';
} else {
currentPlatform = null;
}

final matchingItem = items.firstWhereOrNull(
(item) => item.os == currentPlatform,
);

if (matchingItem != null) {
_updateVersionNotifier(matchingItem);

Log.info(
'[AutoUpdate] latest version: ${matchingItem.displayVersionString}',
);
}
}
}

@override
void onUpdaterError(UpdaterError? error) {
Log.error('[AutoUpdate] On update error: $error');
}

@override
void onUpdaterUpdateNotAvailable(UpdaterError? error) {
Log.info('[AutoUpdate] Update not available $error');
}

@override
void onUpdaterUpdateAvailable(AppcastItem? item) {
_updateVersionNotifier(item);

Log.info('[AutoUpdate] Update available: ${item?.displayVersionString}');
}

@override
void onUpdaterUpdateDownloaded(AppcastItem? item) {
Log.info('[AutoUpdate] Update downloaded: ${item?.displayVersionString}');
}

@override
void onUpdaterUpdateCancelled(AppcastItem? item) {
_updateVersionNotifier(item);

Log.info('[AutoUpdate] Update cancelled: ${item?.displayVersionString}');
}

@override
void onUpdaterUpdateInstalled(AppcastItem? item) {
_updateVersionNotifier(item);

Log.info('[AutoUpdate] Update installed: ${item?.displayVersionString}');
}

@override
void onUpdaterUpdateSkipped(AppcastItem? item) {
_updateVersionNotifier(item);

Log.info('[AutoUpdate] Update skipped: ${item?.displayVersionString}');
}

void _updateVersionNotifier(AppcastItem? item) {
if (item != null) {
ApplicationInfo.latestAppcastItem = item;
ApplicationInfo.latestVersionNotifier.value =
item.displayVersionString ?? '';
}
}
}

class AppFlowyAutoUpdateVersion {
AppFlowyAutoUpdateVersion({
required this.latestVersion,
required this.currentVersion,
required this.isForceUpdate,
});

factory AppFlowyAutoUpdateVersion.initial() => AppFlowyAutoUpdateVersion(
latestVersion: '0.0.0',
currentVersion: '0.0.0',
isForceUpdate: false,
);

final String latestVersion;
final String currentVersion;

final bool isForceUpdate;

bool get isUpdateAvailable => latestVersion != currentVersion;
}
Loading

0 comments on commit f53e9d6

Please sign in to comment.