Skip to content

Commit

Permalink
Merge pull request #480 from lamarios/feature/feedback
Browse files Browse the repository at this point in the history
add feedback form and handling
  • Loading branch information
lamarios authored Feb 14, 2024
2 parents 7887701 + fb89e03 commit 4816722
Show file tree
Hide file tree
Showing 8 changed files with 1,469 additions and 1,348 deletions.
2,662 changes: 1,339 additions & 1,323 deletions lib/l10n/app_en.arb

Large diffs are not rendered by default.

49 changes: 26 additions & 23 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';

import 'package:dynamic_color/dynamic_color.dart';
import 'package:feedback/feedback.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand Down Expand Up @@ -80,29 +81,31 @@ Future<void> main() async {
}

isTv = await isDeviceTv();
runApp(MultiBlocProvider(providers: [
BlocProvider(
create: (context) {
return AppCubit(AppState(0, null, HomeLayout()));
},
),
BlocProvider(
create: (context) {
var settingsCubit =
SettingsCubit(SettingsState.init(), context.read<AppCubit>());
configureBackgroundService(settingsCubit);
return settingsCubit;
},
),
BlocProvider(
create: (context) =>
PlayerCubit(PlayerState.init(null), context.read<SettingsCubit>()),
),
BlocProvider(
create: (context) => DownloadManagerCubit(
const DownloadManagerState(), context.read<PlayerCubit>()),
)
], child: const MyApp()));
runApp(BetterFeedback(
child: MultiBlocProvider(providers: [
BlocProvider(
create: (context) {
return AppCubit(AppState(0, null, HomeLayout()));
},
),
BlocProvider(
create: (context) {
var settingsCubit =
SettingsCubit(SettingsState.init(), context.read<AppCubit>());
configureBackgroundService(settingsCubit);
return settingsCubit;
},
),
BlocProvider(
create: (context) =>
PlayerCubit(PlayerState.init(null), context.read<SettingsCubit>()),
),
BlocProvider(
create: (context) => DownloadManagerCubit(
const DownloadManagerState(), context.read<PlayerCubit>()),
)
], child: const MyApp()),
));
}

late ColorScheme darkColorScheme;
Expand Down
31 changes: 31 additions & 0 deletions lib/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'package:invidious/settings/models/errors/cannot_add_server_error.dart';
import 'package:invidious/settings/models/errors/invidious_service_error.dart';
import 'package:invidious/settings/models/errors/missing_software_key.dart';
import 'package:invidious/settings/models/errors/unreacheable_server.dart';
import 'package:invidious/utils/models/imgur_error.dart';
import 'package:invidious/utils/video_post_processing.dart';
import 'package:invidious/videos/models/db/progress.dart';
import 'package:invidious/videos/models/dearrow.dart';
Expand Down Expand Up @@ -69,6 +70,9 @@ const urlGetPublicPlaylist = '/api/v1/playlists/:id';
const urlGetDislikes = 'https://returnyoutubedislikeapi.com/votes?videoId=';
const urlGetClearHistory = '/api/v1/auth/history';
const urlAddDeleteHistory = '/api/v1/auth/history/:id';
const urlImgurScreenshotUpload = 'https://api.imgur.com/3/image';

const imgurClientId = 'Client-ID 2cfbc27ce77879d';

const maxPing = 9007199254740991;

Expand Down Expand Up @@ -802,4 +806,31 @@ class Service {
final response = await http.get(uri);
return Dislike.fromJson(handleResponse(response));
}

Future<String> uploadImageToImgur(String base64Image) async {
Uri uri = Uri.parse(urlImgurScreenshotUpload);
final headers = {'Authorization': imgurClientId};

final data = <String, String>{'image': base64Image};

final response = await http.post(uri, headers: headers, body: data);
if (response.statusCode != 200) {
throw ImgurError("Non 200 response from Imgur (${response.statusCode})");
} else {
var body = utf8.decode(response.bodyBytes);
final Map<String, dynamic> decoded = jsonDecode(body);

if (decoded.containsKey('data')) {
final Map<String, dynamic> data = decoded['data'];

if (data.containsKey('link')) {
return data['link'].toString();
} else {
throw ImgurError("Response does not containt 'link' key");
}
} else {
throw ImgurError("Response does not containt 'data' key");
}
}
}
}
35 changes: 34 additions & 1 deletion lib/settings/states/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import 'dart:convert';

import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:bloc/bloc.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:feedback/feedback.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:invidious/app/states/app.dart';
import 'package:invidious/workmanager.dart';
import 'package:locale_names/locale_names.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';

import '../../globals.dart';
import '../../home/models/db/home_layout.dart';
Expand All @@ -32,7 +36,7 @@ enum EnableBackGroundNotificationResponse {
needBatteryOptimization;
}

// var _log = Logger('SettingsController');
var _log = Logger('SettingsController');

class SettingsCubit extends Cubit<SettingsState> {
final AppCubit appCubit;
Expand Down Expand Up @@ -431,6 +435,35 @@ class SettingsCubit extends Cubit<SettingsState> {
subscriptionNotifications:
await fileDb.getSubscriptionNotifications()));
}

sendFeedBack(UserFeedback feedback) async {
try {
final deviceInfo = await DeviceInfoPlugin().androidInfo;
final androidInfo = deviceInfo.version;

final firstLine = feedback.text.split('\n')[0];

String body =
'**Runtime info:**\n[Device] Manufacturer: ${deviceInfo.manufacturer} / Brand: ${deviceInfo.brand} / Model: ${deviceInfo.model} / Hardware: ${deviceInfo.hardware}';
body +=
'\n[Android] Version: ${androidInfo.release}.${androidInfo.incremental} (${androidInfo.codename})';
body +=
'\n[Clipious] Version: ${state.packageInfo.version} Build: ${state.packageInfo.buildNumber}';

body += '\n\n\n**Feedback:**\n${feedback.text}\n\n\n';

String screenshotUrl =
await service.uploadImageToImgur(base64Encode(feedback.screenshot));
body += '\n**Screenshot:**\n![app screenshot]($screenshotUrl)';

final url =
'https://github.com/lamarios/clipious/issues/new?title=${Uri.encodeComponent('[App Feedback] $firstLine')}&body=${Uri.encodeComponent(body)}';
await launchUrl(Uri.parse(url));
} catch (err) {
_log.severe("Issue while submitting feedback", err);
rethrow;
}
}
}

@freezed
Expand Down
24 changes: 24 additions & 0 deletions lib/settings/views/screens/settings.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:auto_route/auto_route.dart';
import 'package:feedback/feedback.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:invidious/app/states/app.dart';
import 'package:invidious/router.dart';
import 'package:invidious/utils.dart';
import 'package:invidious/utils/models/imgur_error.dart';
import 'package:invidious/utils/views/components/app_icon.dart';
import 'package:settings_ui/settings_ui.dart';

Expand Down Expand Up @@ -188,6 +191,27 @@ class SettingsScreen extends StatelessWidget {
title: Text(locals.copySettingsAsJson),
description: Text(locals.copySettingsAsJsonDescription),
onPressed: cubit.copySettingsAsJson,
),
SettingsTile(
leading: const Icon(Icons.feedback_outlined),
title: Text(locals.submitFeedback),
description: Text(locals.submitFeedbackDescription),
onPressed: (context) {
okCancelDialog(
context,
locals.submitFeedback,
locals.feedbackDisclaimer,
() => BetterFeedback.of(context).show((feedback) {
try {
cubit.sendFeedBack(feedback);
} catch (err) {
showAlertDialog(context, locals.error, [
Text(locals.feedbackScreenshotError),
if (err is ImgurError) Text(err.error)
]);
}
}));
},
)
])
],
Expand Down
5 changes: 5 additions & 0 deletions lib/utils/models/imgur_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ImgurError extends Error {
String error;

ImgurError(this.error);
}
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
feedback:
dependency: "direct main"
description:
name: feedback
sha256: "3cb138a56a3f3914dd38471479c351ce6df377abb29c5ebe4042be5481fee271"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
ffi:
dependency: transitive
description:
Expand Down
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.18.0+4044
version: 1.19.0+4045

environment:
sdk: '>=3.0.0 <4.0.0'
Expand Down Expand Up @@ -84,6 +84,7 @@ dependencies:
sembast_sqflite: 2.2.0
sembast: 3.5.0+1
sqflite: 2.3.0
feedback: 3.0.0
dependency_overrides:
intl: 0.19.0
meta: 1.11.0
Expand Down

0 comments on commit 4816722

Please sign in to comment.