Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ final class Dependencies extends DependencyProvider {
get<VotingBallotBuilder>(),
get<VotingService>(),
);
})
..registerFactory<AddCollaboratorCubit>(() {
return AddCollaboratorCubit(
get<ProposalService>(),
);
});
}

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 Collaborators 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,
Collaborators? collaborators,
}) async {
return VoicesDialog.show(
context: context,
builder: (context) => AddCollaboratorDialog(
authorId: authorId,
collaborators: collaborators ?? const Collaborators(),
),
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: ScaffoldMessenger(
child: Scaffold(
backgroundColor: Colors.transparent,
body: VoicesPanelDialog(
constraints: const Responsive.single(BoxConstraints(maxWidth: 602, maxHeight: 396)),
child: const 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