Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0f5d083
feat(cat-voices): Include username in `CatalystIdText` (#3725)
LynxLynxx Nov 18, 2025
cd1c5b6
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Nov 19, 2025
0e43764
feat(cat-voices): proposals page not found and other errors (#3751)
dt-iohk Nov 20, 2025
2d48755
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Nov 20, 2025
765f46c
feat(cat-voices): add collaborator dialog (#3749)
LynxLynxx Nov 20, 2025
60dd995
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Nov 20, 2025
196d597
feat(cat-voices): proposal viewer collaborators (#3761)
dt-iohk Nov 24, 2025
cb0a2d9
Merge feat/face-performance-optimization-3352 into feat/co-proposers-…
damian-molinski Nov 26, 2025
4b04abd
feat(cat-voices): proposal viewer accept collaborator invitation (#3770)
dt-iohk Nov 27, 2025
137a065
feat(cat-voices): Proposal brief card collaborators (#3784)
damian-molinski Dec 1, 2025
7ab953d
feat(cat-voices): workspace filtering and tabs (#3765)
LynxLynxx Dec 1, 2025
b2fc9bb
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Dec 1, 2025
b1d760f
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Dec 2, 2025
0a5de40
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Dec 2, 2025
f9d85bf
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
LynxLynxx Dec 2, 2025
c2c0737
feat(cat-voices): Collaborator workspace cards (#3802)
LynxLynxx Dec 3, 2025
79632fa
fix(cat-voices): add validation based on significant part of catalyst…
bstolinski Dec 3, 2025
8a7c6d0
feat(cat-voices): collaborators hover in workspace cards (#3809)
LynxLynxx Dec 5, 2025
61303d2
feat(cat-voices): validate collaborator CatalystId (#3815)
LynxLynxx Dec 5, 2025
8facbec
Merge feat/face-performance-optimization-3352 into feat/co-proposers-…
damian-molinski Dec 5, 2025
0aa8f2c
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
damian-molinski Dec 8, 2025
6438bad
feat(cat-voices): Add collaborators section to proposal builder (#3803)
bstolinski Dec 8, 2025
4ab452c
Merge branch 'main' into feat/co-proposers-3677
dt-iohk Dec 9, 2025
a004ea7
feat(cat-voices): proposals briefs collaborators updates (#3822)
damian-molinski Dec 9, 2025
e063a68
Merge branch 'main' into feat/co-proposers-3677
dt-iohk Dec 10, 2025
0123286
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
damian-molinski Dec 11, 2025
e28bfda
feat(cat-voices): campaign switching (#3847)
dt-iohk Dec 11, 2025
07b0413
Merge branch 'feat/face-performance-optimization-3352' into feat/co-p…
damian-molinski Dec 11, 2025
2185c28
feat(cat-voices): workspace local drafts query (#3844)
damian-molinski Dec 11, 2025
fda378b
chore(cat-voices): CatId optimisation (#3852)
damian-molinski Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ collabs
commitlog
concatcp
configureondemand
coproposer
coproposers
COSE
coti
Expand Down
6 changes: 2 additions & 4 deletions catalyst_voices/apps/voices/integration_test/all_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ import 'suites/suites.dart';

void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
final f14Campaign = Campaign.f14();

setUpAll(() async {
binding.testTextInput.register();

// F14
final f14Campaign = Campaign.f14();
mockedActiveCampaign = f14Campaign;

final proposalSubmissionTimeline = f14Campaign.timeline.phases
.firstWhere((element) => element.type == CampaignPhaseType.proposalSubmission)
.timeline;
Expand All @@ -33,6 +30,7 @@ void main() {

await loggingService.updateSettings(printToConsole: const Optional(false));

await Dependencies.instance.get<CampaignService>().setActiveCampaign(f14Campaign);
await Dependencies.instance.get<SyncManager>().waitForSync;
});

Expand Down
11 changes: 9 additions & 2 deletions catalyst_voices/apps/voices/lib/dependency/dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ final class Dependencies extends DependencyProvider {
)
..registerFactory<WorkspaceBloc>(() {
return WorkspaceBloc(
get<UserService>(),
get<CampaignService>(),
get<ProposalService>(),
get<DocumentMapper>(),
Expand Down Expand Up @@ -187,6 +188,7 @@ final class Dependencies extends DependencyProvider {
isRegistered<LoggingService>() ? get<LoggingService>() : null,
get<DownloaderService>(),
get<DocumentsService>(),
get<CampaignService>(),
);
})
..registerFactory<DocumentLookupBloc>(() {
Expand All @@ -213,6 +215,11 @@ final class Dependencies extends DependencyProvider {
return FeatureFlagsCubit(
get<FeatureFlagsService>(),
);
})
..registerFactory<AddCollaboratorCubit>(() {
return AddCollaboratorCubit(
get<ProposalService>(),
);
});
}

Expand Down Expand Up @@ -296,6 +303,8 @@ final class Dependencies extends DependencyProvider {
get<SignedDocumentManager>(),
get<DocumentRepository>(),
get<DatabaseDocumentsDataSource>(),
get<CastedVotesObserver>(),
get<VotingBallotBuilder>(),
),
)
..registerLazySingleton<CommentRepository>(
Expand Down Expand Up @@ -410,8 +419,6 @@ final class Dependencies extends DependencyProvider {
get<UserService>(),
get<SignerService>(),
get<ActiveCampaignObserver>(),
get<CastedVotesObserver>(),
get<VotingBallotBuilder>(),
);
});
registerLazySingleton<CommentService>(() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'dart:async';

import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart';
import 'package:catalyst_voices/widgets/indicators/voices_circular_progress_indicator.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:flutter/material.dart';

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

@override
Widget build(BuildContext context) {
return BlocSelector<AddCollaboratorCubit, AddCollaboratorState, CollaboratorIdState>(
selector: (state) {
return state.collaboratorIdState;
},
builder: (context, collaboratorIdState) {
return _AddCollaboratorButton(collaboratorIdState);
},
);
}
}

class _AddCollaboratorButton extends StatelessWidget {
final CollaboratorIdState collaboratorIdState;

const _AddCollaboratorButton(this.collaboratorIdState);

@override
Widget build(BuildContext context) {
return VoicesFilledButton(
onTap: collaboratorIdState.isValid ? () => _validateCollaboratorId(context) : null,
trailing: collaboratorIdState.isLoading
? const SizedBox(
width: 16,
height: 16,
child: VoicesCircularProgressIndicator(),
)
: null,
child: Text(context.l10n.addCollaborator),
);
}

void _validateCollaboratorId(BuildContext context) {
if (collaboratorIdState.isLoading) return;

unawaited(context.read<AddCollaboratorCubit>().validateCollaboratorId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import 'dart:async';

import 'package:catalyst_voices/dependency/dependencies.dart';
import 'package:catalyst_voices/pages/co_proposers/widgets/add_collaborator/add_collaborator_view.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';

class AddCollaboratorDialog extends StatefulWidget {
final CatalystId authorId;
final CollaboratorsIds collaborators;

const AddCollaboratorDialog({super.key, required this.authorId, required this.collaborators});

@override
State<AddCollaboratorDialog> createState() => _AddCollaboratorDialogState();

static Future<CatalystId?> show(
BuildContext context, {
required CatalystId authorId,
CollaboratorsIds? collaborators,
}) async {
return VoicesDialog.show(
context: context,
builder: (context) => AddCollaboratorDialog(
authorId: authorId,
collaborators: collaborators ?? const CollaboratorsIds(),
),
routeSettings: const RouteSettings(name: '/add-collaborator-dialog'),
);
}
}

class _AddCollaboratorDialogState extends State<AddCollaboratorDialog> {
late final AddCollaboratorCubit _cubit;

@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cubit,
child: const ScaffoldMessenger(
child: Scaffold(
backgroundColor: Colors.transparent,
body: VoicesPanelDialog(
constraints: Responsive.single(BoxConstraints(maxWidth: 602, maxHeight: 396)),
child: AddCollaboratorView(),
),
),
),
);
}

@override
void dispose() {
unawaited(_cubit.close());
super.dispose();
}

@override
void initState() {
super.initState();
_cubit = Dependencies.instance.get<AddCollaboratorCubit>();

_cubit.init(collaborators: widget.collaborators, authorCatalystId: widget.authorId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:async';

import 'package:catalyst_voices/common/ext/build_context_ext.dart';
import 'package:catalyst_voices/widgets/text_field/voices_text_field.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';

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

@override
Widget build(BuildContext context) {
return BlocSelector<AddCollaboratorCubit, AddCollaboratorState, CollaboratorCatalystId>(
selector: (state) {
return state.collaboratorIdState.collaboratorId;
},
builder: (context, collaboratorId) {
return _AddCollaboratorTextField(collaboratorId);
},
);
}
}

class __AddCollaboratorTextFieldState extends State<_AddCollaboratorTextField> {
late final FocusNode _focusNode;

String? get errorMessage {
final error = widget.collaboratorId.displayError;
if (error is InvalidCatalystIdFormatValidationException) {
return error.message(context);
}
return null;
}

@override
Widget build(BuildContext context) {
return VoicesTextField(
focusNode: _focusNode,
initialText: widget.collaboratorId.value,
onChanged: _onTextFieldChange,
onFieldSubmitted: _onTextFieldSubmitted,
decoration: VoicesTextFieldDecoration(
labelText: context.l10n.catalystId,
labelStyle: context.textTheme.labelLarge,
errorText: errorMessage,
),
);
}

@override
void dispose() {
_focusNode.dispose();
super.dispose();
}

@override
void initState() {
super.initState();
_focusNode = FocusNode()..requestFocus();
}

void _onTextFieldChange(String? value) {
context.read<AddCollaboratorCubit>().updateCollaboratorId(value ?? '');
}

void _onTextFieldSubmitted(String? value) {
unawaited(context.read<AddCollaboratorCubit>().validateCollaboratorId());
}
}

class _AddCollaboratorTextField extends StatefulWidget {
final CollaboratorCatalystId collaboratorId;

const _AddCollaboratorTextField(this.collaboratorId);

@override
State<_AddCollaboratorTextField> createState() => __AddCollaboratorTextFieldState();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import 'package:catalyst_voices/common/error_handler.dart';
import 'package:catalyst_voices/common/ext/build_context_ext.dart';
import 'package:catalyst_voices/common/signal_handler.dart';
import 'package:catalyst_voices/pages/co_proposers/widgets/add_collaborator/add_collaborator_button.dart';
import 'package:catalyst_voices/pages/co_proposers/widgets/add_collaborator/add_collaborator_text_field.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.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:flutter/material.dart';

class AddCollaboratorView extends StatefulWidget {
const AddCollaboratorView({super.key});

@override
State<AddCollaboratorView> createState() => _AddCollaboratorViewState();
}

class _AddCollaboratorViewState extends State<AddCollaboratorView>
with
ErrorHandlerStateMixin<AddCollaboratorCubit, AddCollaboratorView>,
SignalHandlerStateMixin<AddCollaboratorCubit, AddCollaboratorSignal, AddCollaboratorView> {
@override
Widget build(BuildContext context) {
return const SingleChildScrollView(
child: Padding(
padding: EdgeInsets.fromLTRB(40, 42, 40, 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_HeaderIcon(),
_HeaderText(),
SizedBox(height: 24),
_Description(),
SizedBox(height: 28),
AddCollaboratorTextField(),
SizedBox(height: 24),
AddCollaboratorButton(),
],
),
),
);
}

@override
void handleSignal(AddCollaboratorSignal signal) {
return switch (signal) {
ValidCollaboratorIdSignal(:final catalystId) => _popWithResult(catalystId),
};
}

void _popWithResult(CatalystId catalystId) {
Navigator.pop(context, catalystId);
}
}

class _Description extends StatelessWidget {
const _Description();

@override
Widget build(BuildContext context) {
return Text(context.l10n.howToAddCollaboratorDescription);
}
}

class _HeaderIcon extends StatelessWidget {
const _HeaderIcon();

@override
Widget build(BuildContext context) {
return VoicesAssets.icons.userGroup.buildIcon(
size: 76,
color: context.colors.iconsPrimary,
);
}
}

class _HeaderText extends StatelessWidget {
const _HeaderText();

@override
Widget build(BuildContext context) {
return Text(
context.l10n.addCollaborator,
style: context.textTheme.titleLarge,
);
}
}
Loading