Skip to content

Commit

Permalink
add contact support
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-ishita-g committed Jun 5, 2024
1 parent b8ea392 commit 1ff2a77
Show file tree
Hide file tree
Showing 16 changed files with 845 additions and 70 deletions.
28 changes: 10 additions & 18 deletions app/assets/locales/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -113,29 +113,21 @@
"edit_space_your_location_sharing_title": "Your location sharing",
"edit_space_delete_space_title": "Delete space",
"edit_space_leave_space_title": "Leave space",
"edit_space_leave_space_alert_message": "Are you sure you want to leave the {spaceName} space.",
"@edit_space_leave_space_alert_message": {
"placeholders": {
"spaceName": {
"type": "String"
}
}
},
"edit_space_delete_space_alert_message": "Are you sure you want to delete the {spaceName} space.",
"@edit_space_delete_space_alert_message": {
"placeholders": {
"spaceName": {
"type": "String"
}
}
},
"edit_space_leave_alert_title": "Leave",
"edit_space_leave_space_alert_message": "Are you sure you want to leave this space? You will no longer be able to view or share location with member of this space",
"edit_space_delete_space_alert_message": "Confirming will permanently delete this space and associated data. This action cannot be undone.",
"edit_space_no_member_in_space_text": "No members in {spaceName}",
"@edit_space_no_member_in_space_text": {
"placeholders": {
"spaceName": {
"type": "String"
}
}
}
},
"edit_space_leave_space_alert_confirm_button_text": "Leave",

"contact_support_title": "Contact support",
"contact_support_title_field": "Title",
"contact_support_description_title": "Description",
"contact_support_attachment": "Attachment (if any)",
"contact_support_submit_title": "Submit"
}
24 changes: 24 additions & 0 deletions app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,10 @@ PODS:
- Firebase/Firestore (= 10.25.0)
- firebase_core
- Flutter
- cloud_functions (4.7.6):
- Firebase/Functions (= 10.25.0)
- firebase_core
- Flutter
- device_info_plus (0.0.1):
- Flutter
- Firebase/Auth (10.25.0):
Expand All @@ -972,6 +976,9 @@ PODS:
- Firebase/Firestore (10.25.0):
- Firebase/CoreOnly
- FirebaseFirestore (~> 10.25.0)
- Firebase/Functions (10.25.0):
- Firebase/CoreOnly
- FirebaseFunctions (~> 10.25.0)
- Firebase/Storage (10.25.0):
- Firebase/CoreOnly
- FirebaseStorage (~> 10.25.0)
Expand Down Expand Up @@ -1023,6 +1030,15 @@ PODS:
- gRPC-Core (~> 1.62.0)
- leveldb-library (~> 1.22)
- nanopb (< 2.30911.0, >= 2.30908.0)
- FirebaseFunctions (10.25.0):
- FirebaseAppCheckInterop (~> 10.10)
- FirebaseAuthInterop (~> 10.25)
- FirebaseCore (~> 10.0)
- FirebaseCoreExtension (~> 10.0)
- FirebaseMessagingInterop (~> 10.0)
- FirebaseSharedSwift (~> 10.0)
- GTMSessionFetcher/Core (< 4.0, >= 2.1)
- FirebaseMessagingInterop (10.27.0)
- FirebaseSharedSwift (10.27.0)
- FirebaseStorage (10.25.0):
- FirebaseAppCheckInterop (~> 10.0)
Expand Down Expand Up @@ -1182,6 +1198,7 @@ PODS:

DEPENDENCIES:
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
- cloud_functions (from `.symlinks/plugins/cloud_functions/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
Expand Down Expand Up @@ -1211,6 +1228,8 @@ SPEC REPOS:
- FirebaseCoreInternal
- FirebaseFirestore
- FirebaseFirestoreInternal
- FirebaseFunctions
- FirebaseMessagingInterop
- FirebaseSharedSwift
- FirebaseStorage
- GoogleSignIn
Expand All @@ -1228,6 +1247,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
cloud_firestore:
:path: ".symlinks/plugins/cloud_firestore/ios"
cloud_functions:
:path: ".symlinks/plugins/cloud_functions/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
firebase_auth:
Expand Down Expand Up @@ -1262,6 +1283,7 @@ SPEC CHECKSUMS:
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
cloud_firestore: 003d53b6b8b392600b7769acf9421cc4f23e5911
cloud_functions: 1a28a0b6ec4caf864692e54e1f5c1934431510c8
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
Firebase: 0312a2352584f782ea56f66d91606891d4607f06
firebase_auth: 5719ddc9f654b813405899480e84971bd8e61235
Expand All @@ -1275,6 +1297,8 @@ SPEC CHECKSUMS:
FirebaseCoreInternal: 4b297a2d56063dbea2c1d0d04222d44a8d058862
FirebaseFirestore: 977ccc27a3caa5d68279f209c3b0450f85b9dc5f
FirebaseFirestoreInternal: 04b8afa77b4e5b84e86ab5ad985193e9573239fa
FirebaseFunctions: 93b1c7f352aec3922925a0e856cb1d6a09197083
FirebaseMessagingInterop: 8e911a5387664e849d316c5cd257c33fa1d37529
FirebaseSharedSwift: a03fe7a59ee646fef71099a887f774fe25d745b8
FirebaseStorage: 44f4e25073f6fa0d4d8c09f5bec299ee9e4eb985
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
Expand Down
4 changes: 4 additions & 0 deletions app/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,9 @@
</array>
</dict>
</array>
<key>NSPhotoLibraryUsageDescription</key>
<string>App requires permission to access photos</string>
<key>NSCameraUsageDescription</key>
<string>App requires permission to access camera</string>
</dict>
</plist>
9 changes: 9 additions & 0 deletions app/lib/ui/app_route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import 'package:yourspace_flutter/ui/flow/auth/sign_in/phone/verification/phone_verification_screen.dart';
import 'package:yourspace_flutter/ui/flow/onboard/pick_name_screen.dart';
import 'package:yourspace_flutter/ui/flow/setting/contact_support/contact_support_screen.dart';
import 'package:yourspace_flutter/ui/flow/setting/profile/profile_screen.dart';
import 'package:yourspace_flutter/ui/flow/setting/setting_screen.dart';
import 'package:yourspace_flutter/ui/flow/setting/space/edit_space_screen.dart';
Expand All @@ -22,6 +23,7 @@ class AppRoute {
static const pathSetting = '/setting';
static const pathProfile = '/profile';
static const pathEditSpace = '/space';
static const pathContactSupport = '/contact-support';

final String path;
final String? name;
Expand Down Expand Up @@ -152,6 +154,9 @@ class AppRoute {
);
}

static AppRoute get contactSupport =>
AppRoute(pathContactSupport, builder: (_) => const ContactSupportScreen());

static final routes = [
GoRoute(
path: intro.path,
Expand Down Expand Up @@ -216,6 +221,10 @@ class AppRoute {
path: pathEditSpace,
builder: (context, state) => state.widget(context),
),
GoRoute(
path: pathContactSupport,
builder: (context, state) => state.widget(context),
),
];
}

Expand Down
227 changes: 227 additions & 0 deletions app/lib/ui/flow/setting/contact_support/contact_support_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:style/animation/on_tap_scale.dart';
import 'package:style/button/action_button.dart';
import 'package:style/button/bottom_sticky_overlay.dart';
import 'package:style/button/primary_button.dart';
import 'package:style/extenstions/context_extenstions.dart';
import 'package:style/indicator/progress_indicator.dart';
import 'package:style/text/app_text_dart.dart';
import 'package:style/text/app_text_field.dart';
import 'package:yourspace_flutter/domain/extenstions/context_extenstions.dart';
import 'package:yourspace_flutter/ui/flow/setting/contact_support/contact_support_view_model.dart';

import '../../../components/app_page.dart';

class ContactSupportScreen extends ConsumerStatefulWidget {
const ContactSupportScreen({super.key});

@override
ConsumerState<ContactSupportScreen> createState() =>
_ContactSupportScreenState();
}

class _ContactSupportScreenState extends ConsumerState<ContactSupportScreen> {
late ContactSupportViewNotifier notifier;

@override
Widget build(BuildContext context) {
notifier = ref.watch(contactSupportViewStateProvider.notifier);
_observePop();

return AppPage(
title: context.l10n.contact_support_title,
body: _body(context),
);
}

Widget _body(BuildContext context) {
final state = ref.watch(contactSupportViewStateProvider);

return Builder(
builder: (context) => Stack(
children: [
ListView(
padding: MediaQuery.of(context).padding +
const EdgeInsets.all(16) +
BottomStickyOverlay.padding,
children: [
_titleView(
context: context,
controller: state.title,
),
const SizedBox(height: 40),
_descriptionView(
context: context,
controller: state.description,
),
const SizedBox(height: 40),
_attachmentButton(
context: context,
onAttachmentTap: () => notifier.pickAttachments(),
),
const SizedBox(height: 16),
_attachmentList(context, state),
],
),
_submitButton(context, state),
],
),
);
}

Widget _attachmentList(BuildContext context, ContactSupportViewState state) {
return ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 12),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.attachments.length,
itemBuilder: (context, index) => _attachmentItem(
context: context,
path: state.attachments[index].path,
name: state.attachments[index].path.split('/').last,
loading: false,
onCancelTap: () {
notifier.onAttachmentRemoved(index);
}),
separatorBuilder: (context, index) => const SizedBox(height: 8),
);
}

Widget _attachmentItem(
{required BuildContext context,
required String path,
required String name,
required bool loading,
required void Function() onCancelTap}) {
return Row(
children: [
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: context.colorScheme.containerNormal,
borderRadius: BorderRadius.circular(12),
),
child: _imageFileView(
iconColor: context.colorScheme.textDisabled,
path: path,
),
),
const SizedBox(width: 16),
Flexible(
child: Text(
name,
style: AppTextStyle.body2.copyWith(
color: context.colorScheme.textSecondary,
),
overflow: TextOverflow.ellipsis,
),
),
if (loading)
const Padding(
padding: EdgeInsets.only(left: 16),
child: AppProgressIndicator(size: AppProgressIndicatorSize.small),
),
actionButton(
context,
onPressed: () => onCancelTap(),
icon: Icon(
Icons.cancel_rounded,
color: context.colorScheme.textDisabled,
size: 28,
),
),
],
);
}

Widget _imageFileView({
required Color iconColor,
required String path,
}) {
return Hero(
tag: path,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
image: DecorationImage(
image: FileImage(File(path)),
fit: BoxFit.cover,
),
),
),
);
}

Widget _titleView({
required BuildContext context,
required TextEditingController controller,
}) {
return AppTextField(
label: context.l10n.contact_support_title_field,
controller: controller,
onChanged: (value) => notifier.onTitleChanged(value),
);
}

Widget _descriptionView({
required BuildContext context,
required TextEditingController controller,
}) {
return AppTextField(
label: context.l10n.contact_support_description_title,
borderType: AppTextFieldBorderType.outline,
controller: controller,
maxLines: 8,
);
}

Widget _attachmentButton({
required BuildContext context,
required VoidCallback onAttachmentTap,
}) {
return OnTapScale(
onTap: onAttachmentTap,
child: Row(
children: [
Icon(
CupertinoIcons.paperclip,
color: context.colorScheme.textPrimary,
),
Text(
context.l10n.contact_support_attachment,
style: AppTextStyle.body2.copyWith(
color: context.colorScheme.textSecondary,
),
),
],
),
);
}

Widget _submitButton(BuildContext context, ContactSupportViewState state) {
return BottomStickyOverlay(
child: PrimaryButton(
enabled: !state.submitting && state.title.text.length >= 3,
progress: state.submitting,
context.l10n.contact_support_submit_title,
onPressed: () {
notifier.submitSupportRequest();
},
),
);
}

void _observePop() {
ref.listen(contactSupportViewStateProvider.select((state) => state.requestSent), (previous, next) {
if (next) {
context.pop();
}
});
}
}
Loading

0 comments on commit 1ff2a77

Please sign in to comment.