Skip to content

Commit 5f2da0a

Browse files
feat(cat-voices): campaign timeline config (#2125)
* proposal sub guard * feat: add Background widget and pre-proposal submission page with countdown and localized messages * feat: implement BubblePainter for background shapes and enhance AfterProposalSubmissionPage with structured layout and localization * feat: no wallet found dialog ui * feat: enhance account creation flow with wallet check and no wallet dialog integration in registration process * feat: add submission closing warning dialog with countdown and user tips for proposal submissions in workspace and proposal builder * fix: self review * fix: spelling * fix: Warning spelling * fix: correct spelling errors in localization strings for proposal submission details * fix: review * fix:format --------- Co-authored-by: Damian Moliński <[email protected]>
1 parent 4b0144f commit 5f2da0a

File tree

58 files changed

+2182
-304
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2182
-304
lines changed

catalyst_voices/apps/voices/lib/app/view/app.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ class _AppState extends State<App> {
7474
BlocProvider<NewProposalCubit>(
7575
create: (_) => Dependencies.instance.get<NewProposalCubit>(),
7676
),
77+
BlocProvider<CampaignStageCubit>(
78+
lazy: false,
79+
create: (_) => Dependencies.instance.get<CampaignStageCubit>(),
80+
),
7781
];
7882
}
7983
}

catalyst_voices/apps/voices/lib/app/view/app_mobile_access_restriction.dart

Lines changed: 77 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:math';
33
import 'package:catalyst_voices/common/ext/build_context_ext.dart';
44
import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart';
55
import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart';
6+
import 'package:catalyst_voices/widgets/painter/bubble_painter.dart';
67
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
78
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
89
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
@@ -96,181 +97,91 @@ class _Background extends StatelessWidget {
9697
@override
9798
Widget build(BuildContext context) {
9899
return CustomPaint(
99-
painter: _BubblePainter(isMobile: isMobile),
100+
painter: BubblePainter(
101+
bubbles: _buildBubbles(),
102+
shapes: _buildShapes(),
103+
),
100104
size: Size.infinite,
101105
);
102106
}
103-
}
104-
105-
class _BubblePainter extends CustomPainter {
106-
final bool isMobile;
107-
108-
_BubblePainter({required this.isMobile});
109-
110-
@override
111-
void paint(Canvas canvas, Size size) {
112-
// Background
113-
canvas.drawRect(
114-
Rect.fromLTWH(0, 0, size.width, size.height),
115-
Paint()..color = const Color(0xff9BDDF7),
116-
);
117-
118-
// Left bubble
119-
_drawBubble(
120-
canvas,
121-
x: isMobile ? 0 - 70 : 0 - 90,
122-
y: size.height * 0.25,
123-
radius: isMobile ? 110 : 200,
124-
gradientColors: const [Color(0xFFE5F6FF), Color(0xCCE5F6FF)],
125-
gradientStops: const [0.0, 1.0],
126-
);
127-
128-
// Right bubble
129-
_drawBubble(
130-
canvas,
131-
x: isMobile ? size.width + 70 : size.width + 140,
132-
y: isMobile ? size.height : size.height + 140,
133-
radius: isMobile ? 140 : 430,
134-
gradientColors: const [Color(0xFFE5F6FF), Color(0xCCE5F6FF)],
135-
gradientStops: const [0.0, 1.0],
136-
);
137107

138-
// Left shape
139-
_drawShape(
140-
canvas,
141-
size,
142-
controlPoints: [
143-
Point(0, size.height * .7),
144-
Point(size.width * .13, size.height * .82),
145-
Point(size.width * .15, size.height),
146-
Point(0, size.height),
147-
],
148-
gradient: const RadialGradient(
149-
center: Alignment(0.2822, -0.3306),
150-
radius: 0.5,
151-
colors: [Color(0x99F9E7FD), Color(0x99F6CEFF)],
152-
stops: [0.0, 0.0],
153-
),
154-
);
155-
156-
// First right shape
157-
_drawShape(
158-
canvas,
159-
size,
160-
controlPoints: [
161-
Point(size.width * .75, 0),
162-
Point(
163-
isMobile ? size.width * .8 : size.width * .7,
164-
isMobile ? size.height * .15 : size.height * .3,
108+
List<BubbleConfig> _buildBubbles() {
109+
return [
110+
BubbleConfig(
111+
position: (size) => Offset(
112+
isMobile ? 0 - 70 : 0 - 90,
113+
size.height * 0.25,
165114
),
166-
Point(
167-
size.width,
168-
isMobile ? size.height * .25 : size.height * .4,
115+
radius: isMobile ? 110 : 200,
116+
gradientColors: const [Color(0xFFE5F6FF), Color(0xCCE5F6FF)],
117+
gradientStops: const [0.0, 1.0],
118+
shadowBlur: 62.46,
119+
shadowOffset: const Offset(-9.99, -10.99),
120+
shadowColor: const Color.fromRGBO(150, 142, 253, 0.4),
121+
),
122+
BubbleConfig(
123+
position: (size) => Offset(
124+
isMobile ? size.width + 70 : size.width + 140,
125+
isMobile ? size.height : size.height + 140,
169126
),
170-
Point(size.width, 0),
171-
],
172-
color: Color.fromARGB((255 * 0.1).toInt(), 192, 20, 235),
173-
);
174-
175-
// Second right shape
176-
_drawShape(
177-
canvas,
178-
size,
179-
controlPoints: [
180-
Point(size.width, size.height * .2),
181-
Point(size.width * .7, size.height * .45),
182-
Point(size.width, size.height * .6),
183-
],
184-
gradient: const RadialGradient(
185-
center: Alignment(0.2814, -0.3306),
186-
radius: 0.5,
187-
colors: [
188-
Color.fromRGBO(205, 213, 254, 0.7),
189-
Color(0x99C6C5FF),
190-
],
191-
stops: [0.0, 1.0],
127+
radius: isMobile ? 140 : 430,
128+
gradientColors: const [Color(0xFFE5F6FF), Color(0xCCE5F6FF)],
129+
gradientStops: const [0.0, 1.0],
130+
shadowBlur: 62.46,
131+
shadowOffset: const Offset(-9.99, -10.99),
132+
shadowColor: const Color.fromRGBO(150, 142, 253, 0.4),
192133
),
193-
);
134+
];
194135
}
195136

196-
@override
197-
bool shouldRepaint(_BubblePainter oldDelegate) =>
198-
isMobile != oldDelegate.isMobile;
199-
200-
void _drawBubble(
201-
Canvas canvas, {
202-
required double x,
203-
required double y,
204-
required double radius,
205-
required List<Color> gradientColors,
206-
required List<double> gradientStops,
207-
}) {
208-
final rect = Rect.fromCircle(center: Offset(x, y), radius: radius);
209-
final shadowPath = Path()..addOval(rect);
210-
211-
canvas
212-
..save()
213-
..translate(-9.99, -10.99)
214-
..drawShadow(
215-
shadowPath,
216-
const Color.fromRGBO(150, 142, 253, 0.4),
217-
62.46,
218-
true,
219-
)
220-
..restore();
221-
222-
final paintGradient = Paint()
223-
..shader = RadialGradient(
224-
colors: gradientColors,
225-
stops: gradientStops,
226-
center: Alignment.center,
227-
radius: 0.8,
228-
).createShader(rect)
229-
..blendMode = BlendMode.softLight;
230-
231-
canvas.drawCircle(Offset(x, y), radius, paintGradient);
232-
}
233-
234-
void _drawShape(
235-
Canvas canvas,
236-
Size size, {
237-
required List<Point<double>> controlPoints,
238-
Color? color,
239-
RadialGradient? gradient,
240-
}) {
241-
final path = Path()..moveTo(controlPoints[0].x, controlPoints[0].y);
242-
243-
if (controlPoints.length == 4) {
244-
path
245-
..quadraticBezierTo(
246-
controlPoints[1].x,
247-
controlPoints[1].y,
248-
controlPoints[2].x,
249-
controlPoints[2].y,
250-
)
251-
..lineTo(controlPoints[3].x, controlPoints[3].y);
252-
} else if (controlPoints.length == 3) {
253-
path.quadraticBezierTo(
254-
controlPoints[1].x,
255-
controlPoints[1].y,
256-
controlPoints[2].x,
257-
controlPoints[2].y,
258-
);
259-
}
260-
261-
path.close();
262-
263-
final paint = Paint()..style = PaintingStyle.fill;
264-
265-
if (gradient != null) {
266-
paint.shader = gradient.createShader(
267-
Rect.fromLTWH(0, 0, size.width, size.height),
268-
);
269-
} else if (color != null) {
270-
paint.color = color;
271-
}
272-
273-
canvas.drawPath(path, paint);
137+
List<ShapeConfig> _buildShapes() {
138+
return [
139+
ShapeConfig(
140+
controlPointsCalculator: (Size size) => [
141+
Point(0, size.height * .7),
142+
Point(size.width * .13, size.height * .82),
143+
Point(size.width * .15, size.height),
144+
Point(0, size.height),
145+
],
146+
gradient: const RadialGradient(
147+
center: Alignment(0.2822, -0.3306),
148+
radius: 0.5,
149+
colors: [Color(0x99F9E7FD), Color(0x99F6CEFF)],
150+
stops: [1.0, 0.0],
151+
),
152+
),
153+
ShapeConfig(
154+
controlPointsCalculator: (Size size) => [
155+
Point(size.width * .75, 0),
156+
Point(
157+
isMobile ? size.width * .8 : size.width * .7,
158+
isMobile ? size.height * .15 : size.height * .3,
159+
),
160+
Point(
161+
size.width,
162+
isMobile ? size.height * .25 : size.height * .4,
163+
),
164+
Point(size.width, 0),
165+
],
166+
color: Color.fromARGB((255 * 0.1).toInt(), 192, 20, 235),
167+
),
168+
ShapeConfig(
169+
controlPointsCalculator: (Size size) => [
170+
Point(size.width, size.height * .2),
171+
Point(size.width * .7, size.height * .45),
172+
Point(size.width, size.height * .6),
173+
],
174+
gradient: const RadialGradient(
175+
center: Alignment(0.2814, -0.3306),
176+
radius: 0.5,
177+
colors: [
178+
Color.fromRGBO(205, 213, 254, 0.7),
179+
Color(0x99C6C5FF),
180+
],
181+
stops: [0.0, 1.0],
182+
),
183+
),
184+
];
274185
}
275186
}
276187

catalyst_voices/apps/voices/lib/dependency/dependencies.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ final class Dependencies extends DependencyProvider {
143143
get<ProposalService>(),
144144
get<DocumentMapper>(),
145145
);
146+
})
147+
..registerFactory<CampaignStageCubit>(() {
148+
return CampaignStageCubit(get<CampaignService>());
146149
});
147150
}
148151

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import 'package:catalyst_voices/common/ext/build_context_ext.dart';
2+
import 'package:catalyst_voices/pages/campaign/stage/campaign_background.dart';
3+
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
4+
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
5+
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
6+
import 'package:flutter/material.dart';
7+
8+
class AfterProposalSubmissionPage extends StatelessWidget {
9+
const AfterProposalSubmissionPage({super.key});
10+
11+
@override
12+
Widget build(BuildContext context) {
13+
return Theme(
14+
data: ThemeBuilder.buildTheme(),
15+
child: CampaignBackground(
16+
child: ConstrainedBox(
17+
constraints: const BoxConstraints.tightFor(width: 560),
18+
child: Column(
19+
mainAxisAlignment: MainAxisAlignment.center,
20+
mainAxisSize: MainAxisSize.min,
21+
children: [
22+
CatalystImage.asset(VoicesAssets.images.thanks.path),
23+
const SizedBox(height: 17),
24+
const _Header(),
25+
const SizedBox(height: 35),
26+
const _Description(),
27+
const SizedBox(height: 24),
28+
const _ActionButton(),
29+
],
30+
),
31+
),
32+
),
33+
);
34+
}
35+
}
36+
37+
class _ActionButton extends StatelessWidget {
38+
const _ActionButton();
39+
40+
@override
41+
Widget build(BuildContext context) {
42+
return FilledButton(
43+
onPressed: () {
44+
// TODO(LynxLynxx): implement url launching
45+
},
46+
child: Text(context.l10n.learnMore),
47+
);
48+
}
49+
}
50+
51+
class _Description extends StatelessWidget {
52+
const _Description();
53+
54+
@override
55+
Widget build(BuildContext context) {
56+
return Text(
57+
context.l10n.proposalSubmissionClosedDescription,
58+
style: context.textTheme.bodyLarge,
59+
textAlign: TextAlign.center,
60+
);
61+
}
62+
}
63+
64+
class _Header extends StatelessWidget {
65+
const _Header();
66+
67+
@override
68+
Widget build(BuildContext context) {
69+
return Text(
70+
context.l10n.proposalSubmissionIsClosed,
71+
style: context.textTheme.displaySmall,
72+
textAlign: TextAlign.center,
73+
);
74+
}
75+
}

0 commit comments

Comments
 (0)