feat: integrate auto_updater for macOS (#7328)

* 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
This commit is contained in:
Lucas 2025-02-10 09:20:24 +08:00 committed by GitHub
parent fc9c152553
commit f53e9d6549
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 950 additions and 207 deletions

View File

@ -0,0 +1,36 @@
-----BEGIN PUBLIC KEY-----
MIIGQjCCBDUGByqGSM44BAEwggQoAoICAQCJsCIhS7IK+c4R3GVBuJBjI0/gW19n
flfKzC0kdt6HUtyirJ/v8SafqwRWSkbPVcrdVvHHBXOs2v7JogyZuy9v8SRvpW1s
trR6hFExtllYTo8uLi1YE5LGt4SwjduRYpwPvGvSxU5An8yESu/96JShQekHVPTj
ILfDNOOP2iRVIwRRVNZfFT/XOX5mN4RysUd+0KZ5IBR1LzRgw2O02sHheMkK/Uqr
19Of5eC1JTqgki9hHdjtjTsohJCrecFPDg4ej5Ac7HrbsGEklDDwtd7ftit2mnuz
ZNhG9qpfNztz9TQ6HCQCCrQCm1H7BkQDg5y7qFC9bARjMiQh0ygic97USeQLDu3l
EyxPAbIvo4m4IdHpaQSYRkse24y3b+k8BrQ9qNm4ElTIjEt4rs3Ic9aJ2j62qugq
7q+3EA0aDHbsB+TbjMm4sW3G9NVsr88nk1UUCayLDyCHwaXVB5KCfrVgM/5r/Avl
2ukkDdIB3A5gnO5+MY5fVS4WgxCPuAfJ6vp9/r2U1dGRDUAaISqg44uOwppXTnoZ
JQ3ZxZmyF6BVwvpjrZ7B8TfPco2JwBFI33W9byOWckUtc2cLAbuPutR1ymMp1F8J
B4G0VbpzT2liGaHqBV82J5+4ljAhcUnEzFOJ4ysDaJ0/n3NQuNGb2EX6SF9E5A2A
eaeEu5C0MWSFLQIdAJJP3sJsHh/raF/aqWjwKQQ2NWom4fKH1v7OSIcCggIAGn3W
SUWgyINiLoahtDZ2zkL7oMDQwZCC7lGUTyhi9gDnllNtJkpBpyEvgU479MnpGpgp
IlheOHYrEtL3EM31KwKvRism4TMkNd5+ZFw6//AcuoRd0YcGisk/UdE9PlhoKvLu
pzlQLf9iCeFtevW0TgHp/su0ZP6tSb1p/97vJOO5qY8gE4bCUNknlovFuhCwI9Al
eaQjhoDDDhimnkC8ZMsybvri9ZtNe24erZDaD1eQ85U0zR8CR73wFo1osDBjGgeK
cngcnQXoPfpj1tgmd1lEAcO8qD6BzZkEVyN+fgeqj22nfwqDDe9sXIEvf1KkhKEy
zWsdoZ6LCgWfI6Iqs4mqYlJPRhGFZ6hq0tncBzoxZcTeX1EYRhgYUeBe91CRlHMs
SIfsW5fpjWZ2QeDAsr9qmJ+RK06bN6U65o2nSOh0jaJpjT0VF6VdLLDF/bGrIjey
oEHg7b/SO8z7zO5H6CnHF4m4TG/EfkD5CSlT2SGer7yEF5KYeG5QX0W15ELTghak
IRsvBT6D8MWKjnfuxJjKLQoXPiw9Ua+nhG4YyQpDey1e7Y2oUFONdFRR38ocZuqQ
7YyxUzsQPLs1oEqfLm09gaF9gV/yPY09ocV2t+dO6PRq6BHWkdCdipUsFAwcPxmV
r4c8HCXKvlpNHDDu7Lw2CkfQdCbqpuT7gqsz+WgDggIFAAKCAgBOv5yBTT5SuSax
SmWwsoorMV+CxLRj0SM2T2FW9YK354BpDm95rpQZ4Za9m2wj59i73j/HboREsKgd
FzajWEd2S9VSHJ4dYx4m3wkQ6BFN/ryVocmG2PzAistXAOBB32dnQkK5cwDxQn2R
+rKHSSWtuJGv2YrwbxdpZM0zvOHvjYEXndV2XdH/DW4j9DivuCNoJlzaOvyXQsuE
jpcB0tg0QfZ564/6MIbzUivo7RKiLnmFiIksKymDYwp49382EWoSvF+rTjpqs1zH
+7jyva1m14jTgdSDjdoZakpkNpJpyN3Z6whQczBL0g3T3t3VEN6F7NGA0aZVrSkO
rPYXvZdEtsH4qGpb9SQx2fbE3RBD2LcxWxFBk0SlzF7RiCzolre6v/kqfJA5oejA
qTyTJpadheUn/+yYltnzCinILrDYwHzPAZWUXc1guRrdRRcv+d1gW3VG5n37NdYh
lSgowVOUfTcZv6nCr+Ohbirmogc/crcBVUhSxLufajKKMtzJBwbZ8yvUtgmCXCZ7
6kVYIoRuQCkzJvN50dM9Jd20+e2b0MXdj8fEMF9UnBIqAklStMpkaK5dFXLUDHho
uG9cYnd4HIeLHZvfLgQ+DrBBdvLKPBez3vkIofocQDP4KIS4Dra4lLtDA07UmSwY
p6Dfx6Mqf0+pSpyZcf+qW1vlk3q92g==
-----END PUBLIC KEY-----

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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';

View File

@ -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';
@ -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(),

View File

@ -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;
}

View File

@ -1,8 +1,11 @@
import 'dart:io';
import 'package:appflowy_backend/log.dart';
import 'package:auto_updater/auto_updater.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:version/version.dart';
import '../startup.dart';
@ -11,10 +14,39 @@ class ApplicationInfo {
static String applicationVersion = '';
static String buildNumber = '';
static String deviceId = '';
static String architecture = '';
static String os = '';
// macOS major version
static int? macOSMajorVersion;
static int? macOSMinorVersion;
// latest version
static ValueNotifier<String> latestVersionNotifier = ValueNotifier('');
// the version number is like 0.9.0
static String get latestVersion => latestVersionNotifier.value;
// If the latest version is greater than the current version, it means there is an update available
static bool get isUpdateAvailable {
try {
return Version.parse(latestVersion) > Version.parse(applicationVersion);
} catch (e) {
return false;
}
}
// the latest appcast item
static AppcastItem? _latestAppcastItem;
static AppcastItem? get latestAppcastItem => _latestAppcastItem;
static set latestAppcastItem(AppcastItem? value) {
_latestAppcastItem = value;
isCriticalUpdateNotifier.value = value?.criticalUpdate == true;
}
// is critical update
static ValueNotifier<bool> isCriticalUpdateNotifier = ValueNotifier(false);
static bool get isCriticalUpdate => isCriticalUpdateNotifier.value;
}
class ApplicationInfoTask extends LaunchTask {
@ -36,38 +68,54 @@ class ApplicationInfoTask extends LaunchTask {
ApplicationInfo.androidSDKVersion = androidInfo.version.sdkInt;
}
if (Platform.isAndroid || Platform.isIOS) {
ApplicationInfo.applicationVersion = packageInfo.version;
ApplicationInfo.buildNumber = packageInfo.buildNumber;
}
ApplicationInfo.applicationVersion = packageInfo.version;
ApplicationInfo.buildNumber = packageInfo.buildNumber;
String? deviceId;
String? architecture;
String? os;
try {
if (Platform.isAndroid) {
final AndroidDeviceInfo androidInfo =
await deviceInfoPlugin.androidInfo;
deviceId = androidInfo.device;
architecture = androidInfo.supportedAbis.firstOrNull;
os = 'android';
} else if (Platform.isIOS) {
final IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
deviceId = iosInfo.identifierForVendor;
architecture = iosInfo.utsname.machine;
os = 'ios';
} else if (Platform.isMacOS) {
final MacOsDeviceInfo macInfo = await deviceInfoPlugin.macOsInfo;
deviceId = macInfo.systemGUID;
architecture = macInfo.arch;
os = 'macos';
} else if (Platform.isWindows) {
final WindowsDeviceInfo windowsInfo =
await deviceInfoPlugin.windowsInfo;
deviceId = windowsInfo.deviceId;
// we only support x86_64 on Windows
architecture = 'x86_64';
os = 'windows';
} else if (Platform.isLinux) {
final LinuxDeviceInfo linuxInfo = await deviceInfoPlugin.linuxInfo;
deviceId = linuxInfo.machineId;
// we only support x86_64 on Linux
architecture = 'x86_64';
os = 'linux';
} else {
deviceId = null;
architecture = null;
os = null;
}
} catch (e) {
Log.error('Failed to get platform version, $e');
}
ApplicationInfo.deviceId = deviceId ?? '';
ApplicationInfo.architecture = architecture ?? '';
ApplicationInfo.os = os ?? '';
}
@override

View File

@ -1,7 +1,9 @@
export 'app_widget.dart';
export 'appflowy_cloud_task.dart';
export 'auto_update_task.dart';
export 'debug_task.dart';
export 'device_info_task.dart';
export 'feature_flag_task.dart';
export 'generate_router.dart';
export 'hot_key.dart';
export 'load_plugin.dart';

View File

@ -0,0 +1,110 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/util/theme_extension.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class SidebarUpgradeApplicationButton extends StatelessWidget {
const SidebarUpgradeApplicationButton({
super.key,
required this.onUpdateButtonTap,
required this.onCloseButtonTap,
});
final VoidCallback onUpdateButtonTap;
final VoidCallback onCloseButtonTap;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: context.sidebarUpgradeButtonBackground,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// title
_buildTitle(),
const VSpace(2),
// description
_buildDescription(),
const VSpace(10),
// update button
_buildUpdateButton(),
],
),
);
}
Widget _buildTitle() {
return Row(
children: [
const FlowySvg(
FlowySvgs.sidebar_upgrade_version_s,
blendMode: null,
),
const HSpace(6),
FlowyText.medium(
LocaleKeys.autoUpdate_bannerUpdateTitle.tr(),
fontSize: 14,
figmaLineHeight: 18,
),
const Spacer(),
FlowyButton(
useIntrinsicWidth: true,
text: const FlowySvg(FlowySvgs.upgrade_close_s),
onTap: onCloseButtonTap,
),
],
);
}
Widget _buildDescription() {
return Opacity(
opacity: 0.7,
child: FlowyText(
LocaleKeys.autoUpdate_bannerUpdateDescription.tr(),
fontSize: 13,
figmaLineHeight: 16,
maxLines: null,
),
);
}
Widget _buildUpdateButton() {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: onUpdateButtonTap,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
decoration: ShapeDecoration(
color: const Color(0xFFA44AFD),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(9),
),
),
child: FlowyText.medium(
LocaleKeys.autoUpdate_settingsUpdateButton.tr(),
color: Colors.white,
fontSize: 12.0,
figmaLineHeight: 15.0,
),
),
),
);
}
}
extension on BuildContext {
Color get sidebarUpgradeButtonBackground => Theme.of(this).isLightMode
? const Color(0xB2EBE4FF)
: const Color(0xB239275B);
}

View File

@ -8,6 +8,7 @@ import 'package:appflowy/plugins/document/presentation/editor_notification.dart'
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/shared/loading.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/startup/tasks/device_info_task.dart';
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
import 'package:appflowy/workspace/application/action_navigation/navigation_action.dart';
import 'package:appflowy/workspace/application/command_palette/command_palette_bloc.dart';
@ -23,6 +24,7 @@ import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/presentation/command_palette/command_palette.dart';
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/footer/sidebar_footer.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/footer/sidebar_upgarde_application_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/header/sidebar_top_menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/header/sidebar_user.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_folder.dart';
@ -34,6 +36,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:auto_updater/auto_updater.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
@ -261,6 +264,9 @@ class _SidebarState extends State<_Sidebar> {
final _isHovered = ValueNotifier(false);
final _scrollOffset = ValueNotifier<double>(0);
// mute the update button during the current application lifecycle.
final _muteUpdateButton = ValueNotifier(false);
@override
void initState() {
super.initState();
@ -347,6 +353,7 @@ class _SidebarState extends State<_Sidebar> {
const VSpace(8),
_renderUpgradeSpaceButton(menuHorizontalInset),
_buildUpgradeApplicationButton(menuHorizontalInset),
const VSpace(8),
Padding(
@ -432,6 +439,42 @@ class _SidebarState extends State<_Sidebar> {
);
}
Widget _buildUpgradeApplicationButton(EdgeInsets menuHorizontalInset) {
return ValueListenableBuilder(
valueListenable: _muteUpdateButton,
builder: (_, mute, child) {
if (mute) {
return const SizedBox.shrink();
}
return ValueListenableBuilder(
valueListenable: ApplicationInfo.latestVersionNotifier,
builder: (_, latestVersion, child) {
if (!ApplicationInfo.isUpdateAvailable) {
return const SizedBox.shrink();
}
return Padding(
padding: menuHorizontalInset +
const EdgeInsets.only(
left: 4.0,
right: 4.0,
),
child: SidebarUpgradeApplicationButton(
onUpdateButtonTap: () {
autoUpdater.checkForUpdates();
},
onCloseButtonTap: () {
_muteUpdateButton.value = true;
},
),
);
},
);
},
);
}
void _onScrollChanged() {
setState(() => _isScrolling = true);

View File

@ -275,6 +275,8 @@ class ConfirmPopup extends StatefulWidget {
this.confirmButtonColor,
this.child,
this.closeOnAction = true,
this.showCloseButton = true,
this.enableKeyboardListener = true,
});
final String title;
@ -303,6 +305,16 @@ class ConfirmPopup extends StatefulWidget {
///
final bool closeOnAction;
/// Show close button.
/// Defaults to true.
///
final bool showCloseButton;
/// Enable keyboard listener.
/// Defaults to true.
///
final bool enableKeyboardListener;
@override
State<ConfirmPopup> createState() => _ConfirmPopupState();
}
@ -316,14 +328,16 @@ class _ConfirmPopupState extends State<ConfirmPopup> {
focusNode: focusNode,
autofocus: true,
onKeyEvent: (event) {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.escape) {
Navigator.of(context).pop();
} else if (event is KeyUpEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
widget.onConfirm();
if (widget.closeOnAction) {
if (widget.enableKeyboardListener) {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.escape) {
Navigator.of(context).pop();
} else if (event is KeyUpEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
widget.onConfirm();
if (widget.closeOnAction) {
Navigator.of(context).pop();
}
}
}
},
@ -367,15 +381,17 @@ class _ConfirmPopupState extends State<ConfirmPopup> {
),
),
const HSpace(6.0),
FlowyButton(
margin: const EdgeInsets.all(3),
useIntrinsicWidth: true,
text: const FlowySvg(
FlowySvgs.upgrade_close_s,
size: Size.square(18.0),
if (widget.showCloseButton) ...[
FlowyButton(
margin: const EdgeInsets.all(3),
useIntrinsicWidth: true,
text: const FlowySvg(
FlowySvgs.upgrade_close_s,
size: Size.square(18.0),
),
onTap: () => Navigator.of(context).pop(),
),
onTap: () => Navigator.of(context).pop(),
),
],
],
);
}

View File

@ -0,0 +1,147 @@
import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/tasks/device_info_task.dart';
import 'package:appflowy_backend/log.dart';
import 'package:auto_updater/auto_updater.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class SettingsAppVersion extends StatelessWidget {
const SettingsAppVersion({
super.key,
});
@override
Widget build(BuildContext context) {
return ApplicationInfo.isUpdateAvailable
? const _UpdateAppSection()
: _buildIsUpToDate();
}
Widget _buildIsUpToDate() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const FlowyText.regular(
'AppFlowy is up to date!',
figmaLineHeight: 17,
),
const VSpace(4),
Opacity(
opacity: 0.7,
child: FlowyText.regular(
'Version ${ApplicationInfo.applicationVersion} (Official build)',
fontSize: 12,
figmaLineHeight: 13,
),
),
],
);
}
}
class _UpdateAppSection extends StatelessWidget {
const _UpdateAppSection();
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(child: _buildDescription(context)),
_buildUpdateButton(),
],
);
}
Widget _buildUpdateButton() {
return PrimaryRoundedButton(
text: LocaleKeys.autoUpdate_settingsUpdateButton.tr(),
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
fontWeight: FontWeight.w500,
radius: 8.0,
onTap: () {
Log.info('[AutoUpdater] Checking for updates');
autoUpdater.checkForUpdates();
},
);
}
Widget _buildDescription(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
_buildRedDot(),
const HSpace(6),
Flexible(
child: FlowyText.medium(
LocaleKeys.autoUpdate_settingsUpdateTitle.tr(
namedArgs: {
'newVersion': ApplicationInfo.latestVersion,
},
),
figmaLineHeight: 17,
overflow: TextOverflow.ellipsis,
),
),
],
),
const VSpace(4),
_buildCurrentVersionAndLatestVersion(context),
],
);
}
Widget _buildCurrentVersionAndLatestVersion(BuildContext context) {
return Row(
children: [
Flexible(
child: Opacity(
opacity: 0.7,
child: FlowyText.regular(
LocaleKeys.autoUpdate_settingsUpdateDescription.tr(
namedArgs: {
'currentVersion': ApplicationInfo.applicationVersion,
'newVersion': ApplicationInfo.latestVersion,
},
),
fontSize: 12,
figmaLineHeight: 13,
overflow: TextOverflow.ellipsis,
),
),
),
const HSpace(6),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
afLaunchUrlString('https://www.appflowy.io/what-is-new');
},
child: FlowyText.regular(
LocaleKeys.autoUpdate_settingsUpdateWhatsNew.tr(),
decoration: TextDecoration.underline,
color: Theme.of(context).colorScheme.primary,
fontSize: 12,
figmaLineHeight: 13,
overflow: TextOverflow.ellipsis,
),
),
),
],
);
}
Widget _buildRedDot() {
return Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: Color(0xFFFB006D),
shape: BoxShape.circle,
),
);
}
}

View File

@ -14,6 +14,34 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AccountSignInOutSection extends StatelessWidget {
const AccountSignInOutSection({
super.key,
required this.userProfile,
required this.onAction,
this.signIn = true,
});
final UserProfilePB userProfile;
final VoidCallback onAction;
final bool signIn;
@override
Widget build(BuildContext context) {
return Row(
children: [
FlowyText.regular(LocaleKeys.settings_accountPage_login_title.tr()),
const Spacer(),
AccountSignInOutButton(
userProfile: userProfile,
onAction: onAction,
signIn: signIn,
),
],
);
}
}
class AccountSignInOutButton extends StatelessWidget {
const AccountSignInOutButton({
super.key,
@ -32,9 +60,9 @@ class AccountSignInOutButton extends StatelessWidget {
text: signIn
? LocaleKeys.settings_accountPage_login_loginLabel.tr()
: LocaleKeys.settings_accountPage_login_logoutLabel.tr(),
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
fontWeight: FontWeight.w600,
radius: 12.0,
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
fontWeight: FontWeight.w500,
radius: 8.0,
onTap: () =>
signIn ? _showSignInDialog(context) : _showLogoutDialog(context),
);

View File

@ -2,6 +2,7 @@ import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/pages/about/app_version.dart';
import 'package:appflowy/workspace/presentation/settings/pages/account/account.dart';
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
@ -74,23 +75,42 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
title: LocaleKeys.settings_accountPage_email_title.tr(),
children: [
FlowyText.regular(state.userProfile.email),
AccountSignInOutSection(
userProfile: state.userProfile,
onAction: state.userProfile.authenticator ==
AuthenticatorPB.Local
? widget.didLogin
: widget.didLogout,
signIn: state.userProfile.authenticator ==
AuthenticatorPB.Local,
),
],
),
],
// user sign in/out
if (isAuthEnabled &&
state.userProfile.authenticator == AuthenticatorPB.Local) ...[
SettingsCategory(
title: LocaleKeys.settings_accountPage_login_title.tr(),
children: [
AccountSignInOutSection(
userProfile: state.userProfile,
onAction: state.userProfile.authenticator ==
AuthenticatorPB.Local
? widget.didLogin
: widget.didLogout,
signIn: state.userProfile.authenticator ==
AuthenticatorPB.Local,
),
],
),
],
// App version
SettingsCategory(
title: LocaleKeys.settings_accountPage_login_title.tr(),
children: [
AccountSignInOutButton(
userProfile: state.userProfile,
onAction:
state.userProfile.authenticator == AuthenticatorPB.Local
? widget.didLogin
: widget.didLogout,
signIn: state.userProfile.authenticator ==
AuthenticatorPB.Local,
),
title: LocaleKeys.newSettings_myAccount_aboutAppFlowy.tr(),
children: const [
SettingsAppVersion(),
],
),

View File

@ -673,9 +673,13 @@ Future<void> showCustomConfirmDialog({
String? confirmLabel,
ConfirmPopupStyle style = ConfirmPopupStyle.onlyOk,
bool closeOnConfirm = true,
bool showCloseButton = true,
bool enableKeyboardListener = true,
bool barrierDismissible = true,
}) {
return showDialog(
context: context,
barrierDismissible: barrierDismissible,
builder: (context) {
return Dialog(
shape: RoundedRectangleBorder(
@ -692,6 +696,8 @@ Future<void> showCustomConfirmDialog({
confirmButtonColor: Theme.of(context).colorScheme.primary,
style: style,
closeOnAction: closeOnConfirm,
showCloseButton: showCloseButton,
enableKeyboardListener: enableKeyboardListener,
child: builder(context),
),
),

View File

@ -3,6 +3,9 @@ PODS:
- FlutterMacOS
- appflowy_backend (0.0.1):
- FlutterMacOS
- auto_updater_macos (0.0.1):
- FlutterMacOS
- Sparkle
- bitsdojo_window_macos (0.0.1):
- FlutterMacOS
- connectivity_plus (0.0.1):
@ -17,7 +20,7 @@ PODS:
- flowy_infra_ui (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- HotKey (0.2.0)
- HotKey (0.2.1)
- hotkey_manager (0.0.1):
- FlutterMacOS
- HotKey
@ -30,7 +33,7 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- ReachabilitySwift (5.2.3)
- ReachabilitySwift (5.2.4)
- screen_retriever_macos (0.0.1):
- FlutterMacOS
- Sentry/HybridSDK (8.35.1)
@ -43,6 +46,7 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- Sparkle (2.6.4)
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
@ -59,6 +63,7 @@ PODS:
DEPENDENCIES:
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
- appflowy_backend (from `Flutter/ephemeral/.symlinks/plugins/appflowy_backend/macos`)
- auto_updater_macos (from `Flutter/ephemeral/.symlinks/plugins/auto_updater_macos/macos`)
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
@ -86,12 +91,15 @@ SPEC REPOS:
- HotKey
- ReachabilitySwift
- Sentry
- Sparkle
EXTERNAL SOURCES:
app_links:
:path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
appflowy_backend:
:path: Flutter/ephemeral/.symlinks/plugins/appflowy_backend/macos
auto_updater_macos:
:path: Flutter/ephemeral/.symlinks/plugins/auto_updater_macos/macos
bitsdojo_window_macos:
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
connectivity_plus:
@ -136,32 +144,34 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
SPEC CHECKSUMS:
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
appflowy_backend: 865496343de667fc8c600e04b9fd05234e130cf9
bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
flowy_infra_ui: 03301a39ad118771adbf051a664265c61c507f38
app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468
appflowy_backend: 464aeb3e5c6966a41641a2111e5ead72ce2695f7
auto_updater_macos: 3a42f1a06be6981f1a18be37e6e7bf86aa732118
bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9
connectivity_plus: e74b9f74717d2d99d45751750e266e55912baeb5
desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43
device_info_plus: a56e6e74dbbd2bb92f2da12c64ddd4f67a749041
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
flowy_infra_ui: 8760ff42a789de40bf5007a5f176b454722a341e
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6
hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c
irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161
HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277
hotkey_manager: b443f35f4d772162937aa73fd8995e579f8ac4e2
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1
sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737
share_plus: 1fa619de8392a4398bfaf176d441853922614e89
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3
url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
Sparkle: 5f8960a7a119aa7d45dacc0d5837017170bc5675
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c
window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c
PODFILE CHECKSUM: 0532f3f001ca3110b8be345d6491fff690e95823

View File

@ -16,4 +16,8 @@ class AppDelegate: FlutterAppDelegate {
return true
}
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<array>
<string>/</string>
</array>
</dict>
</plist>
<dict>
<key>com.apple.security.app-sandbox</key>
<false />
<key>com.apple.security.files.downloads.read-write</key>
<true />
<key>com.apple.security.files.user-selected.read-write</key>
<true />
<key>com.apple.security.network.client</key>
<true />
<key>com.apple.security.network.server</key>
<true />
<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<array>
<string>/</string>
</array>
</dict>
</plist>

View File

@ -1,57 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>fr</string>
<string>it</string>
<string>zh</string>
</array>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>appflowy-flutter</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>fr</string>
<string>it</string>
<string>zh</string>
</array>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>appflowy-flutter</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true />
</dict>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>SUPublicEDKey</key>
<string>Bs++IOmOwYmNTjMMC2jMqLNldP+mndDp/LwujCg2/kw=</string>
<key>SUAllowsAutomaticUpdates</key>
<false />
</dict>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
</plist>

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<array>
<string>/</string>
</array>
</dict>
</plist>
<dict>
<key>com.apple.security.app-sandbox</key>
<false />
<key>com.apple.security.files.downloads.read-write</key>
<true />
<key>com.apple.security.files.user-selected.read-write</key>
<true />
<key>com.apple.security.network.client</key>
<true />
<key>com.apple.security.network.server</key>
<true />
<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<array>
<string>/</string>
</array>
</dict>
</plist>

View File

@ -1,4 +1,4 @@
platform :osx, '10.13'
platform :osx, '10.14'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
@ -26,11 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; };
33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B7C2E82907836001B5A6F548 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49D7864808B727FDFB82A4C2 /* Pods_Runner.framework */; };
D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; };
D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -50,8 +46,6 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */,
33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */,
);
name = "Bundle Framework";
runOnlyForDeploymentPostprocessing = 0;
@ -71,7 +65,6 @@
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; };
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
@ -80,7 +73,6 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
A82DF8E6F43DF0AD4D0653DC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -88,8 +80,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D73912F022F37F9E000D13A0 /* App.framework in Frameworks */,
33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */,
B7C2E82907836001B5A6F548 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -145,8 +135,6 @@
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
D73912EF22F37F9E000D13A0 /* App.framework */,
33D1A10322148B71006C7A3E /* FlutterMacOS.framework */,
);
path = Flutter;
sourceTree = "<group>";
@ -215,7 +203,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0930;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
@ -268,6 +256,7 @@
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -281,7 +270,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n";
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
};
33CC111E2044C6BF0003C045 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
@ -414,7 +403,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@ -497,7 +486,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@ -544,7 +533,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -49,6 +49,7 @@ class PrimaryRoundedButton extends StatelessWidget {
figmaLineHeight: figmaLineHeight,
color: textColor ?? Theme.of(context).colorScheme.onPrimary,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
margin: margin ?? const EdgeInsets.symmetric(horizontal: 14.0),
backgroundColor: backgroundColor ?? Theme.of(context).colorScheme.primary,

View File

@ -150,6 +150,41 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.4"
auto_updater:
dependency: "direct main"
description:
path: "packages/auto_updater"
ref: "1d81a824f3633f1d0200ba51b78fe0f9ce429458"
resolved-ref: "1d81a824f3633f1d0200ba51b78fe0f9ce429458"
url: "https://github.com/LucasXu0/auto_updater.git"
source: git
version: "1.0.0"
auto_updater_macos:
dependency: "direct overridden"
description:
path: "packages/auto_updater_macos"
ref: "1d81a824f3633f1d0200ba51b78fe0f9ce429458"
resolved-ref: "1d81a824f3633f1d0200ba51b78fe0f9ce429458"
url: "https://github.com/LucasXu0/auto_updater.git"
source: git
version: "1.0.0"
auto_updater_platform_interface:
dependency: "direct overridden"
description:
path: "packages/auto_updater_platform_interface"
ref: "1d81a824f3633f1d0200ba51b78fe0f9ce429458"
resolved-ref: "1d81a824f3633f1d0200ba51b78fe0f9ce429458"
url: "https://github.com/LucasXu0/auto_updater.git"
source: git
version: "1.0.0"
auto_updater_windows:
dependency: transitive
description:
name: auto_updater_windows
sha256: "2bba20a71eee072f49b7267fedd5c4f1406c4b1b1e5b83932c634dbab75b80c9"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
avatar_stack:
dependency: "direct main"
description:
@ -162,10 +197,10 @@ packages:
dependency: transitive
description:
name: barcode
sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003
sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4"
url: "https://pub.dev"
source: hosted
version: "2.2.8"
version: "2.2.9"
bidi:
dependency: transitive
description:
@ -1540,10 +1575,10 @@ packages:
dependency: transitive
description:
name: pdf
sha256: "05df53f8791587402493ac97b9869d3824eccbc77d97855f4545cf72df3cae07"
sha256: adbdec5bc84d20e6c8d67f9c64270aa64d1e9e1ed529f0fef7e7bc7e9400f894
url: "https://pub.dev"
source: hosted
version: "3.11.1"
version: "3.11.2"
percent_indicator:
dependency: "direct main"
description:
@ -2360,6 +2395,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version:
dependency: "direct main"
description:
name: version
sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
visibility_detector:
dependency: transitive
description:

View File

@ -28,6 +28,7 @@ dependencies:
archive: ^3.4.10
auto_size_text_field: ^2.2.3
auto_updater: ^1.0.0
avatar_stack: ^3.0.0
# BitsDojo Window for Windows
@ -142,6 +143,7 @@ dependencies:
url_protocol:
# Window Manager for MacOS and Linux
version: ^3.0.2
window_manager: ^0.4.3
dev_dependencies:
@ -207,6 +209,33 @@ dependency_overrides:
# https://github.com/miguelpruivo/flutter_file_picker/issues/1652
file_picker: 8.1.4
auto_updater:
git:
url: https://github.com/LucasXu0/auto_updater.git
path: packages/auto_updater
ref: 1d81a824f3633f1d0200ba51b78fe0f9ce429458
auto_updater_macos:
git:
url: https://github.com/LucasXu0/auto_updater.git
path: packages/auto_updater_macos
ref: 1d81a824f3633f1d0200ba51b78fe0f9ce429458
auto_updater_platform_interface:
git:
url: https://github.com/LucasXu0/auto_updater.git
path: packages/auto_updater_platform_interface
ref: 1d81a824f3633f1d0200ba51b78fe0f9ce429458
# auto_updater:
# path: /Users/lucas.xu/Desktop/auto_updater/packages/auto_updater
# auto_updater_macos:
# path: /Users/lucas.xu/Desktop/auto_updater/packages/auto_updater_macos
# auto_updater_platform_interface:
# path: /Users/lucas.xu/Desktop/auto_updater/packages/auto_updater_platform_interface
flutter:
generate: true
uses-material-design: true

View File

@ -119,3 +119,11 @@ END
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// WinSparkle
//
// And verify signature using DSA public key:
DSAPub DSAPEM "../../dsa_pub.pem"

View File

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5066 13.892L10.6734 13.6784C11.6487 12.4302 11.9926 11.2916 11.7791 9.94711C14.7314 6.68151 14.9121 3.29101 14.0748 1.20029L14.027 1.07792L13.8959 1.06053C11.6655 0.754485 8.40056 1.73528 5.96573 5.40397C4.62845 5.53682 3.60683 6.14599 2.65933 7.35874L2.49249 7.57229L5.32961 9.01353C5.058 9.6909 5.37947 10.6032 6.12887 11.1887C6.87748 11.7736 7.83985 11.8656 8.41785 11.4555L10.5066 13.892ZM4.1851 13.6929C4.1851 13.6929 5.38377 13.3362 5.78764 12.8193C6.09424 12.4269 6.09502 11.911 5.77351 11.6598C5.452 11.4086 4.95241 11.5348 4.64581 11.9272C4.28381 12.3906 4.1851 13.6929 4.1851 13.6929ZM11.1536 6.2089C11.9263 6.18191 12.5309 5.53362 12.5039 4.76089C12.4769 3.98816 11.8286 3.38362 11.0559 3.4106C10.2832 3.43759 9.67863 4.08588 9.70561 4.85861C9.7326 5.63134 10.3809 6.23588 11.1536 6.2089Z" fill="url(#paint0_linear_8313_6635)"/>
<defs>
<linearGradient id="paint0_linear_8313_6635" x1="4.20975" y1="13.6559" x2="14.9687" y2="11.254" gradientUnits="userSpaceOnUse">
<stop stop-color="#8032FF"/>
<stop offset="1" stop-color="#FB3DFF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -2511,6 +2511,7 @@
"accountLogin": "Account Login",
"updateNameError": "Failed to update name",
"updateIconError": "Failed to update icon",
"aboutAppFlowy": "About @:appName",
"deleteAccount": {
"title": "Delete Account",
"subtitle": "Permanently delete your account and all of your data.",
@ -3081,5 +3082,17 @@
},
"ai": {
"contentPolicyViolation": "Image generation failed due to sensitive content. Please rephrase your input and try again"
},
"autoUpdate": {
"criticalUpdateTitle": "Update required to continue",
"criticalUpdateDescription": "We've made improvements to enhance your experience! Please update from {currentVersion} to {newVersion} to keep using the app.",
"criticalUpdateButton": "Update now",
"bannerUpdateTitle": "New Version Available!",
"bannerUpdateDescription": "Get the latest features and bug fixes. Click \"Update\" to install now",
"bannerUpdateButton": "Update",
"settingsUpdateTitle": "New Version ({newVersion}) Available!",
"settingsUpdateDescription": "Current version: {currentVersion} (Official build) → {newVersion}",
"settingsUpdateButton": "Update now",
"settingsUpdateWhatsNew": "What's new"
}
}