From a0c83251f6a1312decbc90826e48624056b69476 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Tue, 3 Dec 2024 13:50:09 +0100 Subject: [PATCH 01/16] chore: remove workspace test data --- .../lib/pages/workspace/rich_text/answer.dart | 5 - .../workspace/rich_text/bonus_mark_up.dart | 78 ----------- .../delivery_and_accountability.dart | 53 -------- .../rich_text/feasibility_checks.dart | 12 -- .../rich_text/problem_statement.dart | 8 -- .../rich_text/public_description.dart | 121 ------------------ .../rich_text/solution_statement.dart | 8 -- .../lib/pages/workspace/rich_text/title.dart | 8 -- .../workspace/rich_text/value_for_money.dart | 16 --- .../lib/pages/workspace/workspace_page.dart | 116 ----------------- .../workspace/workspace_rich_text_step.dart | 4 +- .../workspace/workspace_setup_panel.dart | 71 +++------- .../navigation/sections_controller.dart | 20 --- .../src/navigation/sections_navigation.dart | 5 - .../workspace/capability_and_feasibility.dart | 51 -------- .../lib/src/workspace/proposal_impact.dart | 41 ------ .../lib/src/workspace/proposal_setup.dart | 27 ---- .../lib/src/workspace/proposal_solution.dart | 73 ----------- .../lib/src/workspace/proposal_summary.dart | 58 --------- .../lib/src/workspace/workspace_sections.dart | 27 ++-- 20 files changed, 32 insertions(+), 770 deletions(-) delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/answer.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/bonus_mark_up.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/delivery_and_accountability.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/feasibility_checks.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/problem_statement.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/public_description.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/solution_statement.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/title.dart delete mode 100644 catalyst_voices/apps/voices/lib/pages/workspace/rich_text/value_for_money.dart delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/capability_and_feasibility.dart delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_impact.dart delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_setup.dart delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_solution.dart delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_summary.dart diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/answer.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/answer.dart deleted file mode 100644 index ef05833b8a5..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/answer.dart +++ /dev/null @@ -1,5 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const answer = [ - {"insert": "Answer\n"} -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/bonus_mark_up.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/bonus_mark_up.dart deleted file mode 100644 index 2accf562797..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/bonus_mark_up.dart +++ /dev/null @@ -1,78 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const bonusMarkUp = [ - { - 'insert': { - 'image': - 'https://upload.wikimedia.org/wikipedia/commons/b/b6/Image_created_with_a_mobile_phone.png', - }, - 'attributes': {'style': 'width: 181.764; height: 140; '}, - }, - {'insert': '\n\n'}, - { - 'insert': 'Legend Tells About Amazonian The Great Smith', - 'attributes': {'bold': true}, - }, - {'insert': '\n\nAn ancient legend confirms '}, - { - 'insert': 'Amazonian as the Father of the Samurai Sword. Amazonian', - 'attributes': {'bold': true}, - }, - {'insert': ' and his son, '}, - { - 'insert': 'Amateur', - 'attributes': {'italic': true}, - }, - { - 'insert': - ', were the prominent smiths who led a team of armorers, employed by ' - 'Emperor Mommy (683-707) to make swords for his army of warriors. ' - "Later his son, Amateur continued his father's ", - }, - { - 'insert': 'great work', - 'attributes': {'italic': true}, - }, - {'insert': '.\n\n'}, - { - 'insert': 'Amateur', - 'attributes': {'italic': true}, - }, - { - 'insert': '\n', - 'attributes': {'list': 'bullet'}, - }, - { - 'insert': 'Amauroses', - 'attributes': {'italic': true}, - }, - { - 'insert': '\n', - 'attributes': {'list': 'bullet'}, - }, - { - 'insert': 'Amateurism', - 'attributes': {'italic': true}, - }, - { - 'insert': '\n', - 'attributes': {'list': 'bullet'}, - }, - {'insert': '\n'}, - { - 'insert': 'Sword 1', - 'attributes': {'italic': true}, - }, - { - 'insert': '\n', - 'attributes': {'list': 'ordered'}, - }, - { - 'insert': 'Sword 2', - 'attributes': {'italic': true}, - }, - { - 'insert': '\n', - 'attributes': {'list': 'ordered'}, - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/delivery_and_accountability.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/delivery_and_accountability.dart deleted file mode 100644 index db16aec5191..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/delivery_and_accountability.dart +++ /dev/null @@ -1,53 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const deliveryAndAccountability = [ - { - "insert": - "The Catalyst Team is committed to providing continuous, accessible updates to the Cardano community. Updates and outputs will also be published on the Catalyst public Gitbook. \n\nIn addition to monthly progress reports and completed milestone proof of achievement ceremonies, the Catalyst team will also promote outcomes, outputs, and general progress in the following ways:   \n\n" - }, - { - "insert": "Weekly newsletters:", - "attributes": {"bold": true} - }, - {"insert": "\nReach: 60,000 mailing list members"}, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "Each week, an update email is sent to all mailing list members to provide a run down on progress and highlight key achievements.  " - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "Fortnightly technical development updates: Reach: Averaging per week: 3000 report readers, 60,000 Twitter views, 100 retweets   " - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "
The Catalyst team will provide technical development updates every two weeks as part of the overall Cardano technical development update communications. This will amount to at least 24 updates over the next 12 months, accounting for the seasonal Winter holiday period. \n
" - }, - { - "insert": "Weekly Town Halls", - "attributes": {"bold": true} - }, - { - "insert": - "\nReach: At least 1000 viewers, up to 10,000 periodically \n
Catalyst Town Hall is a mainstay platform for communicating key progress and achievements, and provides an opportunity to gather insights from attendees about new features or potential changes to Catalyst. Catalyst funded projects that have achieved project-completion status are highlighted weekly.\n
" - }, - { - "insert": "Catalyst Blogs: ", - "attributes": {"bold": true} - }, - { - "insert": - "\nReach: Averaging 5000 readers per blog based on the last 12 months\nRegular blogs published via ProjectCatalyst.io will help to amplify progress and updates to outputs that have been achieved.\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/feasibility_checks.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/feasibility_checks.dart deleted file mode 100644 index 075cf767141..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/feasibility_checks.dart +++ /dev/null @@ -1,12 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const feasibilityChecks = [ - { - "insert": "Approach and implementation", - "attributes": {"bold": true} - }, - { - "insert": - "\n\nEngineering and security best practices will be followed to implement the solution, in addition to consultation with both the Catalyst / Cardano community and internal IOG subject matter experts from cryptography, and game theory domains. Prior user research and community feedback informs our initial understanding of challenges to solve for. \n\nCatalyst Voices intends to develop iOS, Android, and Web applications from a single code base with near-native speed and performance. \n\nWe will approach the implementation of sets of features in terms of “modules”. Each module will correspond to a user role, segmenting the experience into sections aimed at completing specific actions. \n\nRole registrations, participation history, and saved preferences will unlock new aspects of the experience to help users engage at their own pace, on their own terms.\n\nLearnings acquired through developing and maintaining existing tools (such as Catalyst Mobile App, Voting Center, Snapshot Module) will be leveraged in order to rewrite the target development frameworks by building a single platform that is highly secure, extensible, and maintainable. While we anticipate unexpected challenges in integrating all features into a single platform, our plan to leverage battle-tested reference implementations should de-risk and accelerate development significantly. \n\nThe project will also benefit from maximizing the results of prior discovery and design research. Wireframes and mock-up designs created for and after user testing for the proposal submission module to replace Ideascale will continue to refine the UX with further user feedback gathered during the delivery of this project. This will include features and UX interactions for user profiles, cross-module navigation, and embedded guidance. \n\nFinally, the development of Catalyst Voices will follow the testing and deployment framework planned for the existing Catalyst stack. The latest experimental features will be deployed to a public devnet, and the latest stable features will be deployed to a public testnet. The community will have opportunities to engage with new features and provide feedback before promoting features to the production release candidate. \n\nRecurring voting events will run every 2-weeks on the Catalyst testnet to provide more frequent opportunities to engage with each of the phases of proposing, reviewing, and voting.\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/problem_statement.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/problem_statement.dart deleted file mode 100644 index 3c431c41fb4..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/problem_statement.dart +++ /dev/null @@ -1,8 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const problemStatement = [ - { - "insert": - "Catalyst's short participation windows with fragmented UX experience and complicated manual processes frustrate and limit community engagement.\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/public_description.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/public_description.dart deleted file mode 100644 index 9e70174bb1b..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/public_description.dart +++ /dev/null @@ -1,121 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const publicDescription = [ - { - "insert": "Introducing Catalyst Voices
", - "attributes": {"bold": true} - }, - { - "insert": - "Through this 12 month project, the Catalyst Team proposes to unleash the wisdom of the community by delivering a front-end web-browser based application that radically lowers the barriers to meaningful participation in collective decision-making for voters, representatives, and proposers. \n\nA unified front-end interface to meet the needs of the Catalyst community. Developed with continuous feedback from the community, the Catalyst Team will deliver a unified experience to replace the patchwork composed of wallets, Ideascale, standalone web apps, and the Catalyst mobile app today. \n\nThe proposed product design is informed by insights gleaned over nearly three years of operating Catalyst, ongoing discovery research, and feedback from the community. \n
Prior feedback and research indicate 3 significant opportunities to improve the Catalyst experience with a unified platform:\nStreamline the experience by designing modules to serve the needs of each role" - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "Unlock continuous opportunities for ideation, feedback, building skills and reputation, as well as earning rewards" - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "Offer better guidance by creating context based on identity, preferences and intent
" - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": "The proposed outputs will: ", - "attributes": {"bold": true} - }, - { - "insert": - "\nreduce the time and steps required of Catalyst users to complete important actions eliminating the frustration of context switching " - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "accelerate the onboarding and upskilling of both casual and committed Catalyst users" - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "improve the quality of participation with just-in-time tips, and more data provided to voters about Catalyst proposals   " - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "introduce participation history that maintains data and context over time, " - }, - { - "insert": "\n", - "attributes": {"list": "bullet"} - }, - { - "insert": - "
Simplified user processes mean more time spent on activities that matter, enabling new capabilities and co-building opportunities that push the boundaries of distributed decision-making. \n\nAll these benefits add up to productivity gains, a more collaborative and focused community, and better funding decisions that create more value for the Cardano ecosystem. \n" - }, - { - "insert": - "
Unlocking Incremental Value With Milestones & Continuous Testing", - "attributes": {"bold": true} - }, - { - "insert": - "\nThe proposed project will be delivered via a series of milestones, each unlocking new capabilities and creating value for Catalyst and the Cardano \necosystems. \n" - }, - { - "insert": "
Milestones include:", - "attributes": {"bold": true} - }, - {"insert": "\nOpen Source Setup"}, - { - "insert": "\n", - "attributes": {"list": "ordered"} - }, - { - "insert": "Architectural Updates to registrations to support multiple roles" - }, - { - "insert": "\n", - "attributes": {"list": "ordered"} - }, - {"insert": "Backend and Wallet Integration Updates"}, - { - "insert": "\n", - "attributes": {"list": "ordered"} - }, - {"insert": "Voting & Delegation"}, - { - "insert": "\n", - "attributes": {"list": "ordered"} - }, - {"insert": "Proposal Submission & Commentary"}, - { - "insert": "\n", - "attributes": {"list": "ordered"} - }, - {"insert": "\n"}, - { - "insert": "Continuous Testing & Learning", - "attributes": {"bold": true} - }, - { - "insert": - "\nAlong the way, continuous delivery to the Catalyst testnet will ensure that the community has meaningful feedback loops to help guide development - rather than waiting to give feedback. Voters, representatives, and proposers will have a chance to test drive the entire Catalyst process end-to-end every 2 weeks from inside Catalyst Voices once available.\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/solution_statement.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/solution_statement.dart deleted file mode 100644 index 731e2a145a6..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/solution_statement.dart +++ /dev/null @@ -1,8 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const solutionStatement = [ - { - "insert": - "Catalyst Voices provides a unified experience and platform including production-ready liquid democracy, meaningful collaboration opportunities & data-driven context for better onboarding&decisions.\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/title.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/title.dart deleted file mode 100644 index 8c4f522d7b2..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/title.dart +++ /dev/null @@ -1,8 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const title = [ - { - "insert": - "F10 / IOG Catalyst Team : Ideascale replacement and web-browser based Voting Centre with liquid democracy aka “Catalyst Voices”\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/value_for_money.dart b/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/value_for_money.dart deleted file mode 100644 index e18697d3b5c..00000000000 --- a/catalyst_voices/apps/voices/lib/pages/workspace/rich_text/value_for_money.dart +++ /dev/null @@ -1,16 +0,0 @@ -// ignore_for_file: prefer_single_quotes, require_trailing_commas, lines_longer_than_80_chars - -const valueForMoney = [ - { - "insert": - "The requested total budget for developing this first iteration of Catalyst Voices as a functional replacement for Ideascale, the existing mobile application, and wallet interface for registration is 840,000 ADA. This is a 12 Month project.\n    \nFund 11 Period : Open Source Activation             
₳75,000     \nFund 11 Period : Voices Architectural Changes,        
₳150,000\nFund 12 Period : Backend & Wallet Integration        
₳189,000    \nFund 13 Period : Voting & Delegation Implementation  
₳200,000    \nFund 13 Period : Voices First Release - Proposal Process Implementation     
₳226,000    
\n" - }, - { - "insert": "Total: ₳840,000", - "attributes": {"bold": true} - }, - { - "insert": - "\n
To deliver this work we require a small team of rust backend developers, QA engineers, front end developers, site reliability engineers and UI designers.\n
This is a moderately complex proposal due to the development of fully decentralized role based access control to replace the Web2 authorizations and user management required by Ideascale and other typical systems. It will also require throughout the entire project, Architectural Design to work on and refine the proposed CIPs and other technical aspects of the system, Engineering Management to keep the project on track, and Product Management that the final product delights and empowers users across the community.\n
This project will also require regular updates to the community and high levels of community engagement to properly respond to and evaluate feedback or queries we are receiving. Especially as it relates to refining and finalizing the CIPs necessary to underpin the operation of this Project, which will also be critical work underpinning future work both in Catalyst Voices and in future work envisioned for the “Athena” distributed Project Catalyst which we ultimately desire to have all the functionality proposed here.\n
We will also be building and deploying test versions of Catalyst Voices continuously to our internal test deployments, and these will be publicly accessible. This is to allow the community to easily see what state the development is in and the available functionality without needing to run the system themselves. We want to allow the greatest number of Project Catalyst community members to track our progress as possible.\n" - } -]; diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart index 5e06dc2e530..02c29281eb3 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart @@ -1,119 +1,11 @@ -import 'package:catalyst_voices/pages/workspace/rich_text/answer.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/bonus_mark_up.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/delivery_and_accountability.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/feasibility_checks.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/problem_statement.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/public_description.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/solution_statement.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/title.dart'; -import 'package:catalyst_voices/pages/workspace/rich_text/value_for_money.dart'; import 'package:catalyst_voices/pages/workspace/workspace_body.dart'; import 'package:catalyst_voices/pages/workspace/workspace_navigation_panel.dart'; import 'package:catalyst_voices/pages/workspace/workspace_setup_panel.dart'; import 'package:catalyst_voices/widgets/containers/space_scaffold.dart'; import 'package:catalyst_voices/widgets/navigation/sections_controller.dart'; -import 'package:catalyst_voices_models/catalyst_voices_models.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; -final sections = [ - const ProposalSetup( - id: 0, - steps: [ - TitleStep( - id: 0, - sectionId: 0, - data: DocumentJson(title), - guidances: mockGuidance, - ), - ], - ), - ProposalSummary( - id: 1, - steps: [ - ProblemStep( - id: 0, - sectionId: 1, - data: const DocumentJson(problemStatement), - charsLimit: 200, - guidances: [ - mockGuidance[0], - ], - ), - const SolutionStep( - id: 1, - sectionId: 1, - data: DocumentJson(solutionStatement), - charsLimit: 200, - guidances: mockGuidance, - ), - const PublicDescriptionStep( - id: 2, - sectionId: 1, - data: DocumentJson(publicDescription), - charsLimit: 3000, - guidances: mockGuidance, - ), - ], - ), - const ProposalSolution( - id: 2, - steps: [ - ProblemPerspectiveStep( - id: 0, - sectionId: 2, - data: DocumentJson(answer), - charsLimit: 200, - ), - PerspectiveRationaleStep( - id: 1, - sectionId: 2, - data: DocumentJson(answer), - charsLimit: 200, - ), - ProjectEngagementStep( - id: 2, - sectionId: 2, - data: DocumentJson(answer), - charsLimit: 200, - ), - ], - ), - const ProposalImpact( - id: 3, - steps: [ - BonusMarkUpStep( - id: 0, - sectionId: 3, - data: DocumentJson(bonusMarkUp), - charsLimit: 900, - ), - ValueForMoneyStep( - id: 1, - sectionId: 3, - data: DocumentJson(valueForMoney), - charsLimit: 2600, - ), - ], - ), - const CompatibilityAndFeasibility( - id: 4, - steps: [ - DeliveryAndAccountabilityStep( - id: 0, - sectionId: 4, - data: DocumentJson(deliveryAndAccountability), - ), - FeasibilityChecksStep( - id: 1, - sectionId: 4, - data: DocumentJson(feasibilityChecks), - ), - ], - ), -]; - class WorkspacePage extends StatefulWidget { const WorkspacePage({ super.key, @@ -135,8 +27,6 @@ class _WorkspacePageState extends State { _bodyItemScrollController = ItemScrollController(); _sectionsController.attachItemsScrollController(_bodyItemScrollController); - - _populateSections(); } @override @@ -158,10 +48,4 @@ class _WorkspacePageState extends State { ), ); } - - void _populateSections() { - _sectionsController.value = SectionsControllerState.initial( - sections: sections, - ); - } } diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart index 9d5814abbc7..77ea5cc36aa 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart @@ -26,7 +26,7 @@ class _WorkspaceRichTextStepState extends State { void initState() { super.initState(); - final document = Document.fromJson(widget.step.data.value); + final document = Document.fromJson(widget.step.initialData.value); final selectionOffset = document.length == 0 ? 0 : document.length - 1; _controller = VoicesRichTextController( @@ -55,7 +55,7 @@ class _WorkspaceRichTextStepState extends State { ); }, child: VoicesRichText( - title: widget.step.localizedDesc(context), + title: widget.step.description ?? widget.step.name, controller: _controller, editModeController: _editModeController, charsLimit: widget.step.charsLimit, diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart index 279dc34265d..172fa1ee340 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart @@ -1,40 +1,9 @@ -import 'package:catalyst_voices/pages/workspace/workspace_guidance_view.dart'; import 'package:catalyst_voices/widgets/cards/comment_card.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; -const List mockGuidance = [ - Guidance( - title: 'Use a Compelling Hook or Unique Angle', - description: - '''Adding an element of intrigue or a unique approach can make your title stand out. For example, “Revolutionizing Urban Mobility with Eco-Friendly Innovation” not only describes the proposal but also piques curiosity.''', - type: GuidanceType.tips, - weight: 1, - ), - Guidance( - title: 'Be Specific and Solution-Oriented', - description: - '''Use keywords that pinpoint the problem you’re solving or the opportunity you’re capitalizing on. A title like “Streamlining Supply Chains for Cost-Effective and Rapid Delivery” instantly tells the reader what the proposal aims to achieve.''', - type: GuidanceType.mandatory, - weight: 2, - ), - Guidance( - title: 'Highlight the Benefit or Outcome', - description: - '''Make sure the reader can immediately see the value or the end result of your proposal. A title like “Boosting Engagement and Growth through Targeted Digital Strategies” puts the focus on the positive outcomes.''', - type: GuidanceType.mandatory, - weight: 1, - ), - Guidance( - title: 'Education', - description: 'Use keywords that pinpoint the problem yo', - type: GuidanceType.education, - weight: 1, - ), -]; - class WorkspaceSetupPanel extends StatelessWidget { const WorkspaceSetupPanel({super.key}); @@ -47,9 +16,7 @@ class WorkspaceSetupPanel extends StatelessWidget { tabs: [ SpaceSidePanelTab( name: 'Guidance', - body: SetupSectionListener( - SectionsControllerScope.of(context), - ), + body: const _SetupSectionListener(), ), SpaceSidePanelTab( name: 'Comments', @@ -71,30 +38,22 @@ class WorkspaceSetupPanel extends StatelessWidget { } } -class SetupSectionListener extends StatelessWidget { - final SectionsController _controller; - - const SetupSectionListener( - this._controller, { - super.key, - }); +// TODO(damian): Read bloc state +class _SetupSectionListener extends StatelessWidget { + const _SetupSectionListener(); @override Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: _controller, - builder: (context, value, _) { - final activeStepId = value.activeStepId; - final activeStepGuidances = value.activeStepGuidances; - - if (activeStepId == null) { - return Text(context.l10n.selectASection); - } else if (activeStepGuidances == null || activeStepGuidances.isEmpty) { - return Text(context.l10n.noGuidanceForThisSection); - } else { - return GuidanceView(activeStepGuidances); - } - }, - ); + return Text(context.l10n.noGuidanceForThisSection); + // final activeStepId = value.activeStepId; + // final activeStepGuidances = value.activeStepGuidances; + // + // if (activeStepId == null) { + // return Text(context.l10n.selectASection); + // } else if (activeStepGuidances == null || activeStepGuidances.isEmpty) { + // return Text(context.l10n.noGuidanceForThisSection); + // } else { + // return GuidanceView(activeStepGuidances); + // } } } diff --git a/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart b/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart index f3dc14e24be..68429977f4d 100644 --- a/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart +++ b/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart @@ -12,31 +12,18 @@ final class SectionsControllerState extends Equatable { final Set openedSections; final SectionStepId? activeStepId; final Set editStepsIds; - final GuidanceType? activeGuidance; const SectionsControllerState({ this.sections = const [], this.openedSections = const {}, this.activeStepId, this.editStepsIds = const {}, - this.activeGuidance, }); int? get activeSectionId => activeStepId?.sectionId; int? get activeStep => activeStepId?.stepId; - List? get activeStepGuidances { - final activeStepId = this.activeStepId; - if (activeStepId == null) { - return null; - } else { - return sections[activeStepId.sectionId] - .steps[activeStepId.stepId] - .guidances; - } - } - bool get allSegmentsClosed => openedSections.isEmpty; List get listItems { @@ -76,14 +63,12 @@ final class SectionsControllerState extends Equatable { Set? openedSections, Optional? activeStepId, Set? editStepsIds, - Optional? activeGuidance, }) { return SectionsControllerState( sections: sections ?? this.sections, openedSections: openedSections ?? this.openedSections, activeStepId: activeStepId.dataOr(this.activeStepId), editStepsIds: editStepsIds ?? this.editStepsIds, - activeGuidance: activeGuidance?.dataOr(this.activeGuidance), ); } @@ -93,7 +78,6 @@ final class SectionsControllerState extends Equatable { openedSections, activeStepId, editStepsIds, - activeGuidance, ]; } @@ -183,10 +167,6 @@ final class SectionsController extends ValueNotifier { ); } - void setActiveGuidance(GuidanceType? type) { - value = value.copyWith(activeGuidance: Optional(type)); - } - @override void dispose() { detachItemsScrollController(); diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart index 45a82fb464f..3edb249e211 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart @@ -26,8 +26,6 @@ abstract interface class SectionStep implements SectionsListViewItem { bool get isEditable; - List get guidances; - String localizedName(BuildContext context); } @@ -65,15 +63,12 @@ abstract base class BaseSectionStep extends Equatable implements SectionStep { final bool isEnabled; @override final bool isEditable; - @override - final List guidances; const BaseSectionStep({ required this.id, required this.sectionId, this.isEnabled = true, this.isEditable = true, - this.guidances = const [], }); @override diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/capability_and_feasibility.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/capability_and_feasibility.dart deleted file mode 100644 index c24b42a864c..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/capability_and_feasibility.dart +++ /dev/null @@ -1,51 +0,0 @@ -part of 'workspace_sections.dart'; - -final class CompatibilityAndFeasibility extends WorkspaceSection { - const CompatibilityAndFeasibility({ - required super.id, - required super.steps, - }); - - @override - String localizedName(BuildContext context) { - return 'Compatibility & Feasibility'; - } -} - -final class DeliveryAndAccountabilityStep extends RichTextStep { - const DeliveryAndAccountabilityStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - }); - - @override - String localizedName(BuildContext context) { - return 'Delivery & Accountability'; - } - - @override - String localizedDesc(BuildContext context) { - return 'How do you proof trust and accountability for your project?'; - } -} - -final class FeasibilityChecksStep extends RichTextStep { - const FeasibilityChecksStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - }); - - @override - String localizedName(BuildContext context) { - return 'Feasibility checks'; - } - - @override - String localizedDesc(BuildContext context) { - return 'How will you check if your approach will work?'; - } -} diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_impact.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_impact.dart deleted file mode 100644 index 92c55ac5280..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_impact.dart +++ /dev/null @@ -1,41 +0,0 @@ -part of 'workspace_sections.dart'; - -final class ProposalImpact extends WorkspaceSection { - const ProposalImpact({ - required super.id, - required super.steps, - }); - - @override - String localizedName(BuildContext context) { - return 'Proposal impact'; - } -} - -final class BonusMarkUpStep extends RichTextStep { - const BonusMarkUpStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - }); - - @override - String localizedName(BuildContext context) { - return 'Bonus mark-up'; - } -} - -final class ValueForMoneyStep extends RichTextStep { - const ValueForMoneyStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - }); - - @override - String localizedName(BuildContext context) { - return 'Value for Money'; - } -} diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_setup.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_setup.dart deleted file mode 100644 index 179ea21663a..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_setup.dart +++ /dev/null @@ -1,27 +0,0 @@ -part of 'workspace_sections.dart'; - -final class ProposalSetup extends WorkspaceSection { - const ProposalSetup({ - required super.id, - required super.steps, - }); - - @override - String localizedName(BuildContext context) { - return 'Proposal setup'; - } -} - -final class TitleStep extends RichTextStep { - const TitleStep({ - required super.id, - required super.sectionId, - required super.data, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Title'; - } -} diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_solution.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_solution.dart deleted file mode 100644 index 013e1ed37b2..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_solution.dart +++ /dev/null @@ -1,73 +0,0 @@ -part of 'workspace_sections.dart'; - -final class ProposalSolution extends WorkspaceSection { - const ProposalSolution({ - required super.id, - required super.steps, - }); - - @override - String localizedName(BuildContext context) { - return 'Proposal solution'; - } -} - -final class ProblemPerspectiveStep extends RichTextStep { - const ProblemPerspectiveStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Problem perspective'; - } - - @override - String localizedDesc(BuildContext context) { - return "What is your perspective on the problem you're solving?"; - } -} - -final class PerspectiveRationaleStep extends RichTextStep { - const PerspectiveRationaleStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Perspective rationale'; - } - - @override - String localizedDesc(BuildContext context) { - return 'Why did you choose this perspective?'; - } -} - -final class ProjectEngagementStep extends RichTextStep { - const ProjectEngagementStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Project engagement'; - } - - @override - String localizedDesc(BuildContext context) { - return 'Who will your project engage?'; - } -} diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_summary.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_summary.dart deleted file mode 100644 index bed4f23ffcd..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/proposal_summary.dart +++ /dev/null @@ -1,58 +0,0 @@ -part of 'workspace_sections.dart'; - -final class ProposalSummary extends WorkspaceSection { - const ProposalSummary({ - required super.id, - required super.steps, - }); - - @override - String localizedName(BuildContext context) { - return 'Proposal summary'; - } -} - -final class ProblemStep extends RichTextStep { - const ProblemStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Problem segment'; - } -} - -final class SolutionStep extends RichTextStep { - const SolutionStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Solution segment'; - } -} - -final class PublicDescriptionStep extends RichTextStep { - const PublicDescriptionStep({ - required super.id, - required super.sectionId, - required super.data, - super.charsLimit, - super.guidances, - }); - - @override - String localizedName(BuildContext context) { - return 'Public description'; - } -} diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart index 658e273775f..83666a97355 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart @@ -2,17 +2,17 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/widgets.dart'; -part 'capability_and_feasibility.dart'; -part 'proposal_impact.dart'; -part 'proposal_setup.dart'; -part 'proposal_solution.dart'; -part 'proposal_summary.dart'; +final class WorkspaceSection extends BaseSection { + final String name; -sealed class WorkspaceSection extends BaseSection { const WorkspaceSection({ required super.id, required super.steps, + required this.name, }); + + @override + String localizedName(BuildContext context) => name; } sealed class WorkspaceSectionStep extends BaseSectionStep { @@ -21,22 +21,25 @@ sealed class WorkspaceSectionStep extends BaseSectionStep { required super.sectionId, super.isEnabled, super.isEditable, - super.guidances, }); } -abstract base class RichTextStep extends WorkspaceSectionStep { - final DocumentJson data; +final class RichTextStep extends WorkspaceSectionStep { + final String name; + final String? description; + final DocumentJson initialData; final int? charsLimit; const RichTextStep({ required super.id, required super.sectionId, - required this.data, + required this.name, + this.description, + this.initialData = const DocumentJson([]), this.charsLimit, super.isEditable, - super.guidances, }); - String localizedDesc(BuildContext context) => localizedName(context); + @override + String localizedName(BuildContext context) => name; } From 89b450276543764dc3e16dcbd5552530df5714e6 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Tue, 3 Dec 2024 16:02:57 +0100 Subject: [PATCH 02/16] chore: workspace bloc --- .../voices/lib/dependency/dependencies.dart | 3 +- .../spaces/drawer/my_private_proposals.dart | 25 +---- .../lib/pages/spaces/spaces_shell_page.dart | 103 ++++++++++++------ .../lib/src/catalyst_voices_blocs.dart | 1 + .../lib/src/workspace/workspace.dart | 3 + .../lib/src/workspace/workspace_bloc.dart | 7 ++ .../lib/src/workspace/workspace_event.dart | 5 + .../lib/src/workspace/workspace_state.dart | 8 ++ 8 files changed, 102 insertions(+), 53 deletions(-) create mode 100644 catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace.dart create mode 100644 catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart create mode 100644 catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart create mode 100644 catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart diff --git a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart index 1510adc4997..99218ad77e0 100644 --- a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart +++ b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart @@ -61,7 +61,8 @@ final class Dependencies extends DependencyProvider { // TODO(ryszard-schossler): add repository for campaign management ..registerLazySingleton( CampaignBuilderCubit.new, - ); + ) + ..registerFactory(WorkspaceBloc.new); } void _registerRepositories() { diff --git a/catalyst_voices/apps/voices/lib/pages/spaces/drawer/my_private_proposals.dart b/catalyst_voices/apps/voices/lib/pages/spaces/drawer/my_private_proposals.dart index 39813bf2fa3..f05f56257e0 100644 --- a/catalyst_voices/apps/voices/lib/pages/spaces/drawer/my_private_proposals.dart +++ b/catalyst_voices/apps/voices/lib/pages/spaces/drawer/my_private_proposals.dart @@ -8,32 +8,15 @@ class MyPrivateProposals extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( + return const Column( mainAxisSize: MainAxisSize.min, children: [ - const SpaceHeader(Space.workspace), - const SectionHeader( + SpaceHeader(Space.workspace), + SectionHeader( leading: SizedBox(width: 12), title: Text('My private proposals (3/5)'), ), - VoicesNavTile( - name: 'My first proposal', - status: ProposalStatus.draft, - trailing: const MoreOptionsButton(), - onTap: () => Scaffold.of(context).closeDrawer(), - ), - VoicesNavTile( - name: 'My second proposal', - status: ProposalStatus.inProgress, - trailing: const MoreOptionsButton(), - onTap: () => Scaffold.of(context).closeDrawer(), - ), - VoicesNavTile( - name: 'My third proposal', - status: ProposalStatus.inProgress, - trailing: const MoreOptionsButton(), - onTap: () => Scaffold.of(context).closeDrawer(), - ), + // TODO(damian-molinski): watch workspace bloc ], ); } diff --git a/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart b/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart index c4810f7a3c7..d173acd49d7 100644 --- a/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart @@ -1,4 +1,5 @@ import 'package:catalyst_voices/common/ext/ext.dart'; +import 'package:catalyst_voices/dependency/dependencies.dart'; import 'package:catalyst_voices/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart'; import 'package:catalyst_voices/pages/campaign/details/widgets/campaign_management.dart'; import 'package:catalyst_voices/pages/spaces/appbar/spaces_theme_mode_switch.dart'; @@ -57,38 +58,36 @@ class _SpacesShellPageState extends State { final isVisitor = sessionBloc.state is VisitorSessionState; final isUnlocked = sessionBloc.state is ActiveAccountSessionState; - return CallbackShortcuts( - bindings: { - for (final entry in SpacesShellPage._spacesShortcutsActivators.entries) - entry.value: () => entry.key.go(context), - CampaignAdminToolsDialog.shortcut: _toggleCampaignAdminTools, - }, - child: Stack( - children: [ - Scaffold( - appBar: VoicesAppBar( - leading: isVisitor ? null : const DrawerToggleButton(), - automaticallyImplyLeading: false, - actions: _getActions(widget.space), + return _Shortcuts( + toggleCampaignAdminTools: _toggleCampaignAdminTools, + child: _BlocsProviders( + child: Stack( + children: [ + Scaffold( + appBar: VoicesAppBar( + leading: isVisitor ? null : const DrawerToggleButton(), + automaticallyImplyLeading: false, + actions: _getActions(widget.space), + ), + drawer: isVisitor + ? null + : SpacesDrawer( + space: widget.space, + spacesShortcutsActivators: + SpacesShellPage._spacesShortcutsActivators, + isUnlocked: isUnlocked, + ), + body: widget.child, ), - drawer: isVisitor - ? null - : SpacesDrawer( - space: widget.space, - spacesShortcutsActivators: - SpacesShellPage._spacesShortcutsActivators, - isUnlocked: isUnlocked, - ), - body: widget.child, - ), - if (_showAdminTools) - DraggableCampaignAdminToolsDialog( - dialogKey: _adminToolsKey, - selectedSpace: widget.space, - onSpaceSelected: (space) => space.go(context), - onClose: _closeCampaignAdminTools, - ), - ], + if (_showAdminTools) + DraggableCampaignAdminToolsDialog( + dialogKey: _adminToolsKey, + selectedSpace: widget.space, + onSpaceSelected: (space) => space.go(context), + onClose: _closeCampaignAdminTools, + ), + ], + ), ), ); } @@ -120,3 +119,45 @@ class _SpacesShellPageState extends State { }); } } + +class _Shortcuts extends StatelessWidget { + final Widget child; + final VoidCallback toggleCampaignAdminTools; + + const _Shortcuts({ + required this.child, + required this.toggleCampaignAdminTools, + }); + + @override + Widget build(BuildContext context) { + return CallbackShortcuts( + bindings: { + for (final entry in SpacesShellPage._spacesShortcutsActivators.entries) + entry.value: () => entry.key.go(context), + CampaignAdminToolsDialog.shortcut: toggleCampaignAdminTools, + }, + child: child, + ); + } +} + +class _BlocsProviders extends StatelessWidget { + final Widget child; + + const _BlocsProviders({ + required this.child, + }); + + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => Dependencies.instance.get(), + ), + ], + child: child, + ); + } +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart index 3d933105dc2..cf652f75488 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart @@ -7,3 +7,4 @@ export 'login/login.dart'; export 'proposals/proposals.dart'; export 'registration/registration.dart'; export 'session/session.dart'; +export 'workspace/workspace.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace.dart new file mode 100644 index 00000000000..e5b20e31001 --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace.dart @@ -0,0 +1,3 @@ +export 'workspace_bloc.dart'; +export 'workspace_event.dart'; +export 'workspace_state.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart new file mode 100644 index 00000000000..86d2ed3c0eb --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -0,0 +1,7 @@ +import 'package:catalyst_voices_blocs/src/workspace/workspace_event.dart'; +import 'package:catalyst_voices_blocs/src/workspace/workspace_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +final class WorkspaceBloc extends Bloc { + WorkspaceBloc() : super(const WorkspaceState()); +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart new file mode 100644 index 00000000000..46a8ed91fc1 --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart @@ -0,0 +1,5 @@ +import 'package:equatable/equatable.dart'; + +sealed class WorkspaceEvent extends Equatable { + const WorkspaceEvent(); +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart new file mode 100644 index 00000000000..bc70f87eccc --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart @@ -0,0 +1,8 @@ +import 'package:equatable/equatable.dart'; + +final class WorkspaceState extends Equatable { + const WorkspaceState(); + + @override + List get props => []; +} From ee40eced88f3b3242e02672c999ac521506c7e85 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 10:42:44 +0100 Subject: [PATCH 03/16] refactor: use MarkdownString instead of DocumentJson --- .../catalyst_voices_models/lib/src/document/document_json.dart | 1 - .../internal/catalyst_voices_models/lib/src/markdown_string.dart | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/document_json.dart create mode 100644 catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/document_json.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/document_json.dart deleted file mode 100644 index d8e1af38e29..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/document_json.dart +++ /dev/null @@ -1 +0,0 @@ -extension type const DocumentJson(List value) {} diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart new file mode 100644 index 00000000000..65525186eea --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart @@ -0,0 +1 @@ +extension type const MarkdownString(String data) {} From dc7ba4d614f025967b19c5be544079efd1f45d62 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 10:43:11 +0100 Subject: [PATCH 04/16] feat: MarkdownCodec --- .../lib/common/codecs/markdown_codec.dart | 60 +++++++++++++ .../common/codecs/markdown_codec_test.dart | 89 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart create mode 100644 catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart diff --git a/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart b/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart new file mode 100644 index 00000000000..38795f99876 --- /dev/null +++ b/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; + +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter_quill/quill_delta.dart'; +import 'package:markdown/markdown.dart' as md; +import 'package:markdown_quill/markdown_quill.dart'; + +// Note. +// This codec is here because it depends on flutter_quill which is heavy +// package with lots different dependencies which we don't want to have in +// other packages. +// +// If we could have just Delta package it would be preferred to live in +// models/shared package +const markdown = MarkdownCodec(); + +final _mdDocument = md.Document(); +final _mdToDelta = MarkdownToDelta(markdownDocument: _mdDocument); +final _deltaToMd = DeltaToMarkdown( + customContentHandler: DeltaToMarkdown.escapeSpecialCharactersRelaxed, +); + +final class MarkdownCodec extends Codec { + const MarkdownCodec(); + + @override + Converter get decoder => const MarkdownEncoder(); + + @override + Converter get encoder => const MarkdownDecoder(); +} + +class MarkdownDecoder extends Converter { + const MarkdownDecoder(); + + @override + Delta convert(MarkdownString input) { + if (input.data.isEmpty) { + return Delta(); + } + + return _mdToDelta.convert(input.data); + } +} + +class MarkdownEncoder extends Converter { + const MarkdownEncoder(); + + @override + MarkdownString convert(Delta input) { + if (input.isEmpty) { + return const MarkdownString(''); + } + + final data = _deltaToMd.convert(input); + final trimmed = data.trim(); + + return MarkdownString(trimmed); + } +} diff --git a/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart b/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart new file mode 100644 index 00000000000..9dcff952c6a --- /dev/null +++ b/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart @@ -0,0 +1,89 @@ +import 'package:catalyst_voices/common/codecs/markdown_codec.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter_quill/quill_delta.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group(MarkdownCodec, () { + test('code and encode empty string', () { + // Given + const source = MarkdownString(''); + + // When + final delta = markdown.encode(source); + final md = markdown.decode(delta); + + // Then + expect(md, source); + }); + + test('code and encode plain text', () { + // Given + const source = MarkdownString('Hello Catalyst!'); + + // When + final delta = markdown.encode(source); + final md = markdown.decode(delta); + + // Then + expect(md, source); + }); + + group('decode', () { + test('empty delta file builds empty string', () { + // Given + final delta = Delta(); + + // When + final markdownString = markdown.decode(delta); + + // Then + expect(markdownString.data, isEmpty); + }); + + test('plan text delta file builds correct string', () { + // Given + const plainText = 'Hello Catalyst!'; + final delta = Delta() + ..insert(plainText) + ..insert('\n'); + + // When + final markdownString = markdown.decode(delta); + + // Then + expect(markdownString.data, plainText); + }); + }); + + group('encode', () { + test('empty markdown string builds valid empty delta', () { + // Given + const markdownString = MarkdownString(''); + + // When + final delta = markdown.encode(markdownString); + + // Then + expect(delta.isEmpty, isTrue); + }); + + test('plan text markdown builds correct delta', () { + // Given + const plainText = 'Hello Catalyst!'; + const markdownString = MarkdownString(plainText); + + // When + final delta = markdown.encode(markdownString); + + // Then + expect(delta.isNotEmpty, isTrue); + expect(delta.operations, hasLength(1)); + + final operation = delta.operations[0]; + expect(operation.key, 'insert'); + expect(operation.data, '$plainText\n'); + }); + }); + }); +} From 8ff30aed2c2ea81fe48876a4ca21847e480ddbd1 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 10:44:08 +0100 Subject: [PATCH 05/16] chore: markdown convert lib --- catalyst_voices/apps/voices/pubspec.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/catalyst_voices/apps/voices/pubspec.yaml b/catalyst_voices/apps/voices/pubspec.yaml index 3b00fa0d7e6..01ae70d9b41 100644 --- a/catalyst_voices/apps/voices/pubspec.yaml +++ b/catalyst_voices/apps/voices/pubspec.yaml @@ -54,6 +54,8 @@ dependencies: go_router: ^14.0.2 google_fonts: ^6.2.1 intl: ^0.19.0 + markdown: ^7.2.2 + markdown_quill: ^4.2.0 mask_text_input_formatter: ^2.9.0 result_type: ^0.2.0 scrollable_positioned_list: ^0.3.8 From d9e93175bc294086a38218fde7df045d4b196dc6 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 10:44:53 +0100 Subject: [PATCH 06/16] refactor: use MarkdownString --- .../workspace/workspace_rich_text_step.dart | 7 ++++++- .../lib/src/catalyst_voices_models.dart | 2 +- .../lib/src/workspace/workspace_sections.dart | 21 ++++++++++++++++--- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart index 77ea5cc36aa..227cc6141c2 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart @@ -1,7 +1,9 @@ +import 'package:catalyst_voices/common/codecs/markdown_codec.dart'; import 'package:catalyst_voices/widgets/navigation/section_step_state_builder.dart'; import 'package:catalyst_voices/widgets/rich_text/voices_rich_text.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; @@ -26,7 +28,10 @@ class _WorkspaceRichTextStepState extends State { void initState() { super.initState(); - final document = Document.fromJson(widget.step.initialData.value); + final markdownString = widget.step.initialData ?? const MarkdownString(''); + final delta = markdown.encode(markdownString); + + final document = Document.fromDelta(delta); final selectionOffset = document.length == 0 ? 0 : document.length - 1; _controller = VoicesRichTextController( diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart index ebfd61d8e0d..0b120032658 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -8,9 +8,9 @@ export 'campaign/campaign_publish.dart'; export 'campaign/campaign_section.dart'; export 'crypto/keychain_metadata.dart'; export 'crypto/lock_factor.dart'; -export 'document/document_json.dart'; export 'errors/errors.dart'; export 'file/voices_file.dart'; +export 'markdown_string.dart'; export 'optional.dart'; export 'proposal/proposal.dart'; export 'proposal/proposal_builder.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart index 83666a97355..cc503f2deb2 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart @@ -7,12 +7,18 @@ final class WorkspaceSection extends BaseSection { const WorkspaceSection({ required super.id, - required super.steps, required this.name, + required super.steps, }); @override String localizedName(BuildContext context) => name; + + @override + List get props => [ + ...super.props, + name, + ]; } sealed class WorkspaceSectionStep extends BaseSectionStep { @@ -27,7 +33,7 @@ sealed class WorkspaceSectionStep extends BaseSectionStep { final class RichTextStep extends WorkspaceSectionStep { final String name; final String? description; - final DocumentJson initialData; + final MarkdownString? initialData; final int? charsLimit; const RichTextStep({ @@ -35,11 +41,20 @@ final class RichTextStep extends WorkspaceSectionStep { required super.sectionId, required this.name, this.description, - this.initialData = const DocumentJson([]), + this.initialData, this.charsLimit, super.isEditable, }); @override String localizedName(BuildContext context) => name; + + @override + List get props => [ + ...super.props, + name, + description, + initialData, + charsLimit, + ]; } From b8075c84db219fd6f606fe436bbd33e1d0641139 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 10:47:56 +0100 Subject: [PATCH 07/16] feat: updating sections dynamically --- .../lib/pages/workspace/workspace_page.dart | 29 +++++++++++++++++++ .../workspace/workspace_rich_text_step.dart | 2 +- .../lib/src/workspace/workspace_bloc.dart | 28 +++++++++++++++++- .../lib/src/workspace/workspace_event.dart | 7 +++++ .../lib/src/workspace/workspace_state.dart | 19 ++++++++++-- 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart index 02c29281eb3..dcac3447be3 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart @@ -1,9 +1,15 @@ +import 'dart:async'; + import 'package:catalyst_voices/pages/workspace/workspace_body.dart'; import 'package:catalyst_voices/pages/workspace/workspace_navigation_panel.dart'; import 'package:catalyst_voices/pages/workspace/workspace_setup_panel.dart'; import 'package:catalyst_voices/widgets/containers/space_scaffold.dart'; import 'package:catalyst_voices/widgets/navigation/sections_controller.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class WorkspacePage extends StatefulWidget { @@ -19,18 +25,31 @@ class _WorkspacePageState extends State { late final SectionsController _sectionsController; late final ItemScrollController _bodyItemScrollController; + StreamSubscription>? _sectionsSub; + @override void initState() { super.initState(); + final bloc = context.read() + ..add(const LoadCurrentProposalEvent()); + _sectionsController = SectionsController(); _bodyItemScrollController = ItemScrollController(); _sectionsController.attachItemsScrollController(_bodyItemScrollController); + + _sectionsSub = bloc.stream + .map((event) => event.sections) + .distinct(listEquals) + .listen(_updateSections); } @override void dispose() { + unawaited(_sectionsSub?.cancel()); + _sectionsSub = null; + _sectionsController.dispose(); super.dispose(); } @@ -48,4 +67,14 @@ class _WorkspacePageState extends State { ), ); } + + void _updateSections(List
data) { + final state = _sectionsController.value; + + final newState = state.sections.isEmpty + ? SectionsControllerState.initial(sections: data) + : state.copyWith(sections: data); + + _sectionsController.value = newState; + } } diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart index 227cc6141c2..043208cfe52 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart @@ -31,7 +31,7 @@ class _WorkspaceRichTextStepState extends State { final markdownString = widget.step.initialData ?? const MarkdownString(''); final delta = markdown.encode(markdownString); - final document = Document.fromDelta(delta); + final document = delta.isNotEmpty ? Document.fromDelta(delta) : Document(); final selectionOffset = document.length == 0 ? 0 : document.length - 1; _controller = VoicesRichTextController( diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart index 86d2ed3c0eb..6b3104de65c 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -1,7 +1,33 @@ import 'package:catalyst_voices_blocs/src/workspace/workspace_event.dart'; import 'package:catalyst_voices_blocs/src/workspace/workspace_state.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; final class WorkspaceBloc extends Bloc { - WorkspaceBloc() : super(const WorkspaceState()); + WorkspaceBloc() : super(const WorkspaceState()) { + on(_loadCurrentProposal); + } + + Future _loadCurrentProposal( + LoadCurrentProposalEvent event, + Emitter emit, + ) async { + const sections =
[ + WorkspaceSection( + id: 0, + name: 'Proposal solution', + steps: [ + RichTextStep( + id: 0, + sectionId: 0, + name: 'Problem perspective', + description: + "What is your perspective on the problem you're solving?", + ), + ], + ), + ]; + + emit(state.copyWith(sections: sections)); + } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart index 46a8ed91fc1..eae07f5b004 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart @@ -3,3 +3,10 @@ import 'package:equatable/equatable.dart'; sealed class WorkspaceEvent extends Equatable { const WorkspaceEvent(); } + +final class LoadCurrentProposalEvent extends WorkspaceEvent { + const LoadCurrentProposalEvent(); + + @override + List get props => []; +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart index bc70f87eccc..9bd6b58d626 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart @@ -1,8 +1,23 @@ +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:equatable/equatable.dart'; final class WorkspaceState extends Equatable { - const WorkspaceState(); + final List
sections; + + const WorkspaceState({ + this.sections = const [], + }); + + WorkspaceState copyWith({ + List
? sections, + }) { + return WorkspaceState( + sections: sections ?? this.sections, + ); + } @override - List get props => []; + List get props => [ + sections, + ]; } From 7f5bdece9dde62c70ea7915d25cf01660c66a4b1 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 10:54:33 +0100 Subject: [PATCH 08/16] fix: section step title wrapping --- .../voices/lib/widgets/rich_text/voices_rich_text.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/catalyst_voices/apps/voices/lib/widgets/rich_text/voices_rich_text.dart b/catalyst_voices/apps/voices/lib/widgets/rich_text/voices_rich_text.dart index 848796f5364..a8b4aeab9cb 100644 --- a/catalyst_voices/apps/voices/lib/widgets/rich_text/voices_rich_text.dart +++ b/catalyst_voices/apps/voices/lib/widgets/rich_text/voices_rich_text.dart @@ -511,11 +511,13 @@ class _TopBar extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Text( - title, - style: Theme.of(context).textTheme.titleMedium, + Expanded( + child: Text( + title, + style: Theme.of(context).textTheme.titleMedium, + ), ), - const Spacer(), + const SizedBox(width: 16), VoicesTextButton( onTap: onToggleEditMode, child: Text( From 9d93539bf98e77e19ccd34f8b1185edb8b8eba8a Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 13:00:51 +0100 Subject: [PATCH 09/16] feat: ProposalSection --- .../voices/lib/dependency/dependencies.dart | 12 ++- .../lib/pages/treasury/treasury_page.dart | 12 +-- .../lib/widgets/menu/voices_node_menu.dart | 6 +- .../navigation/sections_controller.dart | 14 ++-- .../lib/widgets/navigation/sections_menu.dart | 4 +- .../lib/src/workspace/workspace_bloc.dart | 53 ++++++++++---- .../test/proposals/proposals_cubit_test.dart | 15 +++- .../lib/src/catalyst_voices_models.dart | 1 + .../lib/src/markdown_string.dart | 2 +- .../lib/src/proposal/proposal.dart | 16 ++-- .../lib/src/proposal/proposal_builder.dart | 31 ++++---- .../lib/src/proposal/proposal_section.dart | 73 +++++++++++++++++++ .../lib/src/proposal/proposal_template.dart | 11 ++- .../lib/src/campaign/campaign_repository.dart | 30 +++++++- .../lib/src/proposal/proposal_repository.dart | 44 +++++++++-- .../lib/src/campaign/campaign_service.dart | 40 ++++++++++ .../lib/src/catalyst_voices_services.dart | 1 + .../src/navigation/sections_navigation.dart | 14 ++-- .../lib/examples/voices_menu_example.dart | 8 +- 19 files changed, 307 insertions(+), 80 deletions(-) create mode 100644 catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart create mode 100644 catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart diff --git a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart index 99218ad77e0..5d2280722cb 100644 --- a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart +++ b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart @@ -62,7 +62,11 @@ final class Dependencies extends DependencyProvider { ..registerLazySingleton( CampaignBuilderCubit.new, ) - ..registerFactory(WorkspaceBloc.new); + ..registerFactory(() { + return WorkspaceBloc( + get(), + ); + }); } void _registerRepositories() { @@ -109,5 +113,11 @@ final class Dependencies extends DependencyProvider { }, dispose: (service) => unawaited(service.dispose()), ); + registerLazySingleton(() { + return CampaignService( + campaignRepository: get(), + proposalRepository: get(), + ); + }); } } diff --git a/catalyst_voices/apps/voices/lib/pages/treasury/treasury_page.dart b/catalyst_voices/apps/voices/lib/pages/treasury/treasury_page.dart index e5250fb8910..e554eb3eec0 100644 --- a/catalyst_voices/apps/voices/lib/pages/treasury/treasury_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/treasury/treasury_page.dart @@ -8,16 +8,16 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; const sections = [ CampaignSetup( - id: 0, + id: '0', steps: [ DummyTopicStep( - id: 0, - sectionId: 0, + id: '0', + sectionId: '0', isEditable: false, ), - DummyTopicStep(id: 1, sectionId: 0), - DummyTopicStep(id: 2, sectionId: 0), - DummyTopicStep(id: 3, sectionId: 0), + DummyTopicStep(id: '1', sectionId: '0'), + DummyTopicStep(id: '2', sectionId: '0'), + DummyTopicStep(id: '3', sectionId: '0'), ], ), ]; diff --git a/catalyst_voices/apps/voices/lib/widgets/menu/voices_node_menu.dart b/catalyst_voices/apps/voices/lib/widgets/menu/voices_node_menu.dart index 8a365d2b104..9498840bb8b 100644 --- a/catalyst_voices/apps/voices/lib/widgets/menu/voices_node_menu.dart +++ b/catalyst_voices/apps/voices/lib/widgets/menu/voices_node_menu.dart @@ -6,7 +6,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; final class VoicesNodeMenuItem extends Equatable { - final int id; + final String id; final String label; final bool isEnabled; @@ -28,8 +28,8 @@ class VoicesNodeMenu extends StatelessWidget { final String name; final Widget? icon; final VoidCallback? onHeaderTap; - final int? selectedItemId; - final ValueChanged onItemTap; + final String? selectedItemId; + final ValueChanged onItemTap; final List items; final bool isExpandable; final bool isExpanded; diff --git a/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart b/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart index 68429977f4d..85f31b34c6a 100644 --- a/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart +++ b/catalyst_voices/apps/voices/lib/widgets/navigation/sections_controller.dart @@ -9,7 +9,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; final class SectionsControllerState extends Equatable { final List
sections; - final Set openedSections; + final Set openedSections; final SectionStepId? activeStepId; final Set editStepsIds; @@ -20,9 +20,9 @@ final class SectionsControllerState extends Equatable { this.editStepsIds = const {}, }); - int? get activeSectionId => activeStepId?.sectionId; + String? get activeSectionId => activeStepId?.sectionId; - int? get activeStep => activeStepId?.stepId; + String? get activeStep => activeStepId?.stepId; bool get allSegmentsClosed => openedSections.isEmpty; @@ -60,7 +60,7 @@ final class SectionsControllerState extends Equatable { SectionsControllerState copyWith({ List
? sections, - Set? openedSections, + Set? openedSections, Optional? activeStepId, Set? editStepsIds, }) { @@ -97,7 +97,7 @@ final class SectionsController extends ValueNotifier { _itemsScrollController = null; } - void toggleSection(int id) { + void toggleSection(String id) { final openedSections = {...value.openedSections}; final allSegmentsClosed = value.allSegmentsClosed; final shouldOpen = !openedSections.contains(id); @@ -143,7 +143,7 @@ final class SectionsController extends ValueNotifier { unawaited(_scrollToSectionStep(id)); } - void focusSection(int id) { + void focusSection(String id) { unawaited(_scrollToSection(id)); } @@ -173,7 +173,7 @@ final class SectionsController extends ValueNotifier { super.dispose(); } - Future _scrollToSection(int id) async { + Future _scrollToSection(String id) async { final index = value.listItems.indexWhere((e) => e is Section && e.id == id); if (index == -1) { return; diff --git a/catalyst_voices/apps/voices/lib/widgets/navigation/sections_menu.dart b/catalyst_voices/apps/voices/lib/widgets/navigation/sections_menu.dart index d0435ea1206..e8d23e6bb6f 100644 --- a/catalyst_voices/apps/voices/lib/widgets/navigation/sections_menu.dart +++ b/catalyst_voices/apps/voices/lib/widgets/navigation/sections_menu.dart @@ -31,9 +31,9 @@ class SectionsMenuListener extends StatelessWidget { class SectionsMenu extends StatelessWidget { final List
sections; - final Set openedSections; + final Set openedSections; final SectionStepId? selectedStep; - final ValueChanged onSectionTap; + final ValueChanged onSectionTap; final ValueChanged onStepSelected; const SectionsMenu({ diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart index 6b3104de65c..ab9af9ab430 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -1,10 +1,18 @@ import 'package:catalyst_voices_blocs/src/workspace/workspace_event.dart'; import 'package:catalyst_voices_blocs/src/workspace/workspace_state.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_services/catalyst_voices_services.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; final class WorkspaceBloc extends Bloc { - WorkspaceBloc() : super(const WorkspaceState()) { + final CampaignService _campaignService; + + var _builder = const ProposalBuilder(sections: []); + + WorkspaceBloc( + this._campaignService, + ) : super(const WorkspaceState()) { on(_loadCurrentProposal); } @@ -12,21 +20,34 @@ final class WorkspaceBloc extends Bloc { LoadCurrentProposalEvent event, Emitter emit, ) async { - const sections =
[ - WorkspaceSection( - id: 0, - name: 'Proposal solution', - steps: [ - RichTextStep( - id: 0, - sectionId: 0, - name: 'Problem perspective', - description: - "What is your perspective on the problem you're solving?", - ), - ], - ), - ]; + final activeCampaign = await _campaignService.getActiveCampaign(); + if (activeCampaign == null) { + emit(state.copyWith(sections: [])); + return; + } + + final template = activeCampaign.proposalTemplate; + _builder = ProposalBuilder(sections: List.from(template.sections)); + + final sections = template.sections.map( + (section) { + return WorkspaceSection( + id: section.id, + name: section.name, + steps: section.steps.map( + (step) { + return RichTextStep( + id: step.id, + sectionId: section.id, + name: step.name, + description: step.description, + initialData: step.answer, + ); + }, + ).toList(), + ); + }, + ).toList(); emit(state.copyWith(sections: sections)); } diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart index 42d48a846ba..018442a375b 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart @@ -19,8 +19,19 @@ void main() { publish: ProposalPublish.draft, access: ProposalAccess.private, commentsCount: 0, - completedSegments: 1, - totalSegments: 3, + sections: List.generate(3, (index) { + return ProposalSection( + id: 'f14/0_$index', + name: 'Section_$index', + steps: [ + ProposalSectionStep( + id: 'f14/0_${index}_1', + name: 'Topic 1', + answer: index < 1 ? MarkdownString('Ans') : null, + ), + ], + ); + }), ); final pendingProposal = PendingProposal.fromProposal( diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart index 0b120032658..bdbd300dfb6 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -14,6 +14,7 @@ export 'markdown_string.dart'; export 'optional.dart'; export 'proposal/proposal.dart'; export 'proposal/proposal_builder.dart'; +export 'proposal/proposal_section.dart'; export 'proposal/proposal_template.dart'; export 'registration/registration.dart'; export 'seed_phrase.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart index 65525186eea..0d697c6a531 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart @@ -1 +1 @@ -extension type const MarkdownString(String data) {} +extension type const MarkdownString(String data) implements Object {} diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal.dart index d7ea28d2cba..68995d67524 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal.dart @@ -1,4 +1,5 @@ import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:catalyst_voices_models/src/proposal/proposal_section.dart'; import 'package:equatable/equatable.dart'; // Note. This enum may be deleted later. Its here for backwards compatibility. @@ -18,14 +19,13 @@ final class Proposal extends Equatable { final ProposalStatus status; final ProposalPublish publish; final ProposalAccess access; + final List sections; // This may be a reference to class final String category; // Those may be getters. final int commentsCount; - final int completedSegments; - final int totalSegments; const Proposal({ required this.id, @@ -37,12 +37,17 @@ final class Proposal extends Equatable { required this.status, required this.publish, required this.access, + required this.sections, required this.category, required this.commentsCount, - required this.completedSegments, - required this.totalSegments, }); + int get totalSegments => sections.length; + + int get completedSegments { + return sections.where((element) => element.isCompleted).length; + } + @override List get props => [ id, @@ -53,9 +58,8 @@ final class Proposal extends Equatable { fundsRequested.value, publish, access, + sections, category, commentsCount, - completedSegments, - totalSegments, ]; } diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart index 250bad0247d..b48142930f2 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart @@ -1,26 +1,25 @@ -import 'package:catalyst_voices_models/src/proposal/proposal.dart'; -import 'package:catalyst_voices_models/src/proposal/proposal_template.dart'; +import 'package:catalyst_voices_models/src/proposal/proposal_section.dart'; import 'package:equatable/equatable.dart'; final class ProposalBuilder extends Equatable { - const ProposalBuilder(); + final List sections; - // ignore: avoid_unused_constructor_parameters - const ProposalBuilder.fromTemplate(ProposalTemplate template) : this(); + const ProposalBuilder({ + required this.sections, + }); - // ignore: avoid_unused_constructor_parameters - const ProposalBuilder.fromProposal(Proposal proposal) : this(); + bool get isValid => sections.every((element) => element.isCompleted); - bool get isValid => false; - - Proposal build() { - throw UnimplementedError(); - } - - ProposalBuilder copyWith() { - return const ProposalBuilder(); + ProposalBuilder copyWith({ + List? sections, + }) { + return ProposalBuilder( + sections: sections ?? this.sections, + ); } @override - List get props => []; + List get props => [ + sections, + ]; } diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart new file mode 100644 index 00000000000..d9835709d44 --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart @@ -0,0 +1,73 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:equatable/equatable.dart'; + +final class ProposalSection extends Equatable { + final String id; + final String name; + final List steps; + + const ProposalSection({ + required this.id, + required this.name, + required this.steps, + }); + + bool get isCompleted => steps.every((element) => element.hasAnswer); + + ProposalSection copyWith({ + String? id, + String? name, + List? steps, + }) { + return ProposalSection( + id: id ?? this.id, + name: name ?? this.name, + steps: steps ?? this.steps, + ); + } + + @override + List get props => [ + id, + name, + steps, + ]; +} + +final class ProposalSectionStep extends Equatable { + final String id; + final String name; + final String? description; + final MarkdownString? answer; + + const ProposalSectionStep({ + required this.id, + required this.name, + this.description, + this.answer, + }); + + bool get hasAnswer => answer != null; + + ProposalSectionStep copyWith({ + String? id, + String? name, + Optional? description, + Optional? answer, + }) { + return ProposalSectionStep( + id: id ?? this.id, + name: name ?? this.name, + description: description.dataOr(this.description), + answer: answer.dataOr(this.answer), + ); + } + + @override + List get props => [ + id, + name, + description, + answer, + ]; +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_template.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_template.dart index f7ffb480043..afd4aa586be 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_template.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_template.dart @@ -1,8 +1,15 @@ +import 'package:catalyst_voices_models/src/proposal/proposal_section.dart'; import 'package:equatable/equatable.dart'; final class ProposalTemplate extends Equatable { - const ProposalTemplate(); + final List sections; + + const ProposalTemplate({ + required this.sections, + }); @override - List get props => []; + List get props => [ + sections, + ]; } diff --git a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart index 0caaf3c2461..26ddf618b67 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart @@ -31,7 +31,35 @@ final class CampaignRepository { endDate: now.add(const Duration(days: 92)), proposalsCount: 0, sections: sections, - proposalTemplate: const ProposalTemplate(), + proposalTemplate: ProposalTemplate( + sections: [ + ProposalSection( + id: '${id}_1', + name: 'Proposal Setup', + steps: [ + ProposalSectionStep(id: '${id}_1_1', name: 'Title'), + ], + ), + ProposalSection( + id: '${id}_2', + name: 'Proposal Summary', + steps: [ + ProposalSectionStep(id: '${id}_2_1', name: 'Problem Statement'), + ProposalSectionStep(id: '${id}_2_2', name: 'Solution Statement'), + ], + ), + ProposalSection( + id: '${id}_3', + name: 'Proposal Setup', + steps: [ + ProposalSectionStep(id: '${id}_3_1', name: 'Topic 1'), + ProposalSectionStep(id: '${id}_3_2', name: 'Topic 2'), + ProposalSectionStep(id: '${id}_3_3', name: 'Topic 3'), + ProposalSectionStep(id: '${id}_3_4', name: 'Topic 4'), + ], + ), + ], + ), ); } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart index 92d96ff6644..0b8c87152de 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart @@ -36,8 +36,18 @@ final _proposals = [ access: ProposalAccess.private, commentsCount: 0, description: _proposalDescription, - completedSegments: 0, - totalSegments: 13, + sections: List.generate(13, (index) { + return ProposalSection( + id: 'f14/0_$index', + name: 'Section_$index', + steps: [ + ProposalSectionStep( + id: 'f14/0_${index}_1', + name: 'Topic 1', + ), + ], + ); + }), ), Proposal( id: 'f14/1', @@ -50,8 +60,19 @@ final _proposals = [ access: ProposalAccess.private, commentsCount: 0, description: _proposalDescription, - completedSegments: 7, - totalSegments: 13, + sections: List.generate(13, (index) { + return ProposalSection( + id: 'f14/0_$index', + name: 'Section_$index', + steps: [ + ProposalSectionStep( + id: 'f14/0_${index}_1', + name: 'Topic 1', + answer: index < 7 ? const MarkdownString('Ans') : null, + ), + ], + ); + }), ), Proposal( id: 'f14/2', @@ -64,7 +85,18 @@ final _proposals = [ access: ProposalAccess.private, commentsCount: 0, description: _proposalDescription, - completedSegments: 13, - totalSegments: 13, + sections: List.generate(13, (index) { + return ProposalSection( + id: 'f14/0_$index', + name: 'Section_$index', + steps: [ + ProposalSectionStep( + id: 'f14/0_${index}_1', + name: 'Topic 1', + answer: const MarkdownString('Ans'), + ), + ], + ); + }), ), ]; diff --git a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart new file mode 100644 index 00000000000..7d8e269ed2c --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart @@ -0,0 +1,40 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_repositories/catalyst_voices_repositories.dart'; + +abstract interface class CampaignService { + factory CampaignService({ + required CampaignRepository campaignRepository, + required ProposalRepository proposalRepository, + }) { + return CampaignServiceImpl( + campaignRepository, + proposalRepository, + ); + } + + Future isAnyCampaignActive(); + + Future getActiveCampaign(); +} + +final class CampaignServiceImpl implements CampaignService { + final CampaignRepository _campaignRepository; + final ProposalRepository _proposalRepository; + + const CampaignServiceImpl( + this._campaignRepository, + this._proposalRepository, + ); + + @override + Future isAnyCampaignActive() async { + return true; + } + + @override + Future getActiveCampaign() async { + final campaign = await _campaignRepository.getCampaign(id: 'F14'); + + return campaign; + } +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart index 2032618e3b0..d478ef80cc4 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart @@ -1,3 +1,4 @@ +export 'campaign/campaign_service.dart' show CampaignService; export 'crypto/key_derivation.dart'; export 'downloader/downloader.dart'; export 'keychain/keychain.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart index 3edb249e211..0eba0c4bc75 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/navigation/sections_navigation.dart @@ -3,10 +3,10 @@ import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; -typedef SectionStepId = ({int sectionId, int stepId}); +typedef SectionStepId = ({String sectionId, String stepId}); abstract interface class Section implements SectionsListViewItem { - int get id; + String get id; SvgGenImage get icon; @@ -16,9 +16,9 @@ abstract interface class Section implements SectionsListViewItem { } abstract interface class SectionStep implements SectionsListViewItem { - int get id; + String get id; - int get sectionId; + String get sectionId; SectionStepId get sectionStepId; @@ -32,7 +32,7 @@ abstract interface class SectionStep implements SectionsListViewItem { abstract base class BaseSection extends Equatable implements Section { @override - final int id; + final String id; @override final List steps; @@ -56,9 +56,9 @@ abstract base class BaseSection extends Equatable abstract base class BaseSectionStep extends Equatable implements SectionStep { @override - final int id; + final String id; @override - final int sectionId; + final String sectionId; @override final bool isEnabled; @override diff --git a/catalyst_voices/utilities/uikit_example/lib/examples/voices_menu_example.dart b/catalyst_voices/utilities/uikit_example/lib/examples/voices_menu_example.dart index b85aa136885..a042ed49a7f 100644 --- a/catalyst_voices/utilities/uikit_example/lib/examples/voices_menu_example.dart +++ b/catalyst_voices/utilities/uikit_example/lib/examples/voices_menu_example.dart @@ -13,7 +13,7 @@ class VoicesMenuExample extends StatefulWidget { } class _VoicesMenuExampleState extends State { - int? _selectedItemId; + String? _selectedItemId; @override void dispose() { @@ -45,9 +45,9 @@ class _VoicesMenuExampleState extends State { }, selectedItemId: _selectedItemId, items: const [ - VoicesNodeMenuItem(id: 0, label: 'Start'), - VoicesNodeMenuItem(id: 1, label: 'Vote'), - VoicesNodeMenuItem(id: 2, label: 'Results'), + VoicesNodeMenuItem(id: '0', label: 'Start'), + VoicesNodeMenuItem(id: '1', label: 'Vote'), + VoicesNodeMenuItem(id: '2', label: 'Results'), ], ), ].separatedBy(const SizedBox(height: 12)).toList(), From 6babd42c6686077caf10d0991a1863b41bedfa21 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 13:59:12 +0100 Subject: [PATCH 10/16] feat: saving proposal step answer --- .../workspace/workspace_rich_text_step.dart | 17 +++++++++++++ .../lib/src/workspace/workspace_bloc.dart | 18 +++++++++++-- .../lib/src/workspace/workspace_event.dart | 15 +++++++++++ .../lib/src/catalyst_voices_models.dart | 1 - .../lib/src/proposal/proposal_builder.dart | 25 ------------------- 5 files changed, 48 insertions(+), 28 deletions(-) delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart index 043208cfe52..8fc55ca3397 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart @@ -2,10 +2,12 @@ import 'package:catalyst_voices/common/codecs/markdown_codec.dart'; import 'package:catalyst_voices/widgets/navigation/section_step_state_builder.dart'; import 'package:catalyst_voices/widgets/rich_text/voices_rich_text.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_quill/flutter_quill.dart'; class WorkspaceRichTextStep extends StatefulWidget { @@ -66,10 +68,25 @@ class _WorkspaceRichTextStepState extends State { charsLimit: widget.step.charsLimit, canEditDocumentGetter: _canEditDocument, onEditBlocked: _showEditBlockedRationale, + onSaved: _saveDocument, ), ); } + void _saveDocument(Document document) { + final delta = document.toDelta(); + final markdownString = markdown.decode(delta); + + final sectionStepId = widget.step.sectionStepId; + + final event = UpdateSectionStepAnswer( + id: sectionStepId, + data: markdownString.data.isNotEmpty ? markdownString : null, + ); + + context.read().add(event); + } + bool _canEditDocument(Document document) { final sectionsController = SectionsControllerScope.of(context); diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart index ab9af9ab430..5c2ac2abea0 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -8,18 +8,21 @@ import 'package:flutter_bloc/flutter_bloc.dart'; final class WorkspaceBloc extends Bloc { final CampaignService _campaignService; - var _builder = const ProposalBuilder(sections: []); + final _answers = {}; WorkspaceBloc( this._campaignService, ) : super(const WorkspaceState()) { on(_loadCurrentProposal); + on(_updateStepAnswer); } Future _loadCurrentProposal( LoadCurrentProposalEvent event, Emitter emit, ) async { + _answers.clear(); + final activeCampaign = await _campaignService.getActiveCampaign(); if (activeCampaign == null) { emit(state.copyWith(sections: [])); @@ -27,7 +30,6 @@ final class WorkspaceBloc extends Bloc { } final template = activeCampaign.proposalTemplate; - _builder = ProposalBuilder(sections: List.from(template.sections)); final sections = template.sections.map( (section) { @@ -51,4 +53,16 @@ final class WorkspaceBloc extends Bloc { emit(state.copyWith(sections: sections)); } + + void _updateStepAnswer( + UpdateSectionStepAnswer event, + Emitter emit, + ) { + final answer = event.data; + if (answer != null) { + _answers[event.id] = answer; + } else { + _answers.remove(event.id); + } + } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart index eae07f5b004..db92772cc01 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart @@ -1,3 +1,5 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:equatable/equatable.dart'; sealed class WorkspaceEvent extends Equatable { @@ -10,3 +12,16 @@ final class LoadCurrentProposalEvent extends WorkspaceEvent { @override List get props => []; } + +final class UpdateSectionStepAnswer extends WorkspaceEvent { + final SectionStepId id; + final MarkdownString? data; + + const UpdateSectionStepAnswer({ + required this.id, + this.data, + }); + + @override + List get props => [id, data]; +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart index bdbd300dfb6..4c24c43adfa 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -13,7 +13,6 @@ export 'file/voices_file.dart'; export 'markdown_string.dart'; export 'optional.dart'; export 'proposal/proposal.dart'; -export 'proposal/proposal_builder.dart'; export 'proposal/proposal_section.dart'; export 'proposal/proposal_template.dart'; export 'registration/registration.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart deleted file mode 100644 index b48142930f2..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_builder.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:catalyst_voices_models/src/proposal/proposal_section.dart'; -import 'package:equatable/equatable.dart'; - -final class ProposalBuilder extends Equatable { - final List sections; - - const ProposalBuilder({ - required this.sections, - }); - - bool get isValid => sections.every((element) => element.isCompleted); - - ProposalBuilder copyWith({ - List? sections, - }) { - return ProposalBuilder( - sections: sections ?? this.sections, - ); - } - - @override - List get props => [ - sections, - ]; -} From 503acd5071186203476fd3dc6b4a31c2cef50bc2 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Wed, 4 Dec 2024 14:02:45 +0100 Subject: [PATCH 11/16] refactor: move WorkspaceBloc to app.dart --- .../apps/voices/lib/app/view/app.dart | 3 + .../lib/pages/spaces/spaces_shell_page.dart | 103 ++++++------------ 2 files changed, 34 insertions(+), 72 deletions(-) diff --git a/catalyst_voices/apps/voices/lib/app/view/app.dart b/catalyst_voices/apps/voices/lib/app/view/app.dart index a1887ea92fb..0c7a5b13530 100644 --- a/catalyst_voices/apps/voices/lib/app/view/app.dart +++ b/catalyst_voices/apps/voices/lib/app/view/app.dart @@ -53,6 +53,9 @@ class _AppState extends State { BlocProvider( create: (_) => Dependencies.instance.get(), ), + BlocProvider( + create: (context) => Dependencies.instance.get(), + ), ]; } } diff --git a/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart b/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart index d173acd49d7..c4810f7a3c7 100644 --- a/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/spaces/spaces_shell_page.dart @@ -1,5 +1,4 @@ import 'package:catalyst_voices/common/ext/ext.dart'; -import 'package:catalyst_voices/dependency/dependencies.dart'; import 'package:catalyst_voices/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart'; import 'package:catalyst_voices/pages/campaign/details/widgets/campaign_management.dart'; import 'package:catalyst_voices/pages/spaces/appbar/spaces_theme_mode_switch.dart'; @@ -58,36 +57,38 @@ class _SpacesShellPageState extends State { final isVisitor = sessionBloc.state is VisitorSessionState; final isUnlocked = sessionBloc.state is ActiveAccountSessionState; - return _Shortcuts( - toggleCampaignAdminTools: _toggleCampaignAdminTools, - child: _BlocsProviders( - child: Stack( - children: [ - Scaffold( - appBar: VoicesAppBar( - leading: isVisitor ? null : const DrawerToggleButton(), - automaticallyImplyLeading: false, - actions: _getActions(widget.space), - ), - drawer: isVisitor - ? null - : SpacesDrawer( - space: widget.space, - spacesShortcutsActivators: - SpacesShellPage._spacesShortcutsActivators, - isUnlocked: isUnlocked, - ), - body: widget.child, + return CallbackShortcuts( + bindings: { + for (final entry in SpacesShellPage._spacesShortcutsActivators.entries) + entry.value: () => entry.key.go(context), + CampaignAdminToolsDialog.shortcut: _toggleCampaignAdminTools, + }, + child: Stack( + children: [ + Scaffold( + appBar: VoicesAppBar( + leading: isVisitor ? null : const DrawerToggleButton(), + automaticallyImplyLeading: false, + actions: _getActions(widget.space), ), - if (_showAdminTools) - DraggableCampaignAdminToolsDialog( - dialogKey: _adminToolsKey, - selectedSpace: widget.space, - onSpaceSelected: (space) => space.go(context), - onClose: _closeCampaignAdminTools, - ), - ], - ), + drawer: isVisitor + ? null + : SpacesDrawer( + space: widget.space, + spacesShortcutsActivators: + SpacesShellPage._spacesShortcutsActivators, + isUnlocked: isUnlocked, + ), + body: widget.child, + ), + if (_showAdminTools) + DraggableCampaignAdminToolsDialog( + dialogKey: _adminToolsKey, + selectedSpace: widget.space, + onSpaceSelected: (space) => space.go(context), + onClose: _closeCampaignAdminTools, + ), + ], ), ); } @@ -119,45 +120,3 @@ class _SpacesShellPageState extends State { }); } } - -class _Shortcuts extends StatelessWidget { - final Widget child; - final VoidCallback toggleCampaignAdminTools; - - const _Shortcuts({ - required this.child, - required this.toggleCampaignAdminTools, - }); - - @override - Widget build(BuildContext context) { - return CallbackShortcuts( - bindings: { - for (final entry in SpacesShellPage._spacesShortcutsActivators.entries) - entry.value: () => entry.key.go(context), - CampaignAdminToolsDialog.shortcut: toggleCampaignAdminTools, - }, - child: child, - ); - } -} - -class _BlocsProviders extends StatelessWidget { - final Widget child; - - const _BlocsProviders({ - required this.child, - }); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => Dependencies.instance.get(), - ), - ], - child: child, - ); - } -} From 4585d6abf682f5b1fe71fc614586aeb32ff455c5 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Thu, 5 Dec 2024 09:34:43 +0100 Subject: [PATCH 12/16] chore: guidances --- .../voices/lib/common/ext/guidance_ext.dart | 2 +- .../workspace/workspace_guidance_view.dart | 2 +- .../lib/pages/workspace/workspace_page.dart | 16 +++- .../workspace/workspace_rich_text_step.dart | 2 +- .../workspace/workspace_setup_panel.dart | 33 ++++---- .../lib/widgets/cards/guidance_card.dart | 14 +++- .../lib/src/workspace/workspace_bloc.dart | 57 ++++++++++++-- .../lib/src/workspace/workspace_event.dart | 13 +++- .../lib/src/workspace/workspace_state.dart | 24 ++++++ .../lib/src/catalyst_voices_models.dart | 1 + .../lib/src/proposal}/guidance.dart | 26 +++++-- .../lib/src/proposal/proposal_section.dart | 5 ++ .../lib/src/campaign/campaign_repository.dart | 76 +++++++++++++++++-- .../lib/src/catalyst_voices_view_models.dart | 2 - .../src/proposal/guidance/guidance_type.dart | 11 --- 15 files changed, 230 insertions(+), 54 deletions(-) rename catalyst_voices/packages/internal/{catalyst_voices_view_models/lib/src/proposal/guidance => catalyst_voices_models/lib/src/proposal}/guidance.dart (71%) delete mode 100644 catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance_type.dart diff --git a/catalyst_voices/apps/voices/lib/common/ext/guidance_ext.dart b/catalyst_voices/apps/voices/lib/common/ext/guidance_ext.dart index e4ab1f54554..1a59ca68baa 100644 --- a/catalyst_voices/apps/voices/lib/common/ext/guidance_ext.dart +++ b/catalyst_voices/apps/voices/lib/common/ext/guidance_ext.dart @@ -1,6 +1,6 @@ import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_localization/generated/catalyst_voices_localizations.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; extension GuidanceExt on GuidanceType { String localizedType(VoicesLocalizations localizations) => switch (this) { diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_guidance_view.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_guidance_view.dart index 8538e239967..bade73f0d3e 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_guidance_view.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_guidance_view.dart @@ -2,7 +2,7 @@ import 'package:catalyst_voices/common/ext/guidance_ext.dart'; import 'package:catalyst_voices/widgets/cards/guidance_card.dart'; import 'package:catalyst_voices/widgets/dropdown/voices_dropdown.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:flutter/material.dart'; class GuidanceView extends StatefulWidget { diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart index dcac3447be3..a3b1837803f 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart @@ -25,6 +25,7 @@ class _WorkspacePageState extends State { late final SectionsController _sectionsController; late final ItemScrollController _bodyItemScrollController; + SectionStepId? _activeStepId; StreamSubscription>? _sectionsSub; @override @@ -37,7 +38,9 @@ class _WorkspacePageState extends State { _sectionsController = SectionsController(); _bodyItemScrollController = ItemScrollController(); - _sectionsController.attachItemsScrollController(_bodyItemScrollController); + _sectionsController + ..addListener(_handleSectionsControllerChange) + ..attachItemsScrollController(_bodyItemScrollController); _sectionsSub = bloc.stream .map((event) => event.sections) @@ -77,4 +80,15 @@ class _WorkspacePageState extends State { _sectionsController.value = newState; } + + void _handleSectionsControllerChange() { + final activeStepId = _sectionsController.value.activeStepId; + + if (_activeStepId != activeStepId) { + _activeStepId = activeStepId; + + final event = ActiveStepChangedEvent(activeStepId); + context.read().add(event); + } + } } diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart index 8fc55ca3397..2f619948668 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart @@ -79,7 +79,7 @@ class _WorkspaceRichTextStepState extends State { final sectionStepId = widget.step.sectionStepId; - final event = UpdateSectionStepAnswer( + final event = UpdateStepAnswerEvent( id: sectionStepId, data: markdownString.data.isNotEmpty ? markdownString : null, ); diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart index 172fa1ee340..52129eb8480 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_setup_panel.dart @@ -1,8 +1,11 @@ +import 'package:catalyst_voices/pages/workspace/workspace_guidance_view.dart'; import 'package:catalyst_voices/widgets/cards/comment_card.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class WorkspaceSetupPanel extends StatelessWidget { const WorkspaceSetupPanel({super.key}); @@ -16,7 +19,7 @@ class WorkspaceSetupPanel extends StatelessWidget { tabs: [ SpaceSidePanelTab( name: 'Guidance', - body: const _SetupSectionListener(), + body: const _GuidanceSelector(), ), SpaceSidePanelTab( name: 'Comments', @@ -38,22 +41,22 @@ class WorkspaceSetupPanel extends StatelessWidget { } } -// TODO(damian): Read bloc state -class _SetupSectionListener extends StatelessWidget { - const _SetupSectionListener(); +class _GuidanceSelector extends StatelessWidget { + const _GuidanceSelector(); @override Widget build(BuildContext context) { - return Text(context.l10n.noGuidanceForThisSection); - // final activeStepId = value.activeStepId; - // final activeStepGuidances = value.activeStepGuidances; - // - // if (activeStepId == null) { - // return Text(context.l10n.selectASection); - // } else if (activeStepGuidances == null || activeStepGuidances.isEmpty) { - // return Text(context.l10n.noGuidanceForThisSection); - // } else { - // return GuidanceView(activeStepGuidances); - // } + return BlocSelector( + selector: (state) => state.guidance, + builder: (context, state) { + if (state.isNoneSelected) { + return Text(context.l10n.selectASection); + } else if (state.showEmptyState) { + return Text(context.l10n.noGuidanceForThisSection); + } else { + return GuidanceView(state.guidances); + } + }, + ); } } diff --git a/catalyst_voices/apps/voices/lib/widgets/cards/guidance_card.dart b/catalyst_voices/apps/voices/lib/widgets/cards/guidance_card.dart index 17e2aae73e0..3cf6253019d 100644 --- a/catalyst_voices/apps/voices/lib/widgets/cards/guidance_card.dart +++ b/catalyst_voices/apps/voices/lib/widgets/cards/guidance_card.dart @@ -2,7 +2,7 @@ import 'package:catalyst_voices/common/ext/guidance_ext.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:catalyst_voices_view_models/catalyst_voices_view_models.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:flutter/material.dart'; class GuidanceCard extends StatelessWidget { @@ -57,6 +57,14 @@ class GuidanceCard extends StatelessWidget { ); } - String _buildTypeTitle(BuildContext context) => - '${guidance.type.localizedType(context.l10n)} ${guidance.weightText}'; + String _buildTypeTitle(BuildContext context) { + final weight = guidance.weight; + final localizedType = guidance.type.localizedType(context.l10n); + + if (weight == null) { + return localizedType; + } + + return '$localizedType $weight'; + } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart index 5c2ac2abea0..d1230f03a57 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -9,12 +9,16 @@ final class WorkspaceBloc extends Bloc { final CampaignService _campaignService; final _answers = {}; + final _guidances = >{}; + + SectionStepId? _activeStepId; WorkspaceBloc( this._campaignService, ) : super(const WorkspaceState()) { on(_loadCurrentProposal); - on(_updateStepAnswer); + on(_updateStepAnswer); + on(_handleActiveStepEvent); } Future _loadCurrentProposal( @@ -22,10 +26,16 @@ final class WorkspaceBloc extends Bloc { Emitter emit, ) async { _answers.clear(); + _guidances.clear(); final activeCampaign = await _campaignService.getActiveCampaign(); if (activeCampaign == null) { - emit(state.copyWith(sections: [])); + emit( + state.copyWith( + sections: [], + guidance: const WorkspaceGuidance(isNoneSelected: true), + ), + ); return; } @@ -38,12 +48,14 @@ final class WorkspaceBloc extends Bloc { name: section.name, steps: section.steps.map( (step) { + final id = (sectionId: section.id, stepId: step.id); + return RichTextStep( id: step.id, sectionId: section.id, name: step.name, description: step.description, - initialData: step.answer, + initialData: _answers[id] ?? step.answer, ); }, ).toList(), @@ -51,11 +63,30 @@ final class WorkspaceBloc extends Bloc { }, ).toList(); - emit(state.copyWith(sections: sections)); + for (final section in template.sections) { + for (final step in section.steps) { + final id = (sectionId: section.id, stepId: step.id); + _guidances[id] = step.guidances; + } + } + + final activeStepId = _activeStepId; + final guidances = _guidances[activeStepId] ?? []; + final guidance = WorkspaceGuidance( + isNoneSelected: activeStepId == null, + guidances: guidances, + ); + + emit( + state.copyWith( + sections: sections, + guidance: guidance, + ), + ); } void _updateStepAnswer( - UpdateSectionStepAnswer event, + UpdateStepAnswerEvent event, Emitter emit, ) { final answer = event.data; @@ -65,4 +96,20 @@ final class WorkspaceBloc extends Bloc { _answers.remove(event.id); } } + + void _handleActiveStepEvent( + ActiveStepChangedEvent event, + Emitter emit, + ) { + _activeStepId = event.id; + + final activeStepId = _activeStepId; + final guidances = _guidances[activeStepId] ?? []; + final guidance = WorkspaceGuidance( + isNoneSelected: activeStepId == null, + guidances: guidances, + ); + + emit(state.copyWith(guidance: guidance)); + } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart index db92772cc01..fedafb51793 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart @@ -13,11 +13,11 @@ final class LoadCurrentProposalEvent extends WorkspaceEvent { List get props => []; } -final class UpdateSectionStepAnswer extends WorkspaceEvent { +final class UpdateStepAnswerEvent extends WorkspaceEvent { final SectionStepId id; final MarkdownString? data; - const UpdateSectionStepAnswer({ + const UpdateStepAnswerEvent({ required this.id, this.data, }); @@ -25,3 +25,12 @@ final class UpdateSectionStepAnswer extends WorkspaceEvent { @override List get props => [id, data]; } + +final class ActiveStepChangedEvent extends WorkspaceEvent { + final SectionStepId? id; + + const ActiveStepChangedEvent(this.id); + + @override + List get props => [id]; +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart index 9bd6b58d626..1e9429f486f 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_state.dart @@ -1,23 +1,47 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart'; import 'package:equatable/equatable.dart'; final class WorkspaceState extends Equatable { final List
sections; + final WorkspaceGuidance guidance; const WorkspaceState({ this.sections = const [], + this.guidance = const WorkspaceGuidance(), }); WorkspaceState copyWith({ List
? sections, + WorkspaceGuidance? guidance, }) { return WorkspaceState( sections: sections ?? this.sections, + guidance: guidance ?? this.guidance, ); } @override List get props => [ sections, + guidance, + ]; +} + +final class WorkspaceGuidance extends Equatable { + final bool isNoneSelected; + final List guidances; + + const WorkspaceGuidance({ + this.isNoneSelected = false, + this.guidances = const [], + }); + + bool get showEmptyState => !isNoneSelected && guidances.isEmpty; + + @override + List get props => [ + isNoneSelected, + guidances, ]; } diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart index 4c24c43adfa..dd1b7b607a6 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -12,6 +12,7 @@ export 'errors/errors.dart'; export 'file/voices_file.dart'; export 'markdown_string.dart'; export 'optional.dart'; +export 'proposal/guidance.dart'; export 'proposal/proposal.dart'; export 'proposal/proposal_section.dart'; export 'proposal/proposal_template.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/guidance.dart similarity index 71% rename from catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance.dart rename to catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/guidance.dart index 7cf394c461f..f51cc667d37 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/guidance.dart @@ -1,22 +1,36 @@ -import 'package:catalyst_voices_view_models/src/proposal/guidance/guidance_type.dart'; +import 'dart:core'; + import 'package:equatable/equatable.dart'; +enum GuidanceType { + mandatory(priority: 0), + education(priority: 1), + tips(priority: 2); + + final int priority; + + const GuidanceType({ + required this.priority, + }); +} + final class Guidance extends Equatable implements Comparable { + final String id; final String title; final String description; final GuidanceType type; - final int? weight; // This represents how important the guidance is in - //specific [GuidanceType]. + + /// This represents how important the guidance is in specific [GuidanceType]. + final int? weight; const Guidance({ + required this.id, required this.title, required this.description, required this.type, this.weight, }); - String get weightText => weight?.toString() ?? ''; - @override int compareTo(Guidance other) { final typeComparison = type.priority.compareTo(other.type.priority); @@ -31,9 +45,11 @@ final class Guidance extends Equatable implements Comparable { @override List get props => [ + id, title, description, type, + weight, ]; } diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart index d9835709d44..ca42cec986a 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart @@ -38,12 +38,14 @@ final class ProposalSectionStep extends Equatable { final String id; final String name; final String? description; + final List guidances; final MarkdownString? answer; const ProposalSectionStep({ required this.id, required this.name, this.description, + this.guidances = const [], this.answer, }); @@ -53,12 +55,14 @@ final class ProposalSectionStep extends Equatable { String? id, String? name, Optional? description, + List? guidances, Optional? answer, }) { return ProposalSectionStep( id: id ?? this.id, name: name ?? this.name, description: description.dataOr(this.description), + guidances: guidances ?? this.guidances, answer: answer.dataOr(this.answer), ); } @@ -68,6 +72,7 @@ final class ProposalSectionStep extends Equatable { id, name, description, + guidances, answer, ]; } diff --git a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart index 26ddf618b67..8f6a2f7fab9 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart @@ -37,25 +37,53 @@ final class CampaignRepository { id: '${id}_1', name: 'Proposal Setup', steps: [ - ProposalSectionStep(id: '${id}_1_1', name: 'Title'), + ProposalSectionStep( + id: '${id}_1_1', + name: 'Title', + guidances: List.from(_mockGuidance), + ), ], ), ProposalSection( id: '${id}_2', name: 'Proposal Summary', steps: [ - ProposalSectionStep(id: '${id}_2_1', name: 'Problem Statement'), - ProposalSectionStep(id: '${id}_2_2', name: 'Solution Statement'), + ProposalSectionStep( + id: '${id}_2_1', + name: 'Problem Statement', + guidances: List.from(_mockGuidance), + ), + ProposalSectionStep( + id: '${id}_2_2', + name: 'Solution Statement', + guidances: List.from(_mockGuidance), + ), ], ), ProposalSection( id: '${id}_3', name: 'Proposal Setup', steps: [ - ProposalSectionStep(id: '${id}_3_1', name: 'Topic 1'), - ProposalSectionStep(id: '${id}_3_2', name: 'Topic 2'), - ProposalSectionStep(id: '${id}_3_3', name: 'Topic 3'), - ProposalSectionStep(id: '${id}_3_4', name: 'Topic 4'), + ProposalSectionStep( + id: '${id}_3_1', + name: 'Topic 1', + guidances: List.from(_mockGuidance), + ), + ProposalSectionStep( + id: '${id}_3_2', + name: 'Topic 2', + guidances: List.from(_mockGuidance), + ), + ProposalSectionStep( + id: '${id}_3_3', + name: 'Topic 3', + guidances: List.from(_mockGuidance), + ), + ProposalSectionStep( + id: '${id}_3_4', + name: 'Topic 4', + guidances: List.from(_mockGuidance), + ), ], ), ], @@ -88,3 +116,37 @@ As part of their deliverables, projects will also be required to submit open source, high quality documentation for their technology that can be used as a learning resource by the rest of the community.'''; + +const List _mockGuidance = [ + Guidance( + id: 'g_1', + title: 'Use a Compelling Hook or Unique Angle', + description: + '''Adding an element of intrigue or a unique approach can make your title stand out. For example, “Revolutionizing Urban Mobility with Eco-Friendly Innovation” not only describes the proposal but also piques curiosity.''', + type: GuidanceType.tips, + weight: 1, + ), + Guidance( + id: 'g_1', + title: 'Be Specific and Solution-Oriented', + description: + '''Use keywords that pinpoint the problem you’re solving or the opportunity you’re capitalizing on. A title like “Streamlining Supply Chains for Cost-Effective and Rapid Delivery” instantly tells the reader what the proposal aims to achieve.''', + type: GuidanceType.mandatory, + weight: 2, + ), + Guidance( + id: 'g_1', + title: 'Highlight the Benefit or Outcome', + description: + '''Make sure the reader can immediately see the value or the end result of your proposal. A title like “Boosting Engagement and Growth through Targeted Digital Strategies” puts the focus on the positive outcomes.''', + type: GuidanceType.mandatory, + weight: 1, + ), + Guidance( + id: 'g_1', + title: 'Education', + description: 'Use keywords that pinpoint the problem yo', + type: GuidanceType.education, + weight: 1, + ), +]; diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/catalyst_voices_view_models.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/catalyst_voices_view_models.dart index 29267f3bcf3..5bb833d6762 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/catalyst_voices_view_models.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/catalyst_voices_view_models.dart @@ -11,8 +11,6 @@ export 'navigation/sections_list_view_item.dart'; export 'navigation/sections_navigation.dart'; export 'proposal/comment.dart'; export 'proposal/funded_proposal.dart'; -export 'proposal/guidance/guidance.dart'; -export 'proposal/guidance/guidance_type.dart'; export 'proposal/pending_proposal.dart'; export 'registration/exception/localized_registration_exception.dart'; export 'registration/registration.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance_type.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance_type.dart deleted file mode 100644 index 7b6ccc6990e..00000000000 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal/guidance/guidance_type.dart +++ /dev/null @@ -1,11 +0,0 @@ -enum GuidanceType { mandatory, education, tips } - -extension GuidanceTypeExt on GuidanceType { - int get priority { - return switch (this) { - GuidanceType.mandatory => 0, // Highest priority - GuidanceType.education => 1, - GuidanceType.tips => 2, // Lowest priority - }; - } -} From 77835e4df2e10856979eb1163b0496f468ff78e9 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Thu, 5 Dec 2024 10:25:39 +0100 Subject: [PATCH 13/16] chore: use const in constructor --- .../test/proposals/proposals_cubit_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart index 018442a375b..278246b3b09 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart @@ -27,7 +27,7 @@ void main() { ProposalSectionStep( id: 'f14/0_${index}_1', name: 'Topic 1', - answer: index < 1 ? MarkdownString('Ans') : null, + answer: index < 1 ? const MarkdownString('Ans') : null, ), ], ); From 264091edf856b85d83fcb59e3c0309799a51825b Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Thu, 5 Dec 2024 14:07:11 +0100 Subject: [PATCH 14/16] fix: workspace page event order --- .../apps/voices/lib/pages/workspace/workspace_page.dart | 5 +++-- .../lib/src/{markdown_string.dart => markdown_data.dart} | 0 2 files changed, 3 insertions(+), 2 deletions(-) rename catalyst_voices/packages/internal/catalyst_voices_models/lib/src/{markdown_string.dart => markdown_data.dart} (100%) diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart index a3b1837803f..b2d0e3ea775 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_page.dart @@ -32,8 +32,7 @@ class _WorkspacePageState extends State { void initState() { super.initState(); - final bloc = context.read() - ..add(const LoadCurrentProposalEvent()); + final bloc = context.read(); _sectionsController = SectionsController(); _bodyItemScrollController = ItemScrollController(); @@ -46,6 +45,8 @@ class _WorkspacePageState extends State { .map((event) => event.sections) .distinct(listEquals) .listen(_updateSections); + + bloc.add(const LoadCurrentProposalEvent()); } @override diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_data.dart similarity index 100% rename from catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_string.dart rename to catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_data.dart From 21f74c7da3482febbd431d2c863e9542ec18bc67 Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Thu, 5 Dec 2024 14:07:31 +0100 Subject: [PATCH 15/16] refactor: rename MarkdownString to MarkdownData --- .../lib/common/codecs/markdown_codec.dart | 18 +++++++++--------- .../workspace/workspace_rich_text_step.dart | 2 +- .../common/codecs/markdown_codec_test.dart | 8 ++++---- catalyst_voices/melos.yaml | 2 ++ .../lib/src/workspace/workspace_bloc.dart | 2 +- .../lib/src/workspace/workspace_event.dart | 2 +- .../test/proposals/proposals_cubit_test.dart | 2 +- .../lib/src/catalyst_voices_models.dart | 2 +- .../lib/src/markdown_data.dart | 2 +- .../lib/src/proposal/proposal_section.dart | 4 ++-- .../lib/src/proposal/proposal_repository.dart | 4 ++-- .../lib/src/workspace/workspace_sections.dart | 2 +- 12 files changed, 26 insertions(+), 24 deletions(-) diff --git a/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart b/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart index 38795f99876..08081267f70 100644 --- a/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart +++ b/catalyst_voices/apps/voices/lib/common/codecs/markdown_codec.dart @@ -20,21 +20,21 @@ final _deltaToMd = DeltaToMarkdown( customContentHandler: DeltaToMarkdown.escapeSpecialCharactersRelaxed, ); -final class MarkdownCodec extends Codec { +final class MarkdownCodec extends Codec { const MarkdownCodec(); @override - Converter get decoder => const MarkdownEncoder(); + Converter get decoder => const MarkdownEncoder(); @override - Converter get encoder => const MarkdownDecoder(); + Converter get encoder => const MarkdownDecoder(); } -class MarkdownDecoder extends Converter { +class MarkdownDecoder extends Converter { const MarkdownDecoder(); @override - Delta convert(MarkdownString input) { + Delta convert(MarkdownData input) { if (input.data.isEmpty) { return Delta(); } @@ -43,18 +43,18 @@ class MarkdownDecoder extends Converter { } } -class MarkdownEncoder extends Converter { +class MarkdownEncoder extends Converter { const MarkdownEncoder(); @override - MarkdownString convert(Delta input) { + MarkdownData convert(Delta input) { if (input.isEmpty) { - return const MarkdownString(''); + return const MarkdownData(''); } final data = _deltaToMd.convert(input); final trimmed = data.trim(); - return MarkdownString(trimmed); + return MarkdownData(trimmed); } } diff --git a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart index 2f619948668..676190729c3 100644 --- a/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/workspace/workspace_rich_text_step.dart @@ -30,7 +30,7 @@ class _WorkspaceRichTextStepState extends State { void initState() { super.initState(); - final markdownString = widget.step.initialData ?? const MarkdownString(''); + final markdownString = widget.step.initialData ?? const MarkdownData(''); final delta = markdown.encode(markdownString); final document = delta.isNotEmpty ? Document.fromDelta(delta) : Document(); diff --git a/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart b/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart index 9dcff952c6a..dd0961ed796 100644 --- a/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart +++ b/catalyst_voices/apps/voices/test/common/codecs/markdown_codec_test.dart @@ -7,7 +7,7 @@ void main() { group(MarkdownCodec, () { test('code and encode empty string', () { // Given - const source = MarkdownString(''); + const source = MarkdownData(''); // When final delta = markdown.encode(source); @@ -19,7 +19,7 @@ void main() { test('code and encode plain text', () { // Given - const source = MarkdownString('Hello Catalyst!'); + const source = MarkdownData('Hello Catalyst!'); // When final delta = markdown.encode(source); @@ -59,7 +59,7 @@ void main() { group('encode', () { test('empty markdown string builds valid empty delta', () { // Given - const markdownString = MarkdownString(''); + const markdownString = MarkdownData(''); // When final delta = markdown.encode(markdownString); @@ -71,7 +71,7 @@ void main() { test('plan text markdown builds correct delta', () { // Given const plainText = 'Hello Catalyst!'; - const markdownString = MarkdownString(plainText); + const markdownString = MarkdownData(plainText); // When final delta = markdown.encode(markdownString); diff --git a/catalyst_voices/melos.yaml b/catalyst_voices/melos.yaml index 97a3f9ef63a..16b137e213d 100644 --- a/catalyst_voices/melos.yaml +++ b/catalyst_voices/melos.yaml @@ -112,6 +112,8 @@ command: formz: ^0.7.0 intl: ^0.19.0 logging: ^1.2.0 + markdown: ^7.2.2 + markdown_quill: ^4.2.0 meta: ^1.10.0 result_type: ^0.2.0 password_strength: ^0.2.0 diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart index d1230f03a57..21ebdf53106 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -8,7 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; final class WorkspaceBloc extends Bloc { final CampaignService _campaignService; - final _answers = {}; + final _answers = {}; final _guidances = >{}; SectionStepId? _activeStepId; diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart index fedafb51793..f51dff58017 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_event.dart @@ -15,7 +15,7 @@ final class LoadCurrentProposalEvent extends WorkspaceEvent { final class UpdateStepAnswerEvent extends WorkspaceEvent { final SectionStepId id; - final MarkdownString? data; + final MarkdownData? data; const UpdateStepAnswerEvent({ required this.id, diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart index 278246b3b09..57c89fc6677 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/test/proposals/proposals_cubit_test.dart @@ -27,7 +27,7 @@ void main() { ProposalSectionStep( id: 'f14/0_${index}_1', name: 'Topic 1', - answer: index < 1 ? const MarkdownString('Ans') : null, + answer: index < 1 ? const MarkdownData('Ans') : null, ), ], ); diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart index dd1b7b607a6..216f663bc30 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -10,7 +10,7 @@ export 'crypto/keychain_metadata.dart'; export 'crypto/lock_factor.dart'; export 'errors/errors.dart'; export 'file/voices_file.dart'; -export 'markdown_string.dart'; +export 'markdown_data.dart'; export 'optional.dart'; export 'proposal/guidance.dart'; export 'proposal/proposal.dart'; diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_data.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_data.dart index 0d697c6a531..6c5bebcfef8 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_data.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/markdown_data.dart @@ -1 +1 @@ -extension type const MarkdownString(String data) implements Object {} +extension type const MarkdownData(String data) implements Object {} diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart index ca42cec986a..069fc44ebb9 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/proposal/proposal_section.dart @@ -39,7 +39,7 @@ final class ProposalSectionStep extends Equatable { final String name; final String? description; final List guidances; - final MarkdownString? answer; + final MarkdownData? answer; const ProposalSectionStep({ required this.id, @@ -56,7 +56,7 @@ final class ProposalSectionStep extends Equatable { String? name, Optional? description, List? guidances, - Optional? answer, + Optional? answer, }) { return ProposalSectionStep( id: id ?? this.id, diff --git a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart index 0b8c87152de..67012f80e3e 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/proposal/proposal_repository.dart @@ -68,7 +68,7 @@ final _proposals = [ ProposalSectionStep( id: 'f14/0_${index}_1', name: 'Topic 1', - answer: index < 7 ? const MarkdownString('Ans') : null, + answer: index < 7 ? const MarkdownData('Ans') : null, ), ], ); @@ -93,7 +93,7 @@ final _proposals = [ ProposalSectionStep( id: 'f14/0_${index}_1', name: 'Topic 1', - answer: const MarkdownString('Ans'), + answer: const MarkdownData('Ans'), ), ], ); diff --git a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart index cc503f2deb2..4ad9e817cd7 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/workspace/workspace_sections.dart @@ -33,7 +33,7 @@ sealed class WorkspaceSectionStep extends BaseSectionStep { final class RichTextStep extends WorkspaceSectionStep { final String name; final String? description; - final MarkdownString? initialData; + final MarkdownData? initialData; final int? charsLimit; const RichTextStep({ From fd51e6a010f352d4c48ce1a719dd72cd19a9047b Mon Sep 17 00:00:00 2001 From: Damian Molinski Date: Thu, 5 Dec 2024 14:10:08 +0100 Subject: [PATCH 16/16] refactor: extract _mapProposalSection --- .../lib/src/workspace/workspace_bloc.dart | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart index 21ebdf53106..89ca8bf2c09 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/workspace/workspace_bloc.dart @@ -41,27 +41,7 @@ final class WorkspaceBloc extends Bloc { final template = activeCampaign.proposalTemplate; - final sections = template.sections.map( - (section) { - return WorkspaceSection( - id: section.id, - name: section.name, - steps: section.steps.map( - (step) { - final id = (sectionId: section.id, stepId: step.id); - - return RichTextStep( - id: step.id, - sectionId: section.id, - name: step.name, - description: step.description, - initialData: _answers[id] ?? step.answer, - ); - }, - ).toList(), - ); - }, - ).toList(); + final sections = template.sections.map(_mapProposalSection).toList(); for (final section in template.sections) { for (final step in section.steps) { @@ -112,4 +92,24 @@ final class WorkspaceBloc extends Bloc { emit(state.copyWith(guidance: guidance)); } + + WorkspaceSection _mapProposalSection(ProposalSection section) { + return WorkspaceSection( + id: section.id, + name: section.name, + steps: section.steps.map( + (step) { + final id = (sectionId: section.id, stepId: step.id); + + return RichTextStep( + id: step.id, + sectionId: section.id, + name: step.name, + description: step.description, + initialData: _answers[id] ?? step.answer, + ); + }, + ).toList(), + ); + } }