Skip to content

Commit

Permalink
feat: support clearing caches and fix unable to load image (AppFlowy-…
Browse files Browse the repository at this point in the history
…IO#4809)

* feat: support clearing caches

* fix: try to clear the image cache when loading failed.

* feat: clear cache on mobile

* chore: add error log
  • Loading branch information
LucasXu0 authored Mar 4, 2024
1 parent 6b05be2 commit 8944edf
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import 'dart:io';

import 'package:flutter/material.dart';

import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/share_log_files.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';

import 'widgets/widgets.dart';
Expand Down Expand Up @@ -51,6 +53,31 @@ class SupportSettingGroup extends StatelessWidget {
);
},
),
MobileSettingItem(
name: LocaleKeys.settings_files_clearCache.tr(),
trailing: const Icon(
Icons.chevron_right,
),
onTap: () async {
await showFlowyMobileConfirmDialog(
context,
title: FlowyText(
LocaleKeys.settings_files_areYouSureToClearCache.tr(),
maxLines: 2,
),
content: FlowyText(
LocaleKeys.settings_files_clearCacheDesc.tr(),
fontSize: 12,
maxLines: 4,
),
actionButtonTitle: LocaleKeys.button_yes.tr(),
actionButtonColor: Theme.of(context).colorScheme.error,
onActionButtonPressed: () async {
await getIt<FlowyCacheManager>().clearAllCache();
},
);
},
),
],
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ class _ResizableImageState extends State<ResizableImage> {

imageWidth = widget.width;

if (widget.type == CustomImageType.internal) {
_userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
}
_userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
}

@override
Expand Down
61 changes: 61 additions & 0 deletions frontend/appflowy_flutter/lib/shared/appflowy_cache_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:appflowy_backend/log.dart';
import 'package:path_provider/path_provider.dart';

class FlowyCacheManager {
final _caches = <ICache>[];

// if you add a new cache, you should register it here.
void registerCache(ICache cache) {
_caches.add(cache);
}

void unregisterAllCache(ICache cache) {
_caches.clear();
}

Future<void> clearAllCache() async {
try {
for (final cache in _caches) {
await cache.clearAll();
}

Log.info('Cache cleared');
} catch (e) {
Log.error(e);
}
}

Future<int> getCacheSize() async {
try {
int tmpDirSize = 0;
for (final cache in _caches) {
tmpDirSize += await cache.cacheSize();
}
Log.info('Cache size: $tmpDirSize');
return tmpDirSize;
} catch (e) {
Log.error(e);
return 0;
}
}
}

abstract class ICache {
Future<int> cacheSize();
Future<void> clearAll();
}

class TemporaryDirectoryCache implements ICache {
@override
Future<int> cacheSize() async {
final tmpDir = await getTemporaryDirectory();
final tmpDirStat = await tmpDir.stat();
return tmpDirStat.size;
}

@override
Future<void> clearAll() async {
final tmpDir = await getTemporaryDirectory();
await tmpDir.delete(recursive: true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ class FlowyNetworkImage extends StatelessWidget {
assert(userProfilePB != null && userProfilePB!.token.isNotEmpty);
}

final manager = CustomImageCacheManager();

return CachedNetworkImage(
cacheManager: CustomImageCacheManager(),
cacheManager: manager,
httpHeaders: _header(),
imageUrl: url,
fit: fit,
Expand All @@ -50,6 +52,12 @@ class FlowyNetworkImage extends StatelessWidget {
errorWidget: (context, url, error) =>
errorWidgetBuilder?.call(context, url, error) ??
const SizedBox.shrink(),
errorListener: (value) {
// try to clear the image cache.
manager.removeFile(url);

Log.error(value.toString());
},
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';

class CustomImageCacheManager extends CacheManager with ImageCacheManager {
class CustomImageCacheManager extends CacheManager
with ImageCacheManager
implements ICache {
CustomImageCacheManager._() : super(Config(key));

factory CustomImageCacheManager() => _instance;

static final CustomImageCacheManager _instance = CustomImageCacheManager._();

static const key = 'appflowy_image_cache';

@override
Future<int> cacheSize() async {
// https://github.com/Baseflow/flutter_cache_manager/issues/239#issuecomment-719475429
// this package does not provide a way to get the cache size
return 0;
}

@override
Future<void> clearAll() async {
await emptyCache();
}
}
8 changes: 8 additions & 0 deletions frontend/appflowy_flutter/lib/startup/deps_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_p
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/stability_ai/stability_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/user/application/auth/af_cloud_auth_service.dart';
Expand Down Expand Up @@ -128,6 +130,12 @@ void _resolveCommonService(
getIt.registerFactory<BaseAppearance>(
() => PlatformExtension.isMobile ? MobileAppearance() : DesktopAppearance(),
);

getIt.registerFactory<FlowyCacheManager>(
() => FlowyCacheManager()
..registerCache(TemporaryDirectoryCache())
..registerCache(CustomImageCacheManager()),
);
}

void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_exporter_widget.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart';

import '../../../../generated/locale_keys.g.dart';
import '../../../../../generated/locale_keys.g.dart';

class SettingsExportFileWidget extends StatefulWidget {
const SettingsExportFileWidget({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';

class SettingsFileCacheWidget extends StatelessWidget {
const SettingsFileCacheWidget({
super.key,
});

@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: FlowyText.medium(
LocaleKeys.settings_files_clearCache.tr(),
fontSize: 13,
overflow: TextOverflow.ellipsis,
),
),
const VSpace(8),
Opacity(
opacity: 0.6,
child: FlowyText(
LocaleKeys.settings_files_clearCacheDesc.tr(),
fontSize: 10,
maxLines: 3,
),
),
],
),
),
const _ClearCacheButton(),
],
);
}
}

class _ClearCacheButton extends StatelessWidget {
const _ClearCacheButton();

@override
Widget build(BuildContext context) {
return FlowyIconButton(
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
tooltipText: LocaleKeys.settings_files_clearCache.tr(),
icon: FlowySvg(
FlowySvgs.delete_s,
size: const Size.square(18),
color: Theme.of(context).iconTheme.color,
),
onPressed: () {
NavigatorAlertDialog(
title: LocaleKeys.settings_files_areYouSureToClearCache.tr(),
confirm: () async {
await getIt<FlowyCacheManager>().clearAllCache();
if (context.mounted) {
showSnackBarMessage(
context,
LocaleKeys.settings_files_clearCacheSuccess.tr(),
);
}
},
).show(context);
},
);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import 'dart:io';

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

import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
Expand All @@ -12,12 +9,14 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';

import '../../../../generated/locale_keys.g.dart';
import '../../../../startup/startup.dart';
import '../../../../startup/tasks/prelude.dart';
import '../../../../../generated/locale_keys.g.dart';
import '../../../../../startup/startup.dart';
import '../../../../../startup/tasks/prelude.dart';

class SettingsFileLocationCustomizer extends StatefulWidget {
const SettingsFileLocationCustomizer({
Expand Down Expand Up @@ -262,7 +261,7 @@ class _RecoverDefaultStorageButtonState
tooltipText: LocaleKeys.settings_files_recoverLocationTooltips.tr(),
icon: const FlowySvg(
FlowySvgs.restore_s,
size: Size.square(24),
size: Size.square(20),
),
onPressed: () async {
// reset to the default directory and reload app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:path/path.dart' as p;

import '../../../../generated/locale_keys.g.dart';
import '../../../../../generated/locale_keys.g.dart';

class FileExporterWidget extends StatefulWidget {
const FileExporterWidget({super.key});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:appflowy/workspace/presentation/settings/widgets/setting_file_import_appflowy_data_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_export_file_widget.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/files/setting_file_import_appflowy_data_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_export_file_widget.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_cache_widget.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_customize_location_view.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

Expand All @@ -19,15 +21,15 @@ class _SettingsFileSystemViewState extends State<SettingsFileSystemView> {
// disable export data for v0.2.0 in release mode.
if (kDebugMode) const SettingsExportFileWidget(),
const ImportAppFlowyData(),
// clear the cache
const SettingsFileCacheWidget(),
];

@override
Widget build(BuildContext context) {
return ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) => _items[index],
separatorBuilder: (context, index) => const Divider(),
itemCount: _items.length,
return SeparatedColumn(
separatorBuilder: () => const Divider(),
children: _items,
);
}
}
6 changes: 5 additions & 1 deletion frontend/resources/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,11 @@
"recoverLocationTooltips": "Reset to AppFlowy's default data directory",
"exportFileSuccess": "Export file successfully!",
"exportFileFail": "Export file failed!",
"export": "Export"
"export": "Export",
"clearCache": "Clear cache",
"clearCacheDesc": "Clear the cache including the images, fonts, and other temporary files. This will not delete your data.",
"areYouSureToClearCache": "Are you sure to clear the cache?",
"clearCacheSuccess": "Cache cleared successfully!"
},
"user": {
"name": "Name",
Expand Down

0 comments on commit 8944edf

Please sign in to comment.