mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: upload custom theme issue (#4317)
* fix: FlowyDynamicPlugin encode issue * chore: improve the default theme color * fix: learn more button text invisiable * fix: fix read the wrong theme mode file * fix: prevent current custom theme being deleted * chore: improve theme upload UI and error prompts * chore: delete unnecessary LocaleKeys
This commit is contained in:
parent
239bf2fa70
commit
b1cc4e485b
@ -157,7 +157,8 @@ class ColorSchemeUploadPopover extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (!isBuiltin)
|
||||
// when the custom theme is not the current theme, show the remove button
|
||||
if (!isBuiltin && currentTheme != theme)
|
||||
FlowyIconButton(
|
||||
icon: const FlowySvg(
|
||||
FlowySvgs.close_s,
|
||||
|
@ -0,0 +1,8 @@
|
||||
export 'theme_confirm_delete_dialog.dart';
|
||||
export 'theme_upload_button.dart';
|
||||
export 'theme_upload_learn_more_button.dart';
|
||||
export 'theme_upload_decoration.dart';
|
||||
export 'theme_upload_failure_widget.dart';
|
||||
export 'theme_upload_loading_widget.dart';
|
||||
export 'theme_upload_view.dart';
|
||||
export 'upload_new_theme_widget.dart';
|
@ -1,14 +1,12 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'theme_upload_button.dart';
|
||||
import 'theme_upload_view.dart';
|
||||
|
||||
class ThemeUploadFailureWidget extends StatelessWidget {
|
||||
const ThemeUploadFailureWidget({super.key});
|
||||
const ThemeUploadFailureWidget({super.key, required this.errorMessage});
|
||||
|
||||
final String errorMessage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -23,17 +21,21 @@ class ThemeUploadFailureWidget extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Spacer(),
|
||||
FlowySvg(
|
||||
FlowySvgs.close_m,
|
||||
size: ThemeUploadWidget.iconSize,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
FlowyText.medium(
|
||||
LocaleKeys.settings_appearance_themeUpload_failure.tr(),
|
||||
errorMessage,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
const ThemeUploadLearnMoreButton(),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
ThemeUploadButton(color: Theme.of(context).colorScheme.error),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -0,0 +1,58 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.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:flowy_infra_ui/widget/buttons/secondary_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class ThemeUploadLearnMoreButton extends StatelessWidget {
|
||||
const ThemeUploadLearnMoreButton({super.key});
|
||||
|
||||
static const learnMoreURL =
|
||||
'https://appflowy.gitbook.io/docs/essential-documentation/themes';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: ThemeUploadWidget.buttonSize.height,
|
||||
child: IntrinsicWidth(
|
||||
child: SecondaryButton(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: FlowyText.medium(
|
||||
fontSize: ThemeUploadWidget.buttonFontSize,
|
||||
LocaleKeys.document_plugins_autoGeneratorLearnMore.tr(),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
final uri = Uri.parse(learnMoreURL);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
Dialogs.show(
|
||||
context,
|
||||
child: FlowyDialog(
|
||||
child: FlowyErrorPage.message(
|
||||
LocaleKeys
|
||||
.settings_appearance_themeUpload_urlUploadFailure
|
||||
.tr()
|
||||
.replaceAll(
|
||||
'{}',
|
||||
uri.toString(),
|
||||
),
|
||||
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ class ThemeUploadWidget extends StatefulWidget {
|
||||
|
||||
static const double borderRadius = 8;
|
||||
static const double buttonFontSize = 14;
|
||||
static const Size buttonSize = Size(72, 28);
|
||||
static const Size buttonSize = Size(100, 32);
|
||||
static const EdgeInsets padding = EdgeInsets.all(12.0);
|
||||
static const Size iconSize = Size.square(48);
|
||||
static const Widget elementSpacer = SizedBox(height: 12);
|
||||
@ -41,9 +41,10 @@ class _ThemeUploadWidgetState extends State<ThemeUploadWidget> {
|
||||
key: Key('upload_theme_loading_widget'),
|
||||
);
|
||||
},
|
||||
compilationFailure: () {
|
||||
child = const ThemeUploadFailureWidget(
|
||||
key: Key('upload_theme_failure_widget'),
|
||||
compilationFailure: (errorMessage) {
|
||||
child = ThemeUploadFailureWidget(
|
||||
key: const Key('upload_theme_failure_widget'),
|
||||
errorMessage: errorMessage,
|
||||
);
|
||||
},
|
||||
compilationSuccess: () {
|
||||
|
@ -1,21 +1,13 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'theme_upload_view.dart';
|
||||
|
||||
class UploadNewThemeWidget extends StatelessWidget {
|
||||
const UploadNewThemeWidget({super.key});
|
||||
|
||||
static const learnMoreRedirect =
|
||||
'https://appflowy.gitbook.io/docs/essential-documentation/themes';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -28,6 +20,7 @@ class UploadNewThemeWidget extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Spacer(),
|
||||
FlowySvg(
|
||||
FlowySvgs.folder_m,
|
||||
size: ThemeUploadWidget.iconSize,
|
||||
@ -38,51 +31,12 @@ class UploadNewThemeWidget extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
SizedBox(
|
||||
height: ThemeUploadWidget.buttonSize.height,
|
||||
child: IntrinsicWidth(
|
||||
child: FlowyButton(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
hoverColor: Theme.of(context).colorScheme.onBackground,
|
||||
text: FlowyText.medium(
|
||||
fontSize: ThemeUploadWidget.buttonFontSize,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
LocaleKeys.document_plugins_autoGeneratorLearnMore.tr(),
|
||||
),
|
||||
onTap: () async {
|
||||
final uri = Uri.parse(learnMoreRedirect);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
Dialogs.show(
|
||||
context,
|
||||
child: FlowyDialog(
|
||||
child: FlowyErrorPage.message(
|
||||
LocaleKeys
|
||||
.settings_appearance_themeUpload_urlUploadFailure
|
||||
.tr()
|
||||
.replaceAll(
|
||||
'{}',
|
||||
uri.toString(),
|
||||
),
|
||||
howToFix:
|
||||
LocaleKeys.errorDialog_howToFixFallback.tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const ThemeUploadLearnMoreButton(),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
const Divider(),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
const ThemeUploadButton(),
|
||||
ThemeUploadWidget.elementSpacer,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -64,14 +64,14 @@ class DefaultColorScheme extends FlowyColorScheme {
|
||||
onPrimary: _white,
|
||||
hoverBG1: _lightBg2,
|
||||
hoverBG2: _lightHover,
|
||||
hoverBG3: _lightShader6,
|
||||
hoverFG: _lightShader1,
|
||||
questionBubbleBG: _lightSelector,
|
||||
hoverBG3: _lightShader6,
|
||||
progressBarBGColor: _lightTint9,
|
||||
toolbarColor: _lightShader1,
|
||||
toggleButtonBGColor: _lightShader5,
|
||||
calendarWeekendBGColor: const Color(0xFFFBFBFC),
|
||||
gridRowCountColor: const Color(0xff000000),
|
||||
gridRowCountColor: _lightShader1,
|
||||
);
|
||||
|
||||
const DefaultColorScheme.dark()
|
||||
@ -122,7 +122,7 @@ class DefaultColorScheme extends FlowyColorScheme {
|
||||
progressBarBGColor: _darkShader3,
|
||||
toolbarColor: _darkInput,
|
||||
toggleButtonBGColor: _darkShader1,
|
||||
calendarWeekendBGColor: const Color(0xff121212),
|
||||
gridRowCountColor: _darkMain1,
|
||||
calendarWeekendBGColor: _darkShader1,
|
||||
gridRowCountColor: _darkShader5,
|
||||
);
|
||||
}
|
||||
|
@ -37,8 +37,9 @@ class DynamicPluginBloc extends Bloc<DynamicPluginEvent, DynamicPluginState> {
|
||||
return;
|
||||
}
|
||||
await FlowyPluginService.instance.addPlugin(plugin);
|
||||
} on PluginCompilationException {
|
||||
return emit(const DynamicPluginState.compilationFailure());
|
||||
} on PluginCompilationException catch (exception) {
|
||||
return emit(DynamicPluginState.compilationFailure(
|
||||
errorMessage: exception.message));
|
||||
}
|
||||
|
||||
emit(const DynamicPluginState.compilationSuccess());
|
||||
|
@ -11,7 +11,8 @@ class DynamicPluginState with _$DynamicPluginState {
|
||||
required Iterable<FlowyDynamicPlugin> plugins,
|
||||
}) = Ready;
|
||||
const factory DynamicPluginState.processing() = _Processing;
|
||||
const factory DynamicPluginState.compilationFailure() = _CompilationFailure;
|
||||
const factory DynamicPluginState.compilationFailure(
|
||||
{required String errorMessage}) = _CompilationFailure;
|
||||
const factory DynamicPluginState.deletionFailure({
|
||||
required String path,
|
||||
}) = _DeletionFailure;
|
||||
|
@ -74,21 +74,21 @@ class FlowyDynamicPlugin {
|
||||
/// compilation error will be thrown during the construction of this object.
|
||||
Future<Directory> encode() async {
|
||||
final fs = MemoryFileSystem();
|
||||
final result = fs.directory(_fsPluginName)..createSync();
|
||||
final directory = fs.directory(_fsPluginName)..createSync();
|
||||
|
||||
final lightPath = p.join(_fsPluginName, '$name.$lightExtension');
|
||||
result.childFile(lightPath).createSync();
|
||||
result
|
||||
.childFile(lightPath)
|
||||
final lightThemeFileName = '$name.$lightExtension';
|
||||
directory.childFile(lightThemeFileName).createSync();
|
||||
directory
|
||||
.childFile(lightThemeFileName)
|
||||
.writeAsStringSync(jsonEncode(theme!.lightTheme.toJson()));
|
||||
|
||||
final darkPath = p.join(_fsPluginName, '$name.$darkExtension');
|
||||
result.childFile(darkPath).createSync();
|
||||
result
|
||||
.childFile(p.join(_fsPluginName, '$name.$darkExtension'))
|
||||
final darkThemeFileName = '$name.$darkExtension';
|
||||
directory.childFile(darkThemeFileName).createSync();
|
||||
directory
|
||||
.childFile(darkThemeFileName)
|
||||
.writeAsStringSync(jsonEncode(theme!.darkTheme.toJson()));
|
||||
|
||||
return result;
|
||||
return directory;
|
||||
}
|
||||
|
||||
/// Theme plugins should have the following format.
|
||||
@ -119,13 +119,32 @@ class FlowyDynamicPlugin {
|
||||
event is File && p.basename(event.path).contains(darkExtension))
|
||||
.first as File;
|
||||
|
||||
late final FlowyColorScheme lightTheme;
|
||||
late final FlowyColorScheme darkTheme;
|
||||
|
||||
try {
|
||||
lightTheme = FlowyColorScheme.fromJson(
|
||||
await jsonDecode(await light.readAsString()));
|
||||
} catch (e) {
|
||||
throw PluginCompilationException(
|
||||
'The light theme json file is not valid.',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
darkTheme = FlowyColorScheme.fromJson(
|
||||
await jsonDecode(await dark.readAsString()));
|
||||
} catch (e) {
|
||||
throw PluginCompilationException(
|
||||
'The dark theme json file is not valid.',
|
||||
);
|
||||
}
|
||||
|
||||
final theme = AppTheme(
|
||||
themeName: name,
|
||||
builtIn: false,
|
||||
lightTheme:
|
||||
FlowyColorScheme.fromJson(jsonDecode(await light.readAsString())),
|
||||
darkTheme:
|
||||
FlowyColorScheme.fromJson(jsonDecode(await dark.readAsString())),
|
||||
lightTheme: lightTheme,
|
||||
darkTheme: darkTheme,
|
||||
);
|
||||
|
||||
return FlowyDynamicPlugin._(
|
||||
|
@ -76,8 +76,8 @@ class SecondaryButton extends StatelessWidget {
|
||||
minWidth: size.width,
|
||||
minHeight: size.height,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
bgColor: Theme.of(context).colorScheme.surface,
|
||||
outlineColor: Theme.of(context).colorScheme.primary,
|
||||
bgColor: Colors.transparent,
|
||||
outlineColor: Theme.of(context).colorScheme.onBackground,
|
||||
borderRadius: mode.borderRadius,
|
||||
onPressed: onPressed,
|
||||
child: child,
|
||||
|
@ -356,7 +356,6 @@
|
||||
"button": "Upload",
|
||||
"uploadTheme": "Upload theme",
|
||||
"description": "Upload your own AppFlowy theme using the button below.",
|
||||
"failure": "The theme that was uploaded had an invalid format.",
|
||||
"loading": "Please wait while we validate and upload your theme...",
|
||||
"uploadSuccess": "Your theme was uploaded successfully",
|
||||
"deletionFailure": "Failed to delete the theme. Try to delete it manually.",
|
||||
|
Loading…
Reference in New Issue
Block a user