Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
62038e6
feat(feedback): add cancel option and primary submit
CharlVS Jul 29, 2025
f441b34
Add width and height to UiUnderlineTextButton in feedback form
cursoragent Aug 11, 2025
fdec9fc
Merge branch 'dev' into codex/add-cancel-button-next-to-submit
CharlVS Aug 11, 2025
1bbfb3a
feat(feedback): replace custom Cancel with TextButton; tighten spacin…
cursoragent Aug 12, 2025
70d24f7
Merge branch 'dev' into codex/add-cancel-button-next-to-submit
CharlVS Aug 12, 2025
87b60a6
Merge branch 'dev' into codex/add-cancel-button-next-to-submit
CharlVS Aug 14, 2025
5b9caf1
chore(feedback): address PR comments – replace custom cancel with Tex…
cursoragent Aug 13, 2025
f2aa8e3
feat(feedback): add Cancel button next to Submit and centralize Bette…
CharlVS Aug 14, 2025
416422e
fix(feedback): always render form content; hide drag handle when no s…
CharlVS Aug 14, 2025
b2898f0
Merge branch 'dev' of https://github.com/KomodoPlatform/komodo-wallet…
CharlVS Aug 21, 2025
c393699
ci: ci test fix
CharlVS Aug 21, 2025
988667f
fix(feedback): Fix incorrect locale message
CharlVS Aug 21, 2025
5630831
feat(feedback): attach recent logs (<=9MB) to submissions; refactor s…
CharlVS Aug 21, 2025
0c38441
Merge branch 'dev' into codex/add-cancel-button-next-to-submit
CharlVS Aug 25, 2025
c970a22
Merge branch 'dev' of https://github.com/KomodoPlatform/komodo-wallet…
CharlVS Sep 1, 2025
602a08c
fix(deps): add missing import
CharlVS Sep 1, 2025
baa62ee
chore(analysis): exclude generated files from analysis (freezed, g.dart)
CharlVS Sep 1, 2025
7bb7ea7
Add screenshot sensitivity to protect sensitive UI elements
cursoragent Sep 2, 2025
f73b169
feat(feedback-form): add Cancel button next to Submit
CharlVS Sep 2, 2025
7fe9fb2
merge: integrate screenshot sensitivity and resolve conflicts in feed…
CharlVS Sep 2, 2025
c932e4d
Merge branch 'dev' into codex/add-cancel-button-next-to-submit
CharlVS Sep 2, 2025
98f8670
Merge branch 'dev' of https://github.com/KomodoPlatform/komodo-wallet…
CharlVS Sep 11, 2025
7240906
feat(feedback): include coins commit hashes in feedback metadata; cho…
CharlVS Sep 11, 2025
3cbc8b1
feat(feedback): add coins commit hashes to submission metadata and ca…
CharlVS Sep 11, 2025
496a1f2
Merge branch 'dev' into codex/add-cancel-button-next-to-submit
CharlVS Sep 15, 2025
abb8110
Refactor: Improve feedback key formatting for clarity
cursoragent Sep 15, 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
7 changes: 7 additions & 0 deletions .github/workflows/docker-android-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ jobs:
- name: Build Android image
env:
GITHUB_API_PUBLIC_READONLY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Optional feedback provider secrets to embed dart-defines
TRELLO_API_KEY: ${{ secrets.TRELLO_API_KEY }}
TRELLO_TOKEN: ${{ secrets.TRELLO_TOKEN }}
TRELLO_BOARD_ID: ${{ secrets.TRELLO_BOARD_ID }}
TRELLO_LIST_ID: ${{ secrets.TRELLO_LIST_ID }}
FEEDBACK_API_KEY: ${{ secrets.FEEDBACK_API_KEY }}
FEEDBACK_PRODUCTION_URL: ${{ secrets.FEEDBACK_PRODUCTION_URL }}
run: |
chmod +x .docker/build.sh
sh .docker/build.sh apk release
23 changes: 10 additions & 13 deletions .github/workflows/mobile-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ jobs:
store-password: ${{ secrets.ANDROID_STORE_PASSWORD }}
key-password: ${{ secrets.ANDROID_KEY_PASSWORD }}

# Flutter build with `--no-pub` flag fails on Android due to a
# known regression with the build system in 3.32.5.
# https://github.com/flutter/flutter/issues/169336
# Run config-only before the full double-build to ensure success.
- name: Temporary workaround for Android build issue
if: ${{ matrix.platform == 'Android' }}
run: |
flutter build apk --config-only

- name: Fetch packages and generate assets
uses: ./.github/actions/generate-assets
with:
Expand All @@ -68,19 +77,7 @@ jobs:
TRELLO_LIST_ID: ${{ secrets.TRELLO_LIST_ID }}
FEEDBACK_API_KEY: ${{ secrets.FEEDBACK_API_KEY }}
FEEDBACK_PRODUCTION_URL: ${{ secrets.FEEDBACK_PRODUCTION_URL }}

# Flutter build with `--no-pub` flag fails on Android due to a
# known regression with the build system in 3.32.5.
# https://github.com/flutter/flutter/issues/169336
- name: Temporary workaround for Android build issue
if: ${{ matrix.platform == 'Android' }}
run: |
flutter build apk --config-only

- name: Build for ${{ matrix.platform }}
env:
GITHUB_API_PUBLIC_READONLY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ${{ matrix.build_command }}
BUILD_COMMAND: ${{ matrix.build_command }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Flutter Build Step Removed, CI Pipeline Broken

The Build for ${{ matrix.platform }} step, which executed the flutter build command, was removed. While BUILD_COMMAND is now passed to the generate-assets action, the workflow no longer invokes the actual build. This prevents mobile artifacts (APK/IPA) from being produced, causing the 'Upload artifact' step to fail and breaking the mobile CI pipeline.

Fix in CursorΒ Fix in Web


- name: Upload artifact
uses: actions/upload-artifact@v4
Expand Down
5 changes: 5 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ include:
- package:bloc_lint/recommended.yaml
- package:flutter_lints/flutter.yaml

analyzer:
exclude:
- "**/*.freezed.dart"
- "**/*.g.dart"

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
Expand Down
22 changes: 20 additions & 2 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,28 @@
"feedbackFormDescription": "Our mission to improve the wallet never stops, and your feedback is highly appreciated!",
"feedbackFormThanksTitle": "Thank you for your feedback",
"feedbackFormThanksDescription": "We will send a response to your email address as soon as possible",
"feedbackFormKindQuestion": "What kind of feedback do you want to give?",
"feedbackFormDescribeTitle": "Your feedback",
"feedbackFormContactRequired": "How can we contact you?",
"feedbackFormContactOptional": "How can we contact you? (Optional)",
"feedbackFormMessageHint": "Enter your feedback here...",
"feedbackFormBugReport": "Bug Report",
"feedbackFormFeatureRequest": "Feature Request",
"feedbackFormSupportRequest": "Support Request",
"feedbackFormOther": "Other",
"feedbackFormDiscord": "Discord",
"feedbackFormMatrix": "Matrix",
"feedbackFormTelegram": "Telegram",
"feedbackFormSelectContactMethod": "Select contact method",
"feedbackFormDiscordHint": "Discord username (e.g., username123)",
"feedbackFormMatrixHint": "Matrix ID (e.g., @user:matrix.org)",
"feedbackFormTelegramHint": "Telegram username (e.g., @username)",
"feedbackFormEmailHint": "Your email address",
"feedbackFormContactHint": "Enter your contact details",
"feedbackFormContactOptOut": "I don't want to share contact info",
"email": "Email",
"emailValidatorError": "Please enter a valid email address",
"contactRequiredError": "Contact details are required for support requests",
"contactRequiredError": "Please provide both contact method and details",
"contactDetailsMaxLengthError": "Contact details must be {} characters or less",
"discordUsernameValidatorError": "Please enter a valid Discord username (2-32 characters, letters, numbers, dots, underscores)",
"telegramUsernameValidatorError": "Please enter a valid Telegram username (5-32 characters, letters, numbers, underscores)",
Expand Down Expand Up @@ -491,7 +510,6 @@
"feedback": "Feedback",
"feedbackViewTitle": "Send us your feedback",
"feedbackPageDescription": "Help us improve by sharing your suggestions, reporting bugs, or giving general feedback.",
"sendFeedbackButton": "Share your feedback",
"feedbackThankyou": "Thank you for your feedback!",
"feedbackError": "Failed to submit feedback",
"selectAToken": "Select a token",
Expand Down
103 changes: 74 additions & 29 deletions lib/bloc/feedback_form/feedback_form_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
on<FeedbackFormMessageChanged>(_onMessageChanged);
on<FeedbackFormContactMethodChanged>(_onContactMethodChanged);
on<FeedbackFormContactDetailsChanged>(_onContactDetailsChanged);
on<FeedbackFormContactOptOutChanged>(_onContactOptOutChanged);
on<FeedbackFormSubmitted>(_onSubmitted);
}

Expand All @@ -29,21 +30,25 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
event.type,
state.contactMethod,
);
emit(state.copyWith(
feedbackType: event.type,
contactDetailsError: contactError,
));
emit(
state.copyWith(
feedbackType: event.type,
contactDetailsError: contactError,
),
);
}

void _onMessageChanged(
FeedbackFormMessageChanged event,
Emitter<FeedbackFormState> emit,
) {
final text = _sanitizeInput(event.message);
emit(state.copyWith(
feedbackText: text,
feedbackTextError: _validateFeedbackText(text),
));
emit(
state.copyWith(
feedbackText: text,
feedbackTextError: _validateFeedbackText(text),
),
);
}

void _onContactMethodChanged(
Expand All @@ -55,8 +60,9 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
state.feedbackType,
event.method,
);
emit(state.copyWith(
contactMethod: event.method, contactDetailsError: error));
emit(
state.copyWith(contactMethod: event.method, contactDetailsError: error),
);
}

void _onContactDetailsChanged(
Expand All @@ -72,6 +78,33 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
emit(state.copyWith(contactDetails: details, contactDetailsError: error));
}

void _onContactOptOutChanged(
FeedbackFormContactOptOutChanged event,
Emitter<FeedbackFormState> emit,
) {
if (event.optOut && !state.isSupportType) {
emit(
state.copyWith(
contactOptOut: true,
contactMethod: null,
contactDetails: '',
contactDetailsError: null,
),
);
return;
}

final error = _validateContactDetails(
state.contactDetails,
state.feedbackType,
state.contactMethod,
contactOptOut: event.optOut,
);
emit(
state.copyWith(contactOptOut: event.optOut, contactDetailsError: error),
);
}

Future<void> _onSubmitted(
FeedbackFormSubmitted event,
Emitter<FeedbackFormState> emit,
Expand All @@ -86,10 +119,12 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
if (state.feedbackType == null ||
feedbackErr != null ||
contactErr != null) {
emit(state.copyWith(
feedbackTextError: feedbackErr,
contactDetailsError: contactErr,
));
emit(
state.copyWith(
feedbackTextError: feedbackErr,
contactDetailsError: contactErr,
),
);
return;
}

Expand All @@ -99,17 +134,16 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
feedbackType: state.feedbackType,
feedbackText: state.feedbackText,
contactMethod: state.contactMethod,
contactDetails:
state.contactDetails.isNotEmpty ? state.contactDetails : null,
);
await _onSubmit(
data.toFormattedDescription(),
extras: data.toMap(),
contactDetails: state.contactDetails.isNotEmpty
? state.contactDetails
: null,
);
await _onSubmit(data.toFormattedDescription(), extras: data.toMap());
emit(state.copyWith(status: FeedbackFormStatus.success));
} catch (e) {
emit(state.copyWith(
status: FeedbackFormStatus.failure, errorMessage: '$e'));
emit(
state.copyWith(status: FeedbackFormStatus.failure, errorMessage: '$e'),
);
}
}

Expand All @@ -129,19 +163,27 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
String? _validateContactDetails(
String value,
FeedbackType? type,
ContactMethod? method,
) {
ContactMethod? method, {
bool? contactOptOut,
}) {
final trimmed = value.trim();
final hasMethod = method != null;
final hasDetails = trimmed.isNotEmpty;
final optedOut = contactOptOut ?? state.contactOptOut;

if (type == FeedbackType.support || type == FeedbackType.missingCoins) {
if (!hasMethod || !hasDetails) {
return LocaleKeys.contactRequiredError.tr();
}
} else {
if ((hasMethod && !hasDetails) || (!hasMethod && hasDetails)) {
return LocaleKeys.contactRequiredError.tr();
if (!optedOut) {
if (!hasMethod || !hasDetails) {
return LocaleKeys.contactRequiredError.tr();
}
} else {
if ((hasMethod && !hasDetails) || (!hasMethod && hasDetails)) {
return LocaleKeys.contactRequiredError.tr();
}
}
}

Expand Down Expand Up @@ -187,9 +229,12 @@ class FeedbackFormBloc extends Bloc<FeedbackFormEvent, FeedbackFormState> {
.trim()
.replaceAll(RegExp(r'<[^>]*>'), '')
.replaceAll(
RegExp(r'<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>',
caseSensitive: false),
'')
RegExp(
r'<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>',
caseSensitive: false,
),
'',
)
.replaceAll(RegExp(r'javascript:', caseSensitive: false), '')
.replaceAll(RegExp(r'data:[^,]*script[^,]*,', caseSensitive: false), '')
.replaceAll(RegExp(r'\n{3,}'), '\n\n');
Expand Down
9 changes: 9 additions & 0 deletions lib/bloc/feedback_form/feedback_form_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ class FeedbackFormContactDetailsChanged extends FeedbackFormEvent {
List<Object?> get props => [details];
}

class FeedbackFormContactOptOutChanged extends FeedbackFormEvent {
const FeedbackFormContactOptOutChanged(this.optOut);

final bool optOut;

@override
List<Object?> get props => [optOut];
}

class FeedbackFormSubmitted extends FeedbackFormEvent {
const FeedbackFormSubmitted();
}
36 changes: 27 additions & 9 deletions lib/bloc/feedback_form/feedback_form_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class FeedbackFormState extends Equatable {
this.contactMethod,
this.contactDetails = '',
this.contactDetailsError,
this.contactOptOut = false,
this.status = FeedbackFormStatus.initial,
this.errorMessage,
});
Expand All @@ -20,6 +21,7 @@ class FeedbackFormState extends Equatable {
final ContactMethod? contactMethod;
final String contactDetails;
final String? contactDetailsError;
final bool contactOptOut;
final FeedbackFormStatus status;
final String? errorMessage;

Expand All @@ -29,13 +31,27 @@ class FeedbackFormState extends Equatable {
contactDetailsError == null &&
feedbackText.trim().isNotEmpty;

bool get isSubmitting => status == FeedbackFormStatus.submitting;

bool get isSupportType =>
feedbackType == FeedbackType.support ||
feedbackType == FeedbackType.missingCoins;

bool get isContactOptOutVisible => !isSupportType;

bool get isContactRequired => isSupportType || !contactOptOut;

bool get isContactRowDisabled =>
isSubmitting || (isContactOptOutVisible && contactOptOut);

FeedbackFormState copyWith({
FeedbackType? feedbackType,
String? feedbackText,
String? feedbackTextError,
ContactMethod? contactMethod,
String? contactDetails,
String? contactDetailsError,
bool? contactOptOut,
FeedbackFormStatus? status,
String? errorMessage,
}) {
Expand All @@ -46,20 +62,22 @@ class FeedbackFormState extends Equatable {
contactMethod: contactMethod ?? this.contactMethod,
contactDetails: contactDetails ?? this.contactDetails,
contactDetailsError: contactDetailsError,
contactOptOut: contactOptOut ?? this.contactOptOut,
status: status ?? this.status,
errorMessage: errorMessage ?? this.errorMessage,
);
}

@override
List<Object?> get props => [
feedbackType,
feedbackText,
feedbackTextError,
contactMethod,
contactDetails,
contactDetailsError,
status,
errorMessage,
];
feedbackType,
feedbackText,
feedbackTextError,
contactMethod,
contactDetails,
contactDetailsError,
contactOptOut,
status,
errorMessage,
];
}
Loading
Loading