diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index 4472dbd0f55..c3eafbb4501 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -276,3 +276,4 @@ hotspots precache Precache svgs +Dreps diff --git a/catalyst_voices/lib/app/view/app_content.dart b/catalyst_voices/lib/app/view/app_content.dart index 3ee59726870..01fb08a7068 100644 --- a/catalyst_voices/lib/app/view/app_content.dart +++ b/catalyst_voices/lib/app/view/app_content.dart @@ -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(), diff --git a/catalyst_voices/lib/app/view/app_precache_image_assets.dart b/catalyst_voices/lib/app/view/app_precache_image_assets.dart index 2a0b4b9d587..1a2e4ff62ed 100644 --- a/catalyst_voices/lib/app/view/app_precache_image_assets.dart +++ b/catalyst_voices/lib/app/view/app_precache_image_assets.dart @@ -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, diff --git a/catalyst_voices/lib/common/ext/brand_ext.dart b/catalyst_voices/lib/common/ext/brand_ext.dart new file mode 100644 index 00000000000..ea0cb5f665f --- /dev/null +++ b/catalyst_voices/lib/common/ext/brand_ext.dart @@ -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', + }; + } +} diff --git a/catalyst_voices/lib/common/ext/ext.dart b/catalyst_voices/lib/common/ext/ext.dart new file mode 100644 index 00000000000..0f1f0756527 --- /dev/null +++ b/catalyst_voices/lib/common/ext/ext.dart @@ -0,0 +1,2 @@ +export 'brand_ext.dart'; +export 'space_ext.dart'; diff --git a/catalyst_voices/lib/common/ext/space_ext.dart b/catalyst_voices/lib/common/ext/space_ext.dart new file mode 100644 index 00000000000..dba5b8393cc --- /dev/null +++ b/catalyst_voices/lib/common/ext/space_ext.dart @@ -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!, + }; +} diff --git a/catalyst_voices/lib/pages/overall_spaces/back_fab.dart b/catalyst_voices/lib/pages/overall_spaces/back_fab.dart new file mode 100644 index 00000000000..8995b14db78 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/back_fab.dart @@ -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); + } + }, + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/brands_navigation.dart b/catalyst_voices/lib/pages/overall_spaces/brands_navigation.dart new file mode 100644 index 00000000000..6ae5a6232df --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/brands_navigation.dart @@ -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 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 { + final VoicesColorScheme colors; + + _BackgroundColor(this.colors); + + @override + Color? resolve(Set states) { + if (states.contains(WidgetState.selected)) { + return colors.onSurfacePrimaryContainer?.withOpacity(0.12); + } + + return Colors.transparent; + } +} + +final class _ForegroundColor implements WidgetStateProperty { + final VoicesColorScheme colors; + + _ForegroundColor(this.colors); + + @override + Color? resolve(Set states) { + if (states.contains(WidgetState.disabled)) { + return colors.textOnPrimaryLevel0?.withOpacity(0.3); + } + + return colors.textOnPrimaryLevel0; + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/overall_spaces.dart b/catalyst_voices/lib/pages/overall_spaces/overall_spaces.dart new file mode 100644 index 00000000000..91a8441beb9 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/overall_spaces.dart @@ -0,0 +1 @@ +export 'overall_spaces_page.dart'; diff --git a/catalyst_voices/lib/pages/overall_spaces/overall_spaces_page.dart b/catalyst_voices/lib/pages/overall_spaces/overall_spaces_page.dart new file mode 100644 index 00000000000..d8ce21a60ac --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/overall_spaces_page.dart @@ -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(), + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/discovery_overview.dart b/catalyst_voices/lib/pages/overall_spaces/space/discovery_overview.dart new file mode 100644 index 00000000000..f590e19ffc6 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/discovery_overview.dart @@ -0,0 +1,81 @@ +import 'package:catalyst_voices/common/ext/ext.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_nav_tile.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class DiscoveryOverview extends StatelessWidget { + const DiscoveryOverview({super.key}); + + @override + Widget build(BuildContext context) { + return SpaceOverviewContainer( + child: Column( + children: [ + SpaceOverviewHeader(Space.discovery), + _DiscoveryDashboardTile(), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + _RolesTile(), + _FeedbackTile(), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + _DocumentationTile(), + ], + ), + ); + } +} + +class _DiscoveryDashboardTile extends StatelessWidget { + const _DiscoveryDashboardTile(); + + @override + Widget build(BuildContext context) { + return SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.home), + title: Text( + 'Discovery Dashboard', + style: Theme.of(context).textTheme.bodyLarge, + ), + backgroundColor: Space.discovery.backgroundColor(context), + ); + } +} + +class _RolesTile extends StatelessWidget { + const _RolesTile(); + + @override + Widget build(BuildContext context) { + return SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.user), + title: Text('Catalyst Roles'), + ); + } +} + +class _FeedbackTile extends StatelessWidget { + const _FeedbackTile(); + + @override + Widget build(BuildContext context) { + return SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.annotation), + title: Text('Feedback'), + ); + } +} + +class _DocumentationTile extends StatelessWidget { + const _DocumentationTile(); + + @override + Widget build(BuildContext context) { + return SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.arrow_right), + title: Text('Catalyst Gitbook documentation'), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/funded_projects_overview.dart b/catalyst_voices/lib/pages/overall_spaces/space/funded_projects_overview.dart new file mode 100644 index 00000000000..a0f65d2f5f1 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/funded_projects_overview.dart @@ -0,0 +1,19 @@ +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class FundedProjectsOverview extends StatelessWidget { + const FundedProjectsOverview({super.key}); + + @override + Widget build(BuildContext context) { + return SpaceOverviewContainer( + child: Column( + children: [ + SpaceOverviewHeader(Space.fundedProjects), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/space_overview_header.dart b/catalyst_voices/lib/pages/overall_spaces/space/space_overview_header.dart new file mode 100644 index 00000000000..e55a55132d0 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/space_overview_header.dart @@ -0,0 +1,40 @@ +import 'package:catalyst_voices/common/ext/ext.dart'; +import 'package:catalyst_voices/widgets/widgets.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'; + +class SpaceOverviewHeader extends StatelessWidget { + final Space space; + + const SpaceOverviewHeader( + this.space, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 18), + child: Row( + children: [ + SpaceAvatar(space), + SizedBox(width: 12), + Expanded( + child: Text( + space.localizedName(context.l10n), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.titleMedium?.copyWith( + color: theme.colors.textOnPrimaryLevel0, + ), + ), + ) + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/space_overview_nav_tile.dart b/catalyst_voices/lib/pages/overall_spaces/space/space_overview_nav_tile.dart new file mode 100644 index 00000000000..4390bc38e21 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/space_overview_nav_tile.dart @@ -0,0 +1,61 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class SpaceOverviewNavTile extends StatelessWidget { + final Widget leading; + final Widget title; + final Color? backgroundColor; + final VoidCallback? onTap; + + const SpaceOverviewNavTile({ + super.key, + required this.leading, + required this.title, + this.backgroundColor, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final iconTheme = IconThemeData( + size: 24, + color: theme.colors.iconsForeground, + ); + + final textStyle = (theme.textTheme.labelLarge ?? TextStyle()).copyWith( + color: theme.colors.textOnPrimaryLevel0, + ); + + return IconTheme( + data: iconTheme, + child: DefaultTextStyle( + style: textStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: ConstrainedBox( + constraints: BoxConstraints.tightFor(height: 56), + child: Material( + color: backgroundColor ?? Colors.transparent, + borderRadius: BorderRadius.circular(25), + textStyle: textStyle, + child: InkWell( + onTap: onTap, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + leading, + SizedBox(width: 12), + Expanded(child: title), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/treasury_overview.dart b/catalyst_voices/lib/pages/overall_spaces/space/treasury_overview.dart new file mode 100644 index 00000000000..1dd78d60901 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/treasury_overview.dart @@ -0,0 +1,57 @@ +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class TreasuryOverview extends StatelessWidget { + const TreasuryOverview({super.key}); + + @override + Widget build(BuildContext context) { + return SpaceOverviewContainer( + child: Column( + children: [ + SpaceOverviewHeader(Space.treasury), + SectionHeader(title: Text('Individual private campaigns')), + VoicesNavTile( + name: 'Fund name 1', + status: ProposalStatus.ready, + trailing: MoreOptionsButton(), + ), + VoicesNavTile( + name: 'Fund name 2', + status: ProposalStatus.draft, + trailing: MoreOptionsButton(), + ), + VoicesNavTile( + name: 'Fund name 3', + status: ProposalStatus.draft, + trailing: MoreOptionsButton(), + ), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + SectionHeader(title: Text('Team private campaigns')), + VoicesNavTile( + name: 'Fund name', + status: ProposalStatus.private, + trailing: MoreOptionsButton(), + ), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + SectionHeader(title: Text('Public campaigns')), + VoicesNavTile( + name: 'Fund 14', + status: ProposalStatus.live, + trailing: MoreOptionsButton(), + ), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + SectionHeader(title: Text('Completed campaigns')), + VoicesNavTile( + name: 'Fund 15', + status: ProposalStatus.completed, + trailing: MoreOptionsButton(), + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/voting_overview.dart b/catalyst_voices/lib/pages/overall_spaces/space/voting_overview.dart new file mode 100644 index 00000000000..b8908dfb909 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/voting_overview.dart @@ -0,0 +1,47 @@ +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_nav_tile.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class VotingOverview extends StatelessWidget { + const VotingOverview({super.key}); + + @override + Widget build(BuildContext context) { + return SpaceOverviewContainer( + child: Column( + children: [ + SpaceOverviewHeader(Space.voting), + SectionHeader(title: Text('Active funding rounds')), + VoicesNavTile( + leading: Icon(CatalystVoicesIcons.vote), + name: 'Voting round 14', + status: ProposalStatus.open, + ), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + SectionHeader(title: Text('Funding tracks / Categories')), + SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.document), + title: Text( + 'My fund 14 proposal', + style: Theme.of(context).textTheme.labelMedium, + ), + ), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + SectionHeader(title: Text('Dreps')), + SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.user), + title: Text('Drep signup'), + ), + SpaceOverviewNavTile( + leading: Icon(CatalystVoicesIcons.user), + title: Text('Drep delegation'), + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space/workspace_overview.dart b/catalyst_voices/lib/pages/overall_spaces/space/workspace_overview.dart new file mode 100644 index 00000000000..d19a5a9c476 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space/workspace_overview.dart @@ -0,0 +1,37 @@ +import 'package:catalyst_voices/pages/overall_spaces/space/space_overview_header.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space_overview_container.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class WorkspaceOverview extends StatelessWidget { + const WorkspaceOverview({super.key}); + + @override + Widget build(BuildContext context) { + return SpaceOverviewContainer( + child: Column( + children: [ + SpaceOverviewHeader(Space.workspace), + SectionHeader(title: Text('My private proposals (3/5)')), + VoicesNavTile( + name: 'My first proposal', + status: ProposalStatus.draft, + trailing: MoreOptionsButton(), + ), + VoicesNavTile( + name: 'My second proposal', + status: ProposalStatus.ready, + trailing: MoreOptionsButton(), + ), + VoicesNavTile( + name: 'My third proposal', + status: ProposalStatus.ready, + trailing: MoreOptionsButton(), + ), + VoicesDivider(indent: 0, endIndent: 0, height: 16), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/space_overview_container.dart b/catalyst_voices/lib/pages/overall_spaces/space_overview_container.dart new file mode 100644 index 00000000000..a896a19be4c --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/space_overview_container.dart @@ -0,0 +1,24 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class SpaceOverviewContainer extends StatelessWidget { + final Widget child; + + const SpaceOverviewContainer({ + super.key, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Container( + constraints: BoxConstraints.tightFor(width: 360), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv1White, + ), + padding: EdgeInsets.all(12), + child: child, + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/spaces_overview_list_view.dart b/catalyst_voices/lib/pages/overall_spaces/spaces_overview_list_view.dart new file mode 100644 index 00000000000..21b2e162d25 --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/spaces_overview_list_view.dart @@ -0,0 +1,54 @@ +import 'package:catalyst_voices/pages/overall_spaces/space/discovery_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/funded_projects_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/treasury_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/voting_overview.dart'; +import 'package:catalyst_voices/pages/overall_spaces/space/workspace_overview.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class SpacesListView extends StatefulWidget { + const SpacesListView({ + super.key, + }); + + @override + State createState() => _SpacesListViewState(); +} + +class _SpacesListViewState extends State { + final _scrollController = ScrollController(); + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return VoicesScrollbar( + controller: _scrollController, + alwaysVisible: true, + padding: EdgeInsets.symmetric(horizontal: 6), + child: ListView.separated( + controller: _scrollController, + scrollDirection: Axis.horizontal, + padding: EdgeInsets.only(right: 16, bottom: 24), + itemBuilder: (context, index) { + final space = Space.values[index]; + return switch (space) { + Space.treasury => TreasuryOverview(key: ObjectKey(space)), + Space.discovery => DiscoveryOverview(key: ObjectKey(space)), + Space.workspace => WorkspaceOverview(key: ObjectKey(space)), + Space.voting => VotingOverview(key: ObjectKey(space)), + Space.fundedProjects => + FundedProjectsOverview(key: ObjectKey(space)), + }; + }, + separatorBuilder: (context, index) => SizedBox(width: 16), + itemCount: Space.values.length, + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/overall_spaces/update_ready.dart b/catalyst_voices/lib/pages/overall_spaces/update_ready.dart new file mode 100644 index 00000000000..73a5e33196d --- /dev/null +++ b/catalyst_voices/lib/pages/overall_spaces/update_ready.dart @@ -0,0 +1,93 @@ +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 UpdateReady extends StatelessWidget { + final VoidCallback? onTap; + + const UpdateReady({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final textStyle = TextStyle( + color: theme.colors.textOnPrimaryLevel0, + ); + final iconTheme = IconThemeData( + color: theme.colors.iconsForeground, + ); + + return DefaultTextStyle( + style: textStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: IconTheme( + data: iconTheme, + child: Material( + color: theme.colors.elevationsOnSurfaceNeutralLv1White, + borderRadius: BorderRadius.circular(12), + textStyle: textStyle, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 12), + child: Row( + children: [ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _UpdateReadyText(), + _ClickToRestartText(), + ], + ), + SizedBox(width: 16), + _DownloadIcon(), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _UpdateReadyText extends StatelessWidget { + const _UpdateReadyText(); + + @override + Widget build(BuildContext context) { + return Text( + context.l10n.voicesUpdateReady, + style: Theme.of(context).textTheme.bodyMedium, + ); + } +} + +class _ClickToRestartText extends StatelessWidget { + const _ClickToRestartText(); + + @override + Widget build(BuildContext context) { + return Text( + context.l10n.clickToRestart, + style: Theme.of(context).textTheme.bodySmall, + ); + } +} + +class _DownloadIcon extends StatelessWidget { + const _DownloadIcon(); + + @override + Widget build(BuildContext context) { + return Icon(CatalystVoicesIcons.cloud_download); + } +} diff --git a/catalyst_voices/lib/pages/spaces/individual_private_campaigns.dart b/catalyst_voices/lib/pages/spaces/individual_private_campaigns.dart index db2374d5d61..29a96f0c96a 100644 --- a/catalyst_voices/lib/pages/spaces/individual_private_campaigns.dart +++ b/catalyst_voices/lib/pages/spaces/individual_private_campaigns.dart @@ -14,17 +14,20 @@ class IndividualPrivateCampaigns extends StatelessWidget { leading: SizedBox(width: 12), title: Text('Individual private campaigns'), ), - VoicesDrawerNavItem( + VoicesNavTile( name: 'Fund name 1', status: ProposalStatus.ready, + trailing: MoreOptionsButton(), ), - VoicesDrawerNavItem( + VoicesNavTile( name: 'Campaign 1', status: ProposalStatus.draft, + trailing: MoreOptionsButton(), ), - VoicesDrawerNavItem( + VoicesNavTile( name: 'What happens with a campaign title that is longer that', status: ProposalStatus.draft, + trailing: MoreOptionsButton(), ), ], ); diff --git a/catalyst_voices/lib/pages/spaces/my_private_proposals.dart b/catalyst_voices/lib/pages/spaces/my_private_proposals.dart index 9bd7017c46c..25f87c9520b 100644 --- a/catalyst_voices/lib/pages/spaces/my_private_proposals.dart +++ b/catalyst_voices/lib/pages/spaces/my_private_proposals.dart @@ -14,17 +14,20 @@ class MyPrivateProposals extends StatelessWidget { leading: SizedBox(width: 12), title: Text('My private proposals (3/5)'), ), - VoicesDrawerNavItem( + VoicesNavTile( name: 'My first proposal', status: ProposalStatus.draft, + trailing: MoreOptionsButton(), ), - VoicesDrawerNavItem( + VoicesNavTile( name: 'My second proposal', status: ProposalStatus.inProgress, + trailing: MoreOptionsButton(), ), - VoicesDrawerNavItem( + VoicesNavTile( name: 'My third proposal', status: ProposalStatus.inProgress, + trailing: MoreOptionsButton(), ), ], ); diff --git a/catalyst_voices/lib/pages/spaces/spaces_drawer.dart b/catalyst_voices/lib/pages/spaces/spaces_drawer.dart index 202544a0cb6..e2ba0600c96 100644 --- a/catalyst_voices/lib/pages/spaces/spaces_drawer.dart +++ b/catalyst_voices/lib/pages/spaces/spaces_drawer.dart @@ -27,6 +27,10 @@ class SpacesDrawer extends StatelessWidget { Scaffold.of(context).closeDrawer(); _goTo(context, space: space); }, + onOverallTap: () { + Scaffold.of(context).closeDrawer(); + OverallSpacesRoute().push(context); + }, ), ); } diff --git a/catalyst_voices/lib/routes/routing/overall_spaces_route.dart b/catalyst_voices/lib/routes/routing/overall_spaces_route.dart new file mode 100644 index 00000000000..b2019050198 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/overall_spaces_route.dart @@ -0,0 +1,20 @@ +import 'package:catalyst_voices/pages/overall_spaces/overall_spaces.dart'; +import 'package:catalyst_voices/routes/routing/routes.dart'; +import 'package:catalyst_voices/routes/routing/transitions/fade_page_transition_mixin.dart'; +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; + +part 'overall_spaces_route.g.dart'; + +@TypedGoRoute( + path: '/${Routes.currentMilestone}/spaces', +) +final class OverallSpacesRoute extends GoRouteData + with FadePageTransitionMixin { + const OverallSpacesRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return OverallSpacesPage(); + } +} diff --git a/catalyst_voices/lib/routes/routing/overall_spaces_route.g.dart b/catalyst_voices/lib/routes/routing/overall_spaces_route.g.dart new file mode 100644 index 00000000000..3160be0e8b2 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/overall_spaces_route.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'overall_spaces_route.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $overallSpacesRoute, + ]; + +RouteBase get $overallSpacesRoute => GoRouteData.$route( + path: '/m4/spaces', + factory: $OverallSpacesRouteExtension._fromState, + ); + +extension $OverallSpacesRouteExtension on OverallSpacesRoute { + static OverallSpacesRoute _fromState(GoRouterState state) => + const OverallSpacesRoute(); + + String get location => GoRouteData.$location( + '/m4/spaces', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/catalyst_voices/lib/routes/routing/routes.dart b/catalyst_voices/lib/routes/routing/routes.dart index 9f50c9166a4..e44d80c1a1a 100644 --- a/catalyst_voices/lib/routes/routing/routes.dart +++ b/catalyst_voices/lib/routes/routing/routes.dart @@ -1,6 +1,8 @@ import 'package:catalyst_voices/routes/routing/coming_soon_route.dart' as coming_soon; import 'package:catalyst_voices/routes/routing/login_route.dart' as login; +import 'package:catalyst_voices/routes/routing/overall_spaces_route.dart' + as overall_spaces; import 'package:catalyst_voices/routes/routing/spaces_route.dart' as spaces; import 'package:go_router/go_router.dart'; @@ -17,5 +19,6 @@ abstract final class Routes { ...coming_soon.$appRoutes, ...login.$appRoutes, ...spaces.$appRoutes, + ...overall_spaces.$appRoutes, ]; } diff --git a/catalyst_voices/lib/routes/routing/routing.dart b/catalyst_voices/lib/routes/routing/routing.dart index 9fb12421dd1..fbec95ce4c6 100644 --- a/catalyst_voices/lib/routes/routing/routing.dart +++ b/catalyst_voices/lib/routes/routing/routing.dart @@ -1,4 +1,5 @@ export 'coming_soon_route.dart' hide $appRoutes; export 'login_route.dart' hide $appRoutes; +export 'overall_spaces_route.dart' hide $appRoutes; export 'routes.dart'; export 'spaces_route.dart' hide $appRoutes; diff --git a/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart b/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart index 6b62f97abbb..b854c021c81 100644 --- a/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart +++ b/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart @@ -124,13 +124,17 @@ class _Title extends StatelessWidget { ), xs: ( widgets: [ - Theme.of(context).brandAssets.logoIcon.buildPicture(), + Theme.of(context) + .brandAssets + .brand + .logoIcon(context) + .buildPicture(), ], itemGap: 8 ), sm: ( widgets: [ - Theme.of(context).brandAssets.logo.buildPicture(), + Theme.of(context).brandAssets.brand.logo(context).buildPicture(), if (showSearch) SearchButton( onPressed: () {}, @@ -140,7 +144,7 @@ class _Title extends StatelessWidget { ), other: ( widgets: [ - Theme.of(context).brandAssets.logo.buildPicture(), + Theme.of(context).brandAssets.brand.logo(context).buildPicture(), if (showSearch) SearchButton( onPressed: () {}, diff --git a/catalyst_voices/lib/widgets/avatars/space_avatar.dart b/catalyst_voices/lib/widgets/avatars/space_avatar.dart index 1956e0c0a21..8e17306a2e2 100644 --- a/catalyst_voices/lib/widgets/avatars/space_avatar.dart +++ b/catalyst_voices/lib/widgets/avatars/space_avatar.dart @@ -1,6 +1,5 @@ +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_models/catalyst_voices_models.dart'; import 'package:flutter/material.dart'; @@ -27,60 +26,12 @@ class SpaceAvatar extends StatelessWidget { @override Widget build(BuildContext context) { - final config = data._config(context); - return VoicesAvatar( - icon: Icon(config.iconData), - backgroundColor: config.backgroundColor, - foregroundColor: config.foregroundColor, + icon: Icon(data.icon), + backgroundColor: data.backgroundColor(context), + foregroundColor: data.foregroundColor(context), padding: padding, radius: radius, ); } } - -final class _SpaceAvatarConfig { - final IconData iconData; - final Color backgroundColor; - final Color foregroundColor; - - _SpaceAvatarConfig({ - required this.iconData, - required this.backgroundColor, - required this.foregroundColor, - }); -} - -extension _SpaceExt on Space { - _SpaceAvatarConfig _config(BuildContext context) { - final theme = Theme.of(context); - - return switch (this) { - Space.treasury => _SpaceAvatarConfig( - iconData: CatalystVoicesIcons.fund, - backgroundColor: theme.colors.successContainer!, - foregroundColor: theme.colors.iconsSuccess!, - ), - Space.discovery => _SpaceAvatarConfig( - iconData: CatalystVoicesIcons.light_bulb, - backgroundColor: theme.colors.iconsSecondary!.withOpacity(0.16), - foregroundColor: theme.colors.iconsSecondary!, - ), - Space.workspace => _SpaceAvatarConfig( - iconData: CatalystVoicesIcons.briefcase, - backgroundColor: theme.colorScheme.primaryContainer, - foregroundColor: theme.colorScheme.primary, - ), - Space.voting => _SpaceAvatarConfig( - iconData: CatalystVoicesIcons.vote, - backgroundColor: theme.colors.warningContainer!, - foregroundColor: theme.colors.iconsWarning!, - ), - Space.fundedProjects => _SpaceAvatarConfig( - iconData: CatalystVoicesIcons.flag, - backgroundColor: theme.colors.iconsSecondary!.withOpacity(0.16), - foregroundColor: theme.colors.iconsSecondary!, - ), - }; - } -} diff --git a/catalyst_voices/lib/widgets/buttons/voices_buttons.dart b/catalyst_voices/lib/widgets/buttons/voices_buttons.dart index a096d1e930a..db9f9e94255 100644 --- a/catalyst_voices/lib/widgets/buttons/voices_buttons.dart +++ b/catalyst_voices/lib/widgets/buttons/voices_buttons.dart @@ -129,3 +129,20 @@ class XButton extends StatelessWidget { ); } } + +class MoreOptionsButton extends StatelessWidget { + final VoidCallback? onTap; + + const MoreOptionsButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + child: Icon(CatalystVoicesIcons.dots_vertical), + onTap: onTap, + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/proposal_status_container.dart b/catalyst_voices/lib/widgets/common/proposal_status_container.dart index adb1f59df51..c4413b2160b 100644 --- a/catalyst_voices/lib/widgets/common/proposal_status_container.dart +++ b/catalyst_voices/lib/widgets/common/proposal_status_container.dart @@ -92,6 +92,34 @@ extension _ProposalStatusExt on ProposalStatus { textColor: colors.textPrimary, backgroundColor: colors.onSurfaceNeutralOpaqueLv1, ), + ProposalStatus.private => _ProposalStatusContainerConfig( + iconData: CatalystVoicesIcons.eye_off, + iconColor: colors.iconsForeground, + text: context.l10n.proposalStatusPrivate, + textColor: colors.textPrimary, + backgroundColor: colors.onSurfaceNeutralOpaqueLv1, + ), + ProposalStatus.open => _ProposalStatusContainerConfig( + iconData: CatalystVoicesIcons.check_circle, + iconColor: colors.iconsSuccess, + text: context.l10n.proposalStatusOpen, + textColor: colors.textPrimary, + backgroundColor: colors.onSurfaceNeutralOpaqueLv1, + ), + ProposalStatus.live => _ProposalStatusContainerConfig( + iconData: CatalystVoicesIcons.play, + iconColor: colors.iconsForeground, + text: context.l10n.proposalStatusLive, + textColor: colors.textPrimary, + backgroundColor: colors.successContainer, + ), + ProposalStatus.completed => _ProposalStatusContainerConfig( + iconData: CatalystVoicesIcons.flag, + iconColor: colors.iconsForeground, + text: context.l10n.proposalStatusCompleted, + textColor: colors.textPrimary, + backgroundColor: colors.onSurfaceNeutralOpaqueLv1, + ), }; } } diff --git a/catalyst_voices/lib/widgets/drawer/voices_drawer.dart b/catalyst_voices/lib/widgets/drawer/voices_drawer.dart index a733ff0e02b..17384306bf8 100644 --- a/catalyst_voices/lib/widgets/drawer/voices_drawer.dart +++ b/catalyst_voices/lib/widgets/drawer/voices_drawer.dart @@ -82,7 +82,7 @@ class _Header extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Theme.of(context).brandAssets.logo.buildPicture(), + Theme.of(context).brandAssets.brand.logo(context).buildPicture(), IconButton( onPressed: Navigator.of(context).pop, icon: const Icon(CatalystVoicesIcons.x, size: 22), diff --git a/catalyst_voices/lib/widgets/drawer/voices_drawer_nav_item.dart b/catalyst_voices/lib/widgets/drawer/voices_drawer_nav_item.dart deleted file mode 100644 index ff56551e90e..00000000000 --- a/catalyst_voices/lib/widgets/drawer/voices_drawer_nav_item.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; -import 'package:catalyst_voices/widgets/common/proposal_status_container.dart'; -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; -import 'package:catalyst_voices_models/catalyst_voices_models.dart'; -import 'package:flutter/material.dart'; - -class VoicesDrawerNavItem extends StatelessWidget { - final String name; - final ProposalStatus status; - - const VoicesDrawerNavItem({ - super.key, - required this.name, - required this.status, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - final iconButtonStyle = ButtonStyle( - fixedSize: WidgetStatePropertyAll(Size.square(48)), - ); - - final nameTextStyle = theme.textTheme.labelMedium?.copyWith( - color: theme.colors.textPrimary, - ); - - return IconButtonTheme( - data: IconButtonThemeData(style: iconButtonStyle), - child: Container( - constraints: BoxConstraints(minHeight: 56), - padding: EdgeInsets.only(left: 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Text( - name, - style: nameTextStyle, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - ProposalStatusContainer(type: status), - SizedBox(width: 8), - VoicesIconButton( - child: Icon(CatalystVoicesIcons.dots_vertical), - onTap: () {}, - ), - ], - ), - ), - ); - } -} diff --git a/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart b/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart index 1e4e7393bfe..1703d1b7ace 100644 --- a/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart +++ b/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart @@ -1,4 +1,5 @@ import 'package:catalyst_voices/widgets/avatars/space_avatar.dart'; +import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; import 'package:catalyst_voices/widgets/drawer/voices_drawer.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; @@ -7,11 +8,13 @@ import 'package:flutter/material.dart'; class VoicesDrawerSpaceChooser extends StatelessWidget { final Space currentSpace; final ValueChanged onChanged; + final VoidCallback? onOverallTap; const VoicesDrawerSpaceChooser({ super.key, required this.currentSpace, required this.onChanged, + this.onOverallTap, }); @override @@ -21,9 +24,12 @@ class VoicesDrawerSpaceChooser extends StatelessWidget { selectedItem: currentSpace, onSelected: onChanged, itemBuilder: _itemBuilder, - leading: IconButton( - icon: const Icon(CatalystVoicesIcons.all_spaces_menu, size: 20), - onPressed: () {}, + leading: VoicesIconButton( + child: const Icon( + CatalystVoicesIcons.all_spaces_menu, + size: 20, + ), + onTap: onOverallTap, ), ); } diff --git a/catalyst_voices/lib/widgets/scrollbar/voices_scrollbar.dart b/catalyst_voices/lib/widgets/scrollbar/voices_scrollbar.dart new file mode 100644 index 00000000000..770cdbaa841 --- /dev/null +++ b/catalyst_voices/lib/widgets/scrollbar/voices_scrollbar.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class VoicesScrollbar extends StatelessWidget { + final bool alwaysVisible; + final ScrollController? controller; + final EdgeInsets padding; + final Widget child; + + const VoicesScrollbar({ + super.key, + this.alwaysVisible = false, + this.controller, + this.padding = EdgeInsets.zero, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return RawScrollbar( + controller: controller, + thumbVisibility: alwaysVisible, + trackVisibility: alwaysVisible, + thickness: 8.0, + trackColor: theme.colors.onSurfaceNeutral012, + thumbColor: theme.colorScheme.primary, + shape: StadiumBorder(), + padding: padding, + child: child, + ); + } +} diff --git a/catalyst_voices/lib/widgets/separators/voices_divider.dart b/catalyst_voices/lib/widgets/separators/voices_divider.dart index 20a8a965345..389dde54de5 100644 --- a/catalyst_voices/lib/widgets/separators/voices_divider.dart +++ b/catalyst_voices/lib/widgets/separators/voices_divider.dart @@ -8,6 +8,11 @@ const _kDefaultIndent = 24.0; /// It's primarily used to create horizontal dividers within the context /// of a list or other layout with specific indentation requirements. class VoicesDivider extends StatelessWidget { + /// A double value representing the height of the divider. + /// + /// See [Divider.height]. + final double? height; + /// A double value representing the indentation of the divider from the /// start of the parent container. /// @@ -25,6 +30,7 @@ class VoicesDivider extends StatelessWidget { const VoicesDivider({ super.key, + this.height, this.indent = _kDefaultIndent, this.endIndent = _kDefaultIndent, this.color, @@ -33,6 +39,7 @@ class VoicesDivider extends StatelessWidget { @override Widget build(BuildContext context) { return Divider( + height: height, indent: indent, endIndent: endIndent, color: color, diff --git a/catalyst_voices/lib/widgets/tiles/voices_nav_tile.dart b/catalyst_voices/lib/widgets/tiles/voices_nav_tile.dart new file mode 100644 index 00000000000..0470682cac3 --- /dev/null +++ b/catalyst_voices/lib/widgets/tiles/voices_nav_tile.dart @@ -0,0 +1,65 @@ +import 'package:catalyst_voices/widgets/common/proposal_status_container.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class VoicesNavTile extends StatelessWidget { + final Widget? leading; + final Widget? trailing; + final String name; + final ProposalStatus status; + + const VoicesNavTile({ + super.key, + this.leading, + this.trailing, + required this.name, + required this.status, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final iconButtonStyle = ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + ); + + final nameTextStyle = theme.textTheme.labelLarge?.copyWith( + color: theme.colors.textPrimary, + ); + + final iconTheme = IconThemeData( + size: 24, + color: theme.colors.iconsForeground, + ); + + return IconTheme( + data: iconTheme, + child: IconButtonTheme( + data: IconButtonThemeData(style: iconButtonStyle), + child: Container( + constraints: BoxConstraints(minHeight: 56), + padding: EdgeInsets.only(left: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (leading != null) leading!, + Expanded( + child: Text( + name, + style: nameTextStyle, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ProposalStatusContainer(type: status), + if (trailing != null) trailing!, + ].separatedBy(SizedBox(width: 12)).toList(), + ), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/widgets.dart b/catalyst_voices/lib/widgets/widgets.dart index d5c277f9419..2b16fae9198 100644 --- a/catalyst_voices/lib/widgets/widgets.dart +++ b/catalyst_voices/lib/widgets/widgets.dart @@ -19,7 +19,6 @@ export 'containers/space_side_panel.dart'; export 'containers/workspace_tile_container.dart'; export 'containers/workspace_text_tile_container.dart'; export 'drawer/voices_drawer.dart'; -export 'drawer/voices_drawer_nav_item.dart'; export 'drawer/voices_drawer_space_chooser.dart'; export 'footers/links_page_footer.dart'; export 'footers/standard_links_page_footer.dart'; @@ -36,6 +35,7 @@ export 'menu/voices_wallet_tile.dart'; export 'modals/voices_desktop_dialog.dart'; export 'modals/voices_dialog.dart'; export 'modals/voices_info_dialog.dart'; +export 'scrollbar/voices_scrollbar.dart'; export 'seed_phrase/seed_phrases_completer.dart'; export 'seed_phrase/seed_phrases_picker.dart'; export 'seed_phrase/seed_phrases_sequencer.dart'; @@ -46,6 +46,7 @@ export 'separators/voices_vertical_divider.dart'; export 'text_field/voices_email_text_field.dart'; export 'text_field/voices_password_text_field.dart'; export 'text_field/voices_text_field.dart'; +export 'tiles/voices_nav_tile.dart'; export 'toggles/voices_checkbox.dart'; export 'toggles/voices_checkbox_group.dart'; export 'toggles/voices_radio.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart index 7a7cdf34f52..2512b2762d6 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart @@ -11,13 +11,13 @@ part 'brand_state.dart'; /// This Bloc listens for [BrandEvent]s and updates the [BrandState] /// accordingly. /// The [BrandState] can be consumed by the [MaterialApp] to build the -/// appropriate [ThemeData] based on the [BrandKey] stored in the state. +/// appropriate [ThemeData] based on the [Brand] stored in the state. /// -/// To build the appropriate [ThemeData] based on the [BrandKey] is +/// To build the appropriate [ThemeData] based on the [Brand] is /// possible to use the [ThemeBuilder] utility form the `catalyst_voices_brands` /// package. /// -/// The [BrandChangedEvent] accepts a [BrandKey] a simple enum that +/// The [BrandChangedEvent] accepts a [Brand] a simple enum that /// contains all possible brands/themes. /// /// To trigger the theme change is just necessary to dispatch the @@ -69,6 +69,6 @@ final class BrandBloc extends Bloc { BrandChangedEvent event, Emitter emit, ) { - emit(BrandState(brandKey: event.brand)); + emit(BrandState(brand: event.brand)); } } diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_event.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_event.dart index c8191bae44d..6e350787d75 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_event.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_event.dart @@ -8,7 +8,7 @@ sealed class BrandEvent extends Equatable { } final class BrandChangedEvent extends BrandEvent { - final BrandKey brand; + final Brand brand; const BrandChangedEvent(this.brand); diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart index b5382ae3e2b..06b4329734b 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart @@ -1,11 +1,12 @@ part of 'brand_bloc.dart'; final class BrandState extends Equatable { - final BrandKey? brandKey; + final Brand brand; - const BrandState({BrandKey? brandKey}) - : brandKey = brandKey ?? BrandKey.catalyst; + const BrandState({ + this.brand = Brand.catalyst, + }); @override - List get props => [brandKey ?? '']; + List get props => [brand]; } diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/brands/brand.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/brands/brand.dart new file mode 100644 index 00000000000..2a9092a0bca --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/brands/brand.dart @@ -0,0 +1,27 @@ +import 'package:catalyst_voices_assets/generated/assets.gen.dart'; +import 'package:flutter/material.dart'; + +/// Simple Enum to store all possible Brands. +enum Brand { + catalyst; + + SvgGenImage logo(BuildContext context) { + final brightness = Theme.of(context).brightness; + + return switch (this) { + Brand.catalyst when brightness == Brightness.dark => + VoicesAssets.images.catalystLogoWhite, + Brand.catalyst => VoicesAssets.images.catalystLogo, + }; + } + + SvgGenImage logoIcon(BuildContext context) { + final brightness = Theme.of(context).brightness; + + return switch (this) { + Brand.catalyst when brightness == Brightness.dark => + VoicesAssets.images.catalystLogoIconWhite, + Brand.catalyst => VoicesAssets.images.catalystLogoIcon, + }; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/brands/brands.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/brands/brands.dart deleted file mode 100644 index 68e78f89ce3..00000000000 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/brands/brands.dart +++ /dev/null @@ -1,5 +0,0 @@ -/// Simple Enum to store all possible Brands. -enum BrandKey { - catalyst, - fallback, -} diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/catalyst_voices_brands.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/catalyst_voices_brands.dart index 504dc9581b8..433bcdb9975 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/catalyst_voices_brands.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/catalyst_voices_brands.dart @@ -1,4 +1,4 @@ -export 'brands/brands.dart'; +export 'brands/brand.dart'; export 'theme_builder/theme_builder.dart'; export 'theme_extensions/theme_extensions.dart'; export 'utils/typedefs.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_builder/theme_builder.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_builder/theme_builder.dart index 440921a96a6..1eec73d7ba5 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_builder/theme_builder.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_builder/theme_builder.dart @@ -1,37 +1,25 @@ -import 'package:catalyst_voices_brands/src/brands/brands.dart'; +import 'package:catalyst_voices_brands/src/brands/brand.dart'; import 'package:catalyst_voices_brands/src/themes/catalyst.dart'; -import 'package:catalyst_voices_brands/src/themes/fallback.dart'; import 'package:flutter/material.dart'; /// A utility class to build themes dynamically based on brand keys. /// -/// [buildTheme] can be used to obtain the corresponding theme data for the -/// [BrandKey] passed to the method. -/// -/// [buildDarkTheme] operates in the same way but picks the dark version of the -/// theme for a specific brand. -/// -/// For each brand there is a specific key defined in the [BrandKey] enum +/// For each brand there is a specific key defined in the [Brand] enum /// and a corresponding [ThemeData] in the `themes` folder. /// For each brand a light and a dark [ThemeData] should be defined. -/// -/// [buildTheme] and [buildDarkTheme] default to the [catalyst] theme. -class ThemeBuilder { - static final Map lightThemes = { - BrandKey.catalyst: catalyst, - BrandKey.fallback: fallback, - }; - - static final Map darkThemes = { - BrandKey.catalyst: darkCatalyst, - BrandKey.fallback: darkFallback, - }; - - static ThemeData buildTheme(BrandKey? brandKey) { - return lightThemes[brandKey ?? BrandKey.catalyst]!; - } - - static ThemeData buildDarkTheme(BrandKey? brandKey) { - return darkThemes[brandKey ?? BrandKey.catalyst]!; +abstract final class ThemeBuilder { + /// Can be used to obtain the corresponding theme data for the + /// [Brand] passed to the method as well as [Brightness]. + /// + /// [brand] defaults to [Brand.catalyst]. + /// [brightness] defaults to [Brightness.light]. + static ThemeData buildTheme({ + Brand brand = Brand.catalyst, + Brightness brightness = Brightness.light, + }) { + return switch (brand) { + Brand.catalyst when brightness == Brightness.dark => darkCatalyst, + Brand.catalyst => catalyst, + }; } } diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/brand_assets.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/brand_assets.dart index d722ec6065b..d6dfcb9d769 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/brand_assets.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/brand_assets.dart @@ -1,4 +1,4 @@ -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/src/brands/brand.dart'; import 'package:flutter/material.dart'; /// A `ThemeExtension` that holds brand-specific assets for theming purposes. @@ -9,27 +9,23 @@ import 'package:flutter/material.dart'; /// /// Example usage: /// ```dart -/// final logo = Theme.of(context).brandAssets.logo; -/// final logoIcon = Theme.of(context).brandAssets.logoIcon; +/// final logo = Theme.of(context).brandAssets.brand.logo(context); +/// final logoIcon = Theme.of(context).brandAssets.brand.logoIcon(context); /// ``` @immutable class BrandAssets extends ThemeExtension { - final SvgGenImage logo; - final SvgGenImage logoIcon; + final Brand brand; const BrandAssets({ - required this.logo, - required this.logoIcon, + required this.brand, }); @override ThemeExtension copyWith({ - SvgGenImage? logo, - SvgGenImage? logoIcon, + Brand? brand, }) { return BrandAssets( - logo: logo ?? this.logo, - logoIcon: logo ?? this.logoIcon, + brand: brand ?? this.brand, ); } diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart index 8c69a0324fb..04b9afad0e1 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart @@ -1,4 +1,5 @@ import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/src/brands/brand.dart'; import 'package:catalyst_voices_brands/src/theme_extensions/brand_assets.dart'; import 'package:catalyst_voices_brands/src/theme_extensions/voices_color_scheme.dart'; import 'package:catalyst_voices_brands/src/themes/widgets/buttons_theme.dart'; @@ -171,8 +172,7 @@ final ThemeData catalyst = _buildThemeData( ); final BrandAssets darkBrandAssets = BrandAssets( - logo: VoicesAssets.images.catalystLogoWhite, - logoIcon: VoicesAssets.images.catalystLogoIconWhite, + brand: Brand.catalyst, ); /// Dark [ThemeData] for the `catalyst` brand. @@ -183,8 +183,7 @@ final ThemeData darkCatalyst = _buildThemeData( ); final BrandAssets lightBrandAssets = BrandAssets( - logo: VoicesAssets.images.catalystLogo, - logoIcon: VoicesAssets.images.catalystLogoIcon, + brand: Brand.catalyst, ); TextTheme _buildTextTheme(VoicesColorScheme voicesColorScheme) { @@ -308,7 +307,7 @@ ThemeData _buildThemeData( scrolledUnderElevation: 0, ), drawerTheme: DrawerThemeData( - backgroundColor: voicesColorScheme.elevationsOnSurfaceNeutralLv0, + backgroundColor: voicesColorScheme.onSurfaceNeutralOpaqueLv0, ), dialogTheme: DialogTheme( // TODO(damian-molinski): themed value needed. We don't have it defined yet. diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/fallback.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/fallback.dart deleted file mode 100644 index bdb8b42232e..00000000000 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/fallback.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_brands/src/theme_extensions/brand_assets.dart'; -import 'package:flutter/material.dart'; - -final BrandAssets lightBrandAssets = BrandAssets( - logo: VoicesAssets.images.fallbackLogo, - logoIcon: VoicesAssets.images.fallbackLogoIcon, -); - -/// [ThemeData] for the `fallback` brand. -final ThemeData fallback = ThemeData( - colorScheme: ThemeData.light().colorScheme, - extensions: >[ - lightBrandAssets, - ], -); - -/// Dark [ThemeData] for the `fallback` brand. -final ThemeData darkFallback = ThemeData.dark(); diff --git a/catalyst_voices/packages/catalyst_voices_brands/test/src/catalyst_voices_brands_test.dart b/catalyst_voices/packages/catalyst_voices_brands/test/src/catalyst_voices_brands_test.dart index a3382b86a84..99e8461ec7a 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/test/src/catalyst_voices_brands_test.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/test/src/catalyst_voices_brands_test.dart @@ -7,7 +7,6 @@ import 'package:flutter_test/flutter_test.dart'; void main() { const catalystKey = Key('C'); - const fallbackKey = Key('F'); Widget buildApp() => BlocProvider( create: (context) => BrandBloc(), @@ -19,34 +18,30 @@ void main() { body: Row( children: [ CatalystSvgPicture.asset( - Theme.of(context).brandAssets.logo.path, + Theme.of(context).brandAssets.brand.logo(context).path, ), MaterialButton( key: catalystKey, color: Theme.of(context).primaryColor, onPressed: () { context.read().add( - const BrandChangedEvent(BrandKey.catalyst), + const BrandChangedEvent(Brand.catalyst), ); }, child: const Text('Catalyst'), ), - MaterialButton( - key: fallbackKey, - color: Theme.of(context).primaryColor, - child: const Text('Fallback'), - onPressed: () { - context.read().add( - const BrandChangedEvent(BrandKey.fallback), - ); - }, - ), ], ), ), ), - theme: ThemeBuilder.buildTheme(state.brandKey), - darkTheme: ThemeBuilder.buildTheme(state.brandKey), + theme: ThemeBuilder.buildTheme( + brand: state.brand, + brightness: Brightness.light, + ), + darkTheme: ThemeBuilder.buildTheme( + brand: state.brand, + brightness: Brightness.dark, + ), ); }, ), @@ -56,7 +51,6 @@ void main() { // Colors used in the Brand themes as primary. They are used for // the color of the widgets we are testing and they are the colors // we will check against to ensure correct rendering. - final fallbackColor = ThemeData(useMaterial3: true).primaryColor; const catalystColor = VoicesColors.lightPrimary; testWidgets('Default Catalyst theme is applied', (tester) async { @@ -65,65 +59,8 @@ void main() { ); final catalystButton = find.byKey(catalystKey); - final fallbackButton = find.byKey(fallbackKey); expect(catalystButton, findsOneWidget); - expect(fallbackButton, findsOneWidget); - expect( - tester.widget(catalystButton).color, - catalystColor, - ); - expect( - tester.widget(fallbackButton).color, - catalystColor, - ); - }); - testWidgets('Fallback Theme is applied after switch', (tester) async { - await tester.pumpWidget( - buildApp(), - ); - - final catalystButton = find.byKey(catalystKey); - final fallbackButton = find.byKey(fallbackKey); - - expect(catalystButton, findsOneWidget); - expect(fallbackButton, findsOneWidget); - - await tester.tap(fallbackButton); - // We need to wait for the animation to complete - await tester.pumpAndSettle(); - expect( - tester.widget(catalystButton).color, - fallbackColor, - ); - expect( - tester.widget(catalystButton).color, - fallbackColor, - ); - }); - - testWidgets('Catalyst Theme is applied after switch', (tester) async { - await tester.pumpWidget( - buildApp(), - ); - - final catalystButton = find.byKey(catalystKey); - final fallbackButton = find.byKey(fallbackKey); - - expect(catalystButton, findsOneWidget); - expect(fallbackButton, findsOneWidget); - - // We first switch do FallbackBrand, we wait for the animation completion - // and then we switch back to the CatalystBrand to check the correct - // color is applied. - await tester.tap(fallbackButton); - await tester.pumpAndSettle(); - await tester.tap(catalystButton); - await tester.pumpAndSettle(); - expect( - tester.widget(catalystButton).color, - catalystColor, - ); expect( tester.widget(catalystButton).color, catalystColor, @@ -134,9 +71,6 @@ void main() { final catalystLogo = CatalystSvgPicture.asset( VoicesAssets.images.catalystLogo.path, ); - final fallbackLogo = CatalystSvgPicture.asset( - VoicesAssets.images.fallbackLogo.path, - ); testWidgets('Logo from Default theme is applied', (tester) async { await tester.pumpWidget( buildApp(), @@ -150,47 +84,5 @@ void main() { catalystLogo.bytesLoader, ); }); - - testWidgets('Fallback Logo is applied after switch', (tester) async { - await tester.pumpWidget( - buildApp(), - ); - - final logo = find.byType(CatalystSvgPicture).first; - final fallbackButton = find.byKey(fallbackKey); - - expect(logo, findsOneWidget); - expect(fallbackButton, findsOneWidget); - - await tester.tap(fallbackButton); - await tester.pumpAndSettle(); - expect( - tester.widget(logo).bytesLoader, - fallbackLogo.bytesLoader, - ); - }); - - testWidgets('Catalyst Logo is applied after switch', (tester) async { - await tester.pumpWidget( - buildApp(), - ); - - final logo = find.byType(CatalystSvgPicture).first; - final catalystButton = find.byKey(catalystKey); - final fallbackButton = find.byKey(fallbackKey); - - expect(logo, findsOneWidget); - expect(catalystButton, findsOneWidget); - expect(fallbackButton, findsOneWidget); - - await tester.tap(fallbackButton); - await tester.pumpAndSettle(); - await tester.tap(catalystButton); - await tester.pumpAndSettle(); - expect( - tester.widget(logo).bytesLoader, - catalystLogo.bytesLoader, - ); - }); }); } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index ccd32c13d8b..eb9cc7c6f94 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -340,6 +340,30 @@ abstract class VoicesLocalizations { /// **'In progress'** String get proposalStatusInProgress; + /// Indicates to user that status is in private mode + /// + /// In en, this message translates to: + /// **'Private'** + String get proposalStatusPrivate; + + /// Indicates to user that status is in live mode + /// + /// In en, this message translates to: + /// **'LIVE'** + String get proposalStatusLive; + + /// Indicates to user that status is completed + /// + /// In en, this message translates to: + /// **'Completed'** + String get proposalStatusCompleted; + + /// Indicates to user that status is in open mode + /// + /// In en, this message translates to: + /// **'Open'** + String get proposalStatusOpen; + /// Label shown on a proposal card indicating that the proposal is funded. /// /// In en, this message translates to: @@ -519,6 +543,60 @@ abstract class VoicesLocalizations { /// In en, this message translates to: /// **'Followed'** String get followed; + + /// Overall spaces search brands tile name + /// + /// In en, this message translates to: + /// **'Search Brands'** + String get overallSpacesSearchBrands; + + /// Overall spaces tasks tile name + /// + /// In en, this message translates to: + /// **'Tasks'** + String get overallSpacesTasks; + + /// In different places update popup title + /// + /// In en, this message translates to: + /// **'Voices update ready'** + String get voicesUpdateReady; + + /// In different places update popup body + /// + /// In en, this message translates to: + /// **'Click to restart'** + String get clickToRestart; + + /// Name of space shown in different spaces that indicates its origin + /// + /// In en, this message translates to: + /// **'Treasury'** + String get spaceTreasuryName; + + /// Name of space shown in different spaces that indicates its origin + /// + /// In en, this message translates to: + /// **'Discovery space'** + String get spaceDiscoveryName; + + /// Name of space shown in different spaces that indicates its origin + /// + /// In en, this message translates to: + /// **'Workspace'** + String get spaceWorkspaceName; + + /// Name of space shown in different spaces that indicates its origin + /// + /// In en, this message translates to: + /// **'Treasury'** + String get spaceVotingName; + + /// Name of space shown in different spaces that indicates its origin + /// + /// In en, this message translates to: + /// **'Funded Projects'** + String get spaceFundedProjects; } class _VoicesLocalizationsDelegate extends LocalizationsDelegate { diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index b26ad9e0a12..f8394d1b711 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -133,6 +133,18 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get proposalStatusInProgress => 'In progress'; + @override + String get proposalStatusPrivate => 'Private'; + + @override + String get proposalStatusLive => 'LIVE'; + + @override + String get proposalStatusCompleted => 'Completed'; + + @override + String get proposalStatusOpen => 'Open'; + @override String get fundedProposal => 'Funded proposal'; @@ -273,4 +285,31 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get followed => 'Followed'; + + @override + String get overallSpacesSearchBrands => 'Search Brands'; + + @override + String get overallSpacesTasks => 'Tasks'; + + @override + String get voicesUpdateReady => 'Voices update ready'; + + @override + String get clickToRestart => 'Click to restart'; + + @override + String get spaceTreasuryName => 'Treasury'; + + @override + String get spaceDiscoveryName => 'Discovery space'; + + @override + String get spaceWorkspaceName => 'Workspace'; + + @override + String get spaceVotingName => 'Treasury'; + + @override + String get spaceFundedProjects => 'Funded Projects'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index 3bcc135788c..636c8bb4acd 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -133,6 +133,18 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get proposalStatusInProgress => 'In progress'; + @override + String get proposalStatusPrivate => 'Private'; + + @override + String get proposalStatusLive => 'LIVE'; + + @override + String get proposalStatusCompleted => 'Completed'; + + @override + String get proposalStatusOpen => 'Open'; + @override String get fundedProposal => 'Funded proposal'; @@ -273,4 +285,31 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get followed => 'Followed'; + + @override + String get overallSpacesSearchBrands => 'Search Brands'; + + @override + String get overallSpacesTasks => 'Tasks'; + + @override + String get voicesUpdateReady => 'Voices update ready'; + + @override + String get clickToRestart => 'Click to restart'; + + @override + String get spaceTreasuryName => 'Treasury'; + + @override + String get spaceDiscoveryName => 'Discovery space'; + + @override + String get spaceWorkspaceName => 'Workspace'; + + @override + String get spaceVotingName => 'Treasury'; + + @override + String get spaceFundedProjects => 'Funded Projects'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb index 66fe4ab2677..45eeaa54a4c 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb @@ -169,6 +169,22 @@ "@proposalStatusInProgress": { "description": "Indicates to user that status is in progress" }, + "proposalStatusPrivate": "Private", + "@proposalStatusPrivate": { + "description": "Indicates to user that status is in private mode" + }, + "proposalStatusLive": "LIVE", + "@proposalStatusLive": { + "description": "Indicates to user that status is in live mode" + }, + "proposalStatusCompleted": "Completed", + "@proposalStatusCompleted": { + "description": "Indicates to user that status is completed" + }, + "proposalStatusOpen": "Open", + "@proposalStatusOpen": { + "description": "Indicates to user that status is in open mode" + }, "fundedProposal": "Funded proposal", "@fundedProposal": { "description": "Label shown on a proposal card indicating that the proposal is funded." @@ -329,5 +345,41 @@ "followed": "Followed", "@followed": { "description": "Refers to a list of followed items." + }, + "overallSpacesSearchBrands": "Search Brands", + "@overallSpacesSearchBrands": { + "description": "Overall spaces search brands tile name" + }, + "overallSpacesTasks": "Tasks", + "@overallSpacesTasks": { + "description": "Overall spaces tasks tile name" + }, + "voicesUpdateReady": "Voices update ready", + "@voicesUpdateReady": { + "description": "In different places update popup title" + }, + "clickToRestart": "Click to restart", + "@clickToRestart": { + "description": "In different places update popup body" + }, + "spaceTreasuryName": "Treasury", + "@spaceTreasuryName": { + "description": "Name of space shown in different spaces that indicates its origin" + }, + "spaceDiscoveryName": "Discovery space", + "@spaceDiscoveryName": { + "description": "Name of space shown in different spaces that indicates its origin" + }, + "spaceWorkspaceName": "Workspace", + "@spaceWorkspaceName": { + "description": "Name of space shown in different spaces that indicates its origin" + }, + "spaceVotingName": "Treasury", + "@spaceVotingName": { + "description": "Name of space shown in different spaces that indicates its origin" + }, + "spaceFundedProjects": "Funded Projects", + "@spaceFundedProjects": { + "description": "Name of space shown in different spaces that indicates its origin" } } \ No newline at end of file diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart index cb21b0e7f4c..e57594441d1 100644 --- a/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart @@ -1,5 +1,9 @@ enum ProposalStatus { ready, draft, - inProgress; + inProgress, + private, + open, + live, + completed; } diff --git a/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart b/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart index 48f63dd2fd6..ef7c80fb103 100644 --- a/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart +++ b/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart @@ -1,8 +1,4 @@ import 'package:catalyst_voices/widgets/rich_text/voices_rich_text.dart'; -import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; -import 'package:catalyst_voices_localization/generated/catalyst_voices_localizations.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_localized_locales/flutter_localized_locales.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -12,14 +8,7 @@ void main() { group(VoicesRichText, () { testWidgets('renders correctly', (tester) async { // Given - final widget = MaterialApp( - theme: ThemeBuilder.buildTheme(BrandKey.catalyst), - localizationsDelegates: const [ - ...VoicesLocalizations.localizationsDelegates, - LocaleNamesLocalizationsDelegate(), - ], - home: VoicesRichText(), - ); + final widget = VoicesRichText(); // When await tester.pumpApp(widget); diff --git a/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart b/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart index b968fe0a13e..05f2319cebf 100644 --- a/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart +++ b/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart @@ -241,7 +241,7 @@ class _MaterialApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - theme: ThemeBuilder.buildTheme(BrandKey.catalyst), + theme: ThemeBuilder.buildTheme(brand: Brand.catalyst), home: Scaffold(body: child), ); } diff --git a/catalyst_voices/uikit_example/lib/main.dart b/catalyst_voices/uikit_example/lib/main.dart index 2addc762f5f..c0ab3a06f19 100644 --- a/catalyst_voices/uikit_example/lib/main.dart +++ b/catalyst_voices/uikit_example/lib/main.dart @@ -38,8 +38,11 @@ class _UIKitExampleAppState extends State { LocaleNamesLocalizationsDelegate(), ], localeListResolutionCallback: basicLocaleListResolution, - theme: ThemeBuilder.buildTheme(BrandKey.catalyst), - darkTheme: ThemeBuilder.buildDarkTheme(BrandKey.catalyst), + theme: ThemeBuilder.buildTheme(brand: Brand.catalyst), + darkTheme: ThemeBuilder.buildTheme( + brand: Brand.catalyst, + brightness: Brightness.dark, + ), themeMode: _themeMode, onGenerateRoute: _onGenerateRoute, ),