Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Feedback Form #158

Merged
merged 8 commits into from
Nov 24, 2023
Merged
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ migrate_working_dir/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
.vscode/

# Flutter/Dart/Pub related
**/doc/api/
8 changes: 8 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -7,3 +7,11 @@ linter:
analyzer:
errors:
invalid_annotation_target: ignore
exclude:
- '**/*.g.dart'
- lib/base/networking/apis/tumdev/campus_backend.pb.dart
- lib/base/networking/apis/tumdev/campus_backend.pbenum.dart
- lib/base/networking/apis/tumdev/campus_backend.pbgrpc.dart
- lib/base/networking/apis/tumdev/campus_backend.pbjson.dart
- lib/base/networking/apis/google/protobuf/empty.pbjson.dart
- lib/base/networking/apis/google/protobuf/timestamp.pb.dart
2 changes: 1 addition & 1 deletion ios/build/.last_build_id
Original file line number Diff line number Diff line change
@@ -1 +1 @@
d4fe773e5af8aea054184ce4dc69442d
4988aa35441b901c4e8c4b76a3fe883f
4 changes: 2 additions & 2 deletions l10n.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
arb-dir: lib/base/localization/l10n
arb-dir: lib/base/localization/
template-arb-file: app_en.arb
untranslated-messages-file: lib/base/localization/l10n/translation_todo.json
untranslated-messages-file: lib/base/localization/translation_todo.json
output-localization-file: app_localizations.dart
File renamed without changes.
6 changes: 3 additions & 3 deletions lib/base/helpers/placeholder_text.dart
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ class PlaceholderText extends StatelessWidget {

@override
Widget build(BuildContext context) {
var textSize = _calcTextSize();
var textSize = _calcTextSize(context);
return Container(
height: textSize.height,
width: textSize.width,
@@ -17,11 +17,11 @@ class PlaceholderText extends StatelessWidget {
}

/// found on: https://stackoverflow.com/a/63133637
Size _calcTextSize() {
Size _calcTextSize(BuildContext context) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr,
textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
textScaleFactor: MediaQuery.textScaleFactorOf(context),
)..layout();
return textPainter.size;
}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
"identification":"Identifikation",
"places":"Orte",
"settings":"Einstellungen",
"settingsAndFeedback":"Einstellungen & Feedback",
"generalSettings":"Allgemeine Einstellungen",
"comingSoon":"demnächst verfügbar",
"appearance":"Darstellung",
@@ -200,7 +201,7 @@
},
"noRoomsFound":"Keine Räume gefunden",
"noMealPlanFound":"Kein Essensplan gefunden",
"mealPlan": "Essensplan",
"mealPlan":"Essensplan",
"noEntriesFoundSearch":"Keine {searchCategory} gefunden",
"@noEntriesFoundSearch":{
"description":"Title of Search Category",
@@ -251,5 +252,17 @@
}
},
"closed":"Geschlossen",
"closedToday":"Heute geschlossen"
"closedToday":"Heute geschlossen",
"submitFeedback":"Feedback einreichen",
"yourEmailAddress":"Deine E-Mail-Adresse",
"message":"Nachricht",
"shareLocation":"Aktuellen Standort teilen",
"shareDeviceInformation":"Informationen über das Gerät teilen",
"submit":"Senden",
"yourMessage":"Deine Nachricht...",
"yourEmail":"deineemail@tum.de",
"invalidEmail":"Ungültige E-Mail-Adresse",
"invalidMessage":"Ungültige Nachricht",
"unableToSend":"Nachricht kann nicht gesendet werden!",
"successfullySent":"Nachricht erfolgreich gesendet!\n Danke für Dein Feedback!"
}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
"identification":"Identification",
"places":"Places",
"settings":"Settings",
"settingsAndFeedback":"Settings & Feedback",
"generalSettings":"General Settings",
"comingSoon":"Coming Soon",
"language":"Language",
@@ -200,7 +201,7 @@
},
"noRoomsFound":"No Rooms Found",
"noMealPlanFound":"No Meal Plan Found",
"mealPlan": "Meal Plan",
"mealPlan":"Meal Plan",
"noEntriesFoundSearch":"No {searchCategory} Found",
"@noEntriesFoundSearch":{
"description":"Title of Search Category",
@@ -251,5 +252,17 @@
}
},
"closed":"Closed",
"closedToday":"Closed Today"
"closedToday":"Closed Today",
"submitFeedback":"Submit Feedback",
"yourEmailAddress":"Your Email Address",
"message":"Message",
"shareLocation":"Share Current Location",
"shareDeviceInformation":"Share Device's Information",
"submit":"Submit",
"yourMessage":"Your Message",
"yourEmail":"youremail@tum.de",
"invalidEmail":"Invalid Email Address",
"invalidMessage":"Invalid Message",
"unableToSend":"Unable to Send Message!",
"successfullySent":"Message Successfully Sent!\n Thanks for Your Feedback!"
}
File renamed without changes.
40 changes: 5 additions & 35 deletions lib/base/networking/apis/tumdev/cache_interceptor.dart
Original file line number Diff line number Diff line change
@@ -30,37 +30,7 @@ class CacheInterceptor implements ClientInterceptor {
CallOptions options,
ClientStreamingInvoker<Q, R> invoker,
) {
final key = "${method.path}?${requests.toString()}";
final (bool, dynamic) cachedResponse;
if (kIsWeb) {
cachedResponse = memCacheStore.get(key);
} else {
cachedResponse = hiveCacheStore.get(key);
}
final factory = _getFactory<R>();
if (cachedResponse.$1 && cachedResponse.$2 != null && factory != null) {
final data = factory(cachedResponse.$2!.data);
return ResponseStream<R>(
ClientCall(
ClientMethod<Q, R>(method.path, (value) => [], (value) => data),
requests,
options,
null,
true,
),
);
}

final response = invoker(method, requests, options);
response.listen((data) {
if (kIsWeb) {
memCacheStore.put(key, _convertToBuffer<R>(data));
} else {
hiveCacheStore.put(key, _convertToBuffer<R>(data));
}
});

return response;
return invoker(method, requests, options);
}

@override
@@ -107,10 +77,10 @@ class CacheInterceptor implements ClientInterceptor {
// TODO: figure out nicer solution or add missing classes
Uint8List? _convertToBuffer<R>(R data) {
switch (R) {
case ListNewsReply:
case ListNewsReply _:
final listData = data as ListNewsReply;
return listData.writeToBuffer();
case ListMoviesReply:
case ListMoviesReply _:
final movieData = data as ListMoviesReply;
return movieData.writeToBuffer();
default:
@@ -121,12 +91,12 @@ class CacheInterceptor implements ClientInterceptor {
// TODO: figure out nicer solution or add missing classes
R Function(List<int>, [ExtensionRegistry])? _getFactory<R>() {
switch (R) {
case ListNewsReply:
case ListNewsReply _:
return ListNewsReply.fromBuffer as R Function(
List<int>, [
ExtensionRegistry,
]);
case ListMoviesReply:
case ListMoviesReply _:
return ListMoviesReply.fromBuffer as R Function(
List<int>, [
ExtensionRegistry,
14 changes: 14 additions & 0 deletions lib/feedbackComponent/services/feedback_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'dart:async';

import 'package:campus_flutter/base/networking/apis/tumdev/cached_client.dart';
import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pbgrpc.dart';
import 'package:campus_flutter/providers_get_it.dart';

class FeedbackService {
static Future<CreateFeedbackReply> sendFeedback(
CreateFeedbackRequest createFeedbackRequest,
) async {
CachedCampusClient mainApi = getIt<CachedCampusClient>();
return await mainApi.createFeedback(Stream.value(createFeedbackRequest));
}
}
113 changes: 113 additions & 0 deletions lib/feedbackComponent/viewModels/feedback_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import 'dart:developer';

import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pb.dart';
import 'package:campus_flutter/base/services/location_service.dart';
import 'package:campus_flutter/feedbackComponent/services/feedback_service.dart';
import 'package:campus_flutter/providers_get_it.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:rxdart/rxdart.dart';

class FeedbackViewModel {
BehaviorSubject<bool> shareLocation = BehaviorSubject.seeded(false);
BehaviorSubject<bool> shareDeviceInfos = BehaviorSubject.seeded(false);
BehaviorSubject<bool> activeButton = BehaviorSubject.seeded(false);
BehaviorSubject<bool?> validEmail = BehaviorSubject.seeded(null);
BehaviorSubject<bool?> validMessage = BehaviorSubject.seeded(null);
BehaviorSubject<bool?> successfullySent = BehaviorSubject.seeded(null);
final TextEditingController emailAddress = TextEditingController();
final TextEditingController message = TextEditingController();

final Ref ref;

FeedbackViewModel(this.ref);

initForm() {
final email = ref.read(profileDetailsViewModel).personDetails.value?.email;
if (email != null) {
emailAddress.text = email;
validEmail.add(true);
}
}

Future<void> sendFeedBack() async {
Position? position;
if (shareLocation.value) {
position = await LocationService.getLastKnown();
}

String? deviceInfos;
if (shareDeviceInfos.value) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
final baseInfo = await deviceInfo.deviceInfo;
deviceInfos =
"${baseInfo.data["machine"] ?? baseInfo.data["model"]} - ${baseInfo.data["systemVersion"]}";
}

PackageInfo packageInfo = await PackageInfo.fromPlatform();
String packageInfos = packageInfo.version;

final feedback = CreateFeedbackRequest(
recipient: CreateFeedbackRequest_Recipient.TUM_DEV,
fromEmail: emailAddress.text,
message: message.text,
location: Coordinate(
latitude: position?.latitude,
longitude: position?.longitude,
),
osVersion: deviceInfos,
appVersion: packageInfos,
);

FeedbackService.sendFeedback(feedback).then(
(value) => successfullySent.add(true),
onError: (error) {
final errors = error as StateError;
log(errors.stackTrace.toString());
successfullySent.addError(error);
},
);
}

void checkMessageValidity() {
if (message.value.text.isNotEmpty) {
validMessage.add(true);
} else {
validMessage.add(false);
}
checkButton();
}

void checkEmailValidity() {
final RegExp validEmailRegex = RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+",
);
if (emailAddress.value.text.isNotEmpty) {
if (validEmailRegex.hasMatch(emailAddress.value.text)) {
validEmail.add(true);
} else {
validEmail.add(false);
}
}
checkButton();
}

void checkButton() {
activeButton
.add((validEmail.value ?? false) && (validMessage.value ?? false));
}

void clearForm() {
emailAddress.text = "";
message.text = "";
shareLocation.add(false);
shareDeviceInfos.add(false);
activeButton.add(false);
validMessage.add(null);
validEmail.add(null);
successfullySent.add(null);
}
}
31 changes: 31 additions & 0 deletions lib/feedbackComponent/views/feedback_checkmark_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rxdart/rxdart.dart';

class FeedbackCheckMarkView extends ConsumerWidget {
const FeedbackCheckMarkView({
super.key,
required this.text,
required this.isChecked,
});

final String text;
final BehaviorSubject<bool> isChecked;

@override
Widget build(BuildContext context, WidgetRef ref) {
return StreamBuilder(
stream: isChecked,
builder: (context, snapshot) {
return ListTile(
dense: true,
title: Text(text),
trailing: Checkbox(
value: snapshot.data ?? false,
onChanged: (newValue) => isChecked.add(newValue ?? false),
),
);
},
);
}
}
Loading