Skip to content

Commit

Permalink
feat(neon): Support custom backgrounds
Browse files Browse the repository at this point in the history
Signed-off-by: jld3103 <[email protected]>
  • Loading branch information
provokateurin committed Oct 7, 2023
1 parent 041ae0a commit 298abbd
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 41 deletions.
1 change: 1 addition & 0 deletions packages/neon/neon/lib/l10n/en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"globalOptionsThemeModeAutomatic": "Automatic",
"globalOptionsThemeOLEDAsDark": "OLED theme as dark theme",
"globalOptionsThemeKeepOriginalAccentColor": "Keep the original accent color",
"globalOptionsThemeCustomBackground": "Custom background",
"globalOptionsPushNotificationsEnabled": "Enabled",
"globalOptionsPushNotificationsEnabledDisabledNotice": "No UnifiedPush distributor could be found or you denied the permission for showing notifications. Please go to the app settings and allow notifications and go to https://unifiedpush.org/users/distributors and setup any of the listed distributors. Then re-open this app and you should be able to enable notifications",
"globalOptionsPushNotificationsDistributor": "UnifiedPush Distributor",
Expand Down
6 changes: 6 additions & 0 deletions packages/neon/neon/lib/l10n/localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,12 @@ abstract class AppLocalizations {
/// **'Keep the original accent color'**
String get globalOptionsThemeKeepOriginalAccentColor;

/// No description provided for @globalOptionsThemeCustomBackground.
///
/// In en, this message translates to:
/// **'Custom background'**
String get globalOptionsThemeCustomBackground;

/// No description provided for @globalOptionsPushNotificationsEnabled.
///
/// In en, this message translates to:
Expand Down
3 changes: 3 additions & 0 deletions packages/neon/neon/lib/l10n/localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get globalOptionsThemeKeepOriginalAccentColor => 'Keep the original accent color';

@override
String get globalOptionsThemeCustomBackground => 'Custom background';

@override
String get globalOptionsPushNotificationsEnabled => 'Enabled';

Expand Down
82 changes: 46 additions & 36 deletions packages/neon/neon/lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'package:neon/src/utils/global_options.dart';
import 'package:neon/src/utils/localizations.dart';
import 'package:neon/src/utils/provider.dart';
import 'package:neon/src/utils/push_utils.dart';
import 'package:neon/src/widgets/custom_background.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:quick_actions/quick_actions.dart';
import 'package:tray_manager/tray_manager.dart' as tray;
Expand Down Expand Up @@ -280,42 +281,51 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
valueListenable: _globalOptions.themeOLEDAsDark,
builder: (final context, final themeOLEDAsDark, final _) => ValueListenableBuilder(
valueListenable: _globalOptions.themeKeepOriginalAccentColor,
builder: (final context, final themeKeepOriginalAccentColor, final _) => StreamBuilder<Account?>(
stream: _accountsBloc.activeAccount,
builder: (final context, final activeAccountSnapshot) {
FlutterNativeSplash.remove();
return ResultBuilder<CoreOcsGetCapabilitiesResponseApplicationJson_Ocs_Data?>.behaviorSubject(
stream: activeAccountSnapshot.hasData
? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities
: null,
builder: (final context, final capabilitiesSnapshot) {
final appTheme = AppTheme(
capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming,
keepOriginalAccentColor: themeKeepOriginalAccentColor,
oledAsDark: themeOLEDAsDark,
appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(),
neonTheme: widget.neonTheme,
);

return MaterialApp.router(
localizationsDelegates: [
..._appImplementations.map((final app) => app.localizationsDelegate),
...AppLocalizations.localizationsDelegates,
],
supportedLocales: {
..._appImplementations
.map((final app) => app.supportedLocales)
.expand((final element) => element),
...AppLocalizations.supportedLocales,
},
themeMode: themeMode,
theme: appTheme.lightTheme,
darkTheme: appTheme.darkTheme,
routerConfig: _routerDelegate,
);
},
);
},
builder: (final context, final themeKeepOriginalAccentColor, final _) => ValueListenableBuilder(
valueListenable: _globalOptions.themeCustomBackground,
builder: (final context, final themeCustomBackground, final _) => StreamBuilder<Account?>(
stream: _accountsBloc.activeAccount,
builder: (final context, final activeAccountSnapshot) {
FlutterNativeSplash.remove();
return ResultBuilder<CoreOcsGetCapabilitiesResponseApplicationJson_Ocs_Data?>.behaviorSubject(
stream: activeAccountSnapshot.hasData
? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities
: null,
builder: (final context, final capabilitiesSnapshot) {
final nextcloudTheme = capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming;
final appTheme = AppTheme(
nextcloudTheme,
keepOriginalAccentColor: themeKeepOriginalAccentColor,
oledAsDark: themeOLEDAsDark,
customBackground: themeCustomBackground,
appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(),
neonTheme: widget.neonTheme,
);

return MaterialApp.router(
localizationsDelegates: [
..._appImplementations.map((final app) => app.localizationsDelegate),
...AppLocalizations.localizationsDelegates,
],
supportedLocales: {
..._appImplementations
.map((final app) => app.supportedLocales)
.expand((final element) => element),
...AppLocalizations.supportedLocales,
},
themeMode: themeMode,
theme: appTheme.lightTheme,
darkTheme: appTheme.darkTheme,
routerConfig: _routerDelegate,
builder: (final context, final child) => NeonCustomBackground(
theme: themeCustomBackground ? nextcloudTheme : null,
child: child,
),
);
},
);
},
),
),
),
),
Expand Down
5 changes: 1 addition & 4 deletions packages/neon/neon/lib/src/pages/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,7 @@ class _HomePageState extends State<HomePage> {
if (drawerAlwaysVisible) {
return Row(
children: [
ColoredBox(
color: Theme.of(context).colorScheme.background,
child: drawer,
),
drawer,
Expanded(
child: body,
),
Expand Down
3 changes: 3 additions & 0 deletions packages/neon/neon/lib/src/pages/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ class _SettingsPageState extends State<SettingsPage> {
ToggleSettingsTile(
option: globalOptions.themeKeepOriginalAccentColor,
),
ToggleSettingsTile(
option: globalOptions.themeCustomBackground,
),
],
),
SettingsCategory(
Expand Down
23 changes: 22 additions & 1 deletion packages/neon/neon/lib/src/theme/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ class AppTheme {
required this.neonTheme,
final bool keepOriginalAccentColor = false,
this.oledAsDark = false,
this.customBackground = false,
this.appThemes,
}) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor;

final CoreThemingPublicCapabilities_Theming? nextcloudTheme;
final bool keepOriginalAccentColor;
final bool oledAsDark;
final bool customBackground;
final Iterable<ThemeExtension>? appThemes;
final NeonTheme neonTheme;

Expand All @@ -40,7 +42,7 @@ class AppTheme {
ThemeData _getTheme(final Brightness brightness) {
final colorScheme = _buildColorScheme(brightness);

return ThemeData(
final theme = ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
scaffoldBackgroundColor: colorScheme.background,
Expand All @@ -54,6 +56,25 @@ class AppTheme {
...?appThemes,
],
);

if (customBackground) {
return theme.copyWith(
scaffoldBackgroundColor: Colors.transparent,
cardColor: Colors.transparent,
drawerTheme: const DrawerThemeData(
backgroundColor: Colors.transparent,
),
appBarTheme: const AppBarTheme(
backgroundColor: Colors.transparent,
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: Colors.transparent,
elevation: 0,
),
);
}

return theme;
}

ThemeData get lightTheme => _getTheme(Brightness.light);
Expand Down
9 changes: 9 additions & 0 deletions packages/neon/neon/lib/src/utils/global_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class GlobalOptions extends OptionsCollection {
themeMode,
themeOLEDAsDark,
themeKeepOriginalAccentColor,
themeCustomBackground,
pushNotificationsEnabled,
pushNotificationsDistributor,
startupMinimized,
Expand Down Expand Up @@ -140,6 +141,13 @@ class GlobalOptions extends OptionsCollection {
defaultValue: false,
);

late final themeCustomBackground = ToggleOption(
storage: storage,
key: GlobalOptionKeys.themeCustomBackground,
label: (final context) => AppLocalizations.of(context).globalOptionsThemeCustomBackground,
defaultValue: false,
);

late final pushNotificationsEnabled = ToggleOption(
storage: storage,
key: GlobalOptionKeys.pushNotificationsEnabled,
Expand Down Expand Up @@ -224,6 +232,7 @@ enum GlobalOptionKeys implements Storable {
themeMode._('theme-mode'),
themeOledAsDark._('theme-oled-as-dark'),
themeKeepOriginalAccentColor._('theme-keep-original-accent-color'),
themeCustomBackground._('theme-custom-background'),
pushNotificationsEnabled._('push-notifications-enabled'),
pushNotificationsDistributor._('push-notifications-distributor'),
startupMinimized._('startup-minimized'),
Expand Down
59 changes: 59 additions & 0 deletions packages/neon/neon/lib/src/widgets/custom_background.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/blocs.dart';
import 'package:neon/nextcloud.dart';
import 'package:neon/src/utils/hex_color.dart';
import 'package:neon/src/utils/provider.dart';
import 'package:neon/src/widgets/image.dart';

@internal
class NeonCustomBackground extends StatelessWidget {
const NeonCustomBackground({
required this.theme,
required this.child,
super.key,
});

final CoreThemingPublicCapabilities_Theming? theme;
final Widget? child;

double _opacity(final BuildContext context) => Theme.of(context).brightness == Brightness.light ? 0.2 : 0.1;

@override
Widget build(final BuildContext context) {
if (theme == null) {
return ColoredBox(
color: Theme.of(context).colorScheme.background,
child: child,
);
}

if (theme?.backgroundPlain ?? true) {
return ColoredBox(
color:
Color.lerp(HexColor(theme!.background), Theme.of(context).colorScheme.background, 1 - _opacity(context))!,
child: child,
);
}

final image = NeonCachedImage.url(
url: theme!.background,
account: NeonProvider.of<AccountsBloc>(context).activeAccount.value,
fit: BoxFit.fill,
);
return ColoredBox(
color: Theme.of(context).colorScheme.background,
child: Stack(
children: [
Positioned.fill(
child: Opacity(
opacity: _opacity(context),
child: image,
),
),
if (child != null) child!,
],
),
);
}
}

0 comments on commit 298abbd

Please sign in to comment.