Skip to content

Commit

Permalink
feat: Overall Spaces (#804)
Browse files Browse the repository at this point in the history
* feat: blank overall spaces page

* feat: wip - overall spaces scaffolding

* refactor: rename BrandKey to Brand and remove fallback

* feat: overall spaces brands navigation

* feat: update ready

* refactor: move brand related assets to enum function

* refactor: move space colors and icons to SpaceExt

* feat: VoicesScrollbar

* fix: VoicesScrollbar  ScrollController

* feat: more spaces overviews

* fix: remove unused widget

* refactor: extract VoicesDrawerNavItem into VoicesNavTile
  • Loading branch information
damian-molinski authored Sep 13, 2024
1 parent 6b53a99 commit 3a43252
Show file tree
Hide file tree
Showing 57 changed files with 1,392 additions and 340 deletions.
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,4 @@ hotspots
precache
Precache
svgs
Dreps
10 changes: 8 additions & 2 deletions catalyst_voices/lib/app/view/app_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ final class AppContent extends StatelessWidget {
routerConfig: routerConfig,
// Light mode is "go to" for now.
themeMode: ThemeMode.light,
theme: ThemeBuilder.buildTheme(BrandKey.catalyst),
darkTheme: ThemeBuilder.buildDarkTheme(BrandKey.catalyst),
theme: ThemeBuilder.buildTheme(
brand: Brand.catalyst,
brightness: Brightness.light,
),
darkTheme: ThemeBuilder.buildTheme(
brand: Brand.catalyst,
brightness: Brightness.dark,
),
builder: (context, child) {
return GlobalPrecacheImages(
child: child ?? SizedBox.shrink(),
Expand Down
4 changes: 2 additions & 2 deletions catalyst_voices/lib/app/view/app_precache_image_assets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class GlobalPrecacheImages extends StatelessWidget {

return AppPrecacheImageAssets(
svgs: [
theme.brandAssets.logo,
theme.brandAssets.logoIcon,
theme.brandAssets.brand.logo(context),
theme.brandAssets.brand.logoIcon(context),
],
assets: [
VoicesAssets.images.comingSoonBkg,
Expand Down
11 changes: 11 additions & 0 deletions catalyst_voices/lib/common/ext/brand_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';

extension BrandExt on Brand {
String localizedName(VoicesLocalizations localizations) {
return switch (this) {
// not localizable
Brand.catalyst => 'Catalyst',
};
}
}
2 changes: 2 additions & 0 deletions catalyst_voices/lib/common/ext/ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'brand_ext.dart';
export 'space_ext.dart';
43 changes: 43 additions & 0 deletions catalyst_voices/lib/common/ext/space_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:flutter/material.dart';

extension SpaceExt on Space {
String localizedName(VoicesLocalizations localizations) {
return switch (this) {
Space.treasury => localizations.spaceTreasuryName,
Space.discovery => localizations.spaceDiscoveryName,
Space.workspace => localizations.spaceWorkspaceName,
Space.voting => localizations.spaceVotingName,
Space.fundedProjects => localizations.spaceFundedProjects,
};
}

IconData get icon => switch (this) {
Space.treasury => CatalystVoicesIcons.fund,
Space.discovery => CatalystVoicesIcons.light_bulb,
Space.workspace => CatalystVoicesIcons.briefcase,
Space.voting => CatalystVoicesIcons.vote,
Space.fundedProjects => CatalystVoicesIcons.flag,
};

Color backgroundColor(BuildContext context) => switch (this) {
Space.treasury => Theme.of(context).colors.successContainer!,
Space.discovery =>
Theme.of(context).colors.iconsSecondary!.withOpacity(0.16),
Space.workspace => Theme.of(context).colorScheme.primaryContainer,
Space.voting => Theme.of(context).colors.warningContainer!,
Space.fundedProjects =>
Theme.of(context).colors.iconsSecondary!.withOpacity(0.16),
};

Color foregroundColor(BuildContext context) => switch (this) {
Space.treasury => Theme.of(context).colors.iconsSuccess!,
Space.discovery => Theme.of(context).colors.iconsSecondary!,
Space.workspace => Theme.of(context).colorScheme.primary,
Space.voting => Theme.of(context).colors.iconsWarning!,
Space.fundedProjects => Theme.of(context).colors.iconsSecondary!,
};
}
31 changes: 31 additions & 0 deletions catalyst_voices/lib/pages/overall_spaces/back_fab.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:catalyst_voices/routes/routes.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

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

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return FloatingActionButton(
shape: CircleBorder(),
backgroundColor: theme.colorScheme.primary,
foregroundColor: theme.colors.iconsBackground,
child: Icon(CatalystVoicesIcons.arrow_left),
onPressed: () {
final goRouter = GoRouter.of(context);

if (goRouter.canPop()) {
goRouter.pop();
} else {
// TODO(damian-molinski): should go to initial route later
// goRouter.go(Routes.initialLocation);
TreasuryRoute().go(context);
}
},
);
}
}
186 changes: 186 additions & 0 deletions catalyst_voices/lib/pages/overall_spaces/brands_navigation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import 'package:catalyst_voices/common/ext/ext.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:flutter/material.dart';

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

@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv1White,
),
padding: EdgeInsets.symmetric(vertical: 8),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
...Brand.values.map(
(brand) {
return _BrandTile(
brand,
key: ObjectKey(brand),
onTap: () {},
);
},
),
VoicesDivider(
height: 16,
indent: 0,
endIndent: 0,
),
_SearchTile(),
_TasksTile(),
],
),
);
}
}

class _BrandTile extends StatelessWidget {
final Brand brand;
final VoidCallback? onTap;

const _BrandTile(
this.brand, {
super.key,
this.onTap,
});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isCurrent = theme.brandAssets.brand == brand;

return _BrandsNavigationTile(
onTap: onTap,
isSelected: isCurrent,
leading: brand.logoIcon(context).buildIcon(allowColorFilter: false),
content: Text(brand.localizedName(context.l10n)),
);
}
}

class _SearchTile extends StatelessWidget {
const _SearchTile();

@override
Widget build(BuildContext context) {
return _BrandsNavigationTile(
leading: Icon(CatalystVoicesIcons.search),
content: Text(context.l10n.overallSpacesSearchBrands),
);
}
}

class _TasksTile extends StatelessWidget {
const _TasksTile();

@override
Widget build(BuildContext context) {
return _BrandsNavigationTile(
leading: Icon(CatalystVoicesIcons.collection),
content: Text(context.l10n.overallSpacesTasks),
);
}
}

class _BrandsNavigationTile extends StatelessWidget {
final VoidCallback? onTap;
final bool isSelected;
final Widget leading;
final Widget content;

const _BrandsNavigationTile({
this.onTap,
this.isSelected = false,
required this.leading,
required this.content,
});

Set<WidgetState> get _states => {
if (onTap == null) WidgetState.disabled,
if (isSelected) WidgetState.selected,
};

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

final backgroundColor = _BackgroundColor(theme.colors);
final foregroundColor = _ForegroundColor(theme.colors);

final textStyle = (theme.textTheme.bodyLarge ?? TextStyle())
.copyWith(color: foregroundColor.resolve(_states));

final iconTheme = IconThemeData(
size: 24,
color: foregroundColor.resolve(_states),
);

return DefaultTextStyle(
style: textStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
child: IconTheme(
data: iconTheme,
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(height: 56),
child: Material(
textStyle: textStyle,
color: backgroundColor.resolve(_states),
child: InkWell(
onTap: onTap,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
leading,
SizedBox(width: 12),
Expanded(child: content),
],
),
),
),
),
),
),
);
}
}

final class _BackgroundColor implements WidgetStateProperty<Color?> {
final VoicesColorScheme colors;

_BackgroundColor(this.colors);

@override
Color? resolve(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return colors.onSurfacePrimaryContainer?.withOpacity(0.12);
}

return Colors.transparent;
}
}

final class _ForegroundColor implements WidgetStateProperty<Color?> {
final VoicesColorScheme colors;

_ForegroundColor(this.colors);

@override
Color? resolve(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return colors.textOnPrimaryLevel0?.withOpacity(0.3);
}

return colors.textOnPrimaryLevel0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'overall_spaces_page.dart';
51 changes: 51 additions & 0 deletions catalyst_voices/lib/pages/overall_spaces/overall_spaces_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:catalyst_voices/pages/overall_spaces/back_fab.dart';
import 'package:catalyst_voices/pages/overall_spaces/brands_navigation.dart';
import 'package:catalyst_voices/pages/overall_spaces/spaces_overview_list_view.dart';
import 'package:catalyst_voices/pages/overall_spaces/update_ready.dart';
import 'package:flutter/material.dart';

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

@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16)
.add(EdgeInsets.only(bottom: 12, left: 16)),
child: Row(
children: [
_Navigation(),
SizedBox(width: 16),
Expanded(child: SpacesListView()),
],
),
),
);
}
}

class _Navigation extends StatelessWidget {
const _Navigation();

@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 200),
child: Column(
children: [
BrandsNavigation(),
Spacer(),
UpdateReady(),
SizedBox(height: 20),
Align(
alignment: Alignment.centerLeft,
child: BackFab(),
),
],
),
);
}
}
Loading

0 comments on commit 3a43252

Please sign in to comment.