mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 02:32:11 +00:00
improve faq
This commit is contained in:
parent
4a8fbdce28
commit
81370d27a9
6 changed files with 91 additions and 22 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit 24d048b4abbe5c266b09965cc6f3ebdf83f97855
|
Subproject commit 4e6d3eb9b7602030bf3d4b6dcb92ecc0f23de386
|
||||||
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:twonly/src/visual/views/settings/help/faq/faq_markdown.view.dart';
|
||||||
|
|
||||||
class FaqView extends StatefulWidget {
|
class FaqView extends StatefulWidget {
|
||||||
const FaqView({super.key});
|
const FaqView({super.key});
|
||||||
|
|
@ -19,7 +21,7 @@ class _FaqViewState extends State<FaqView> {
|
||||||
Map<String, dynamic>? _faqData;
|
Map<String, dynamic>? _faqData;
|
||||||
String? _locale;
|
String? _locale;
|
||||||
late String domain;
|
late String domain;
|
||||||
bool noInternet = false;
|
bool _noInternet = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -29,30 +31,40 @@ class _FaqViewState extends State<FaqView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchFAQData() async {
|
Future<void> _fetchFAQData() async {
|
||||||
|
final cacheFile = File('${AppEnvironment.cacheDir}/faq.json');
|
||||||
try {
|
try {
|
||||||
final response = await http.get(Uri.parse('$domain/faq.json'));
|
final response = await http.get(Uri.parse('$domain/faq.json'));
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
final jsonData = utf8.decode(response.bodyBytes);
|
||||||
setState(() {
|
setState(() {
|
||||||
_faqData =
|
_faqData = json.decode(jsonData) as Map<String, dynamic>?;
|
||||||
json.decode(utf8.decode(response.bodyBytes))
|
|
||||||
as Map<String, dynamic>?;
|
_noInternet = false;
|
||||||
noInternet = false;
|
|
||||||
});
|
});
|
||||||
|
cacheFile.writeAsStringSync(jsonData);
|
||||||
} else {
|
} else {
|
||||||
Log.error('FAQ got ${response.statusCode}');
|
Log.error('FAQ got ${response.statusCode}');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
setState(() {
|
setState(() {
|
||||||
noInternet = true;
|
_noInternet = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (_noInternet && cacheFile.existsSync()) {
|
||||||
|
final jsonData = cacheFile.readAsStringSync();
|
||||||
|
setState(() {
|
||||||
|
_faqData = json.decode(jsonData) as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
_noInternet = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (noInternet) {
|
if (_noInternet) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.lang.settingsHelpFAQ),
|
title: Text(context.lang.settingsHelpFAQ),
|
||||||
|
|
@ -73,26 +85,39 @@ class _FaqViewState extends State<FaqView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final faq = _faqData![_locale ?? 'en'] as Map;
|
final faq = _faqData![_locale ?? 'en'] as Map;
|
||||||
|
final sortedCategories = faq.entries.toList()
|
||||||
|
..sort((a, b) {
|
||||||
|
final aPriority = (a.value['meta']['priority'] as num? ?? 0).toInt();
|
||||||
|
final bPriority = (b.value['meta']['priority'] as num? ?? 0).toInt();
|
||||||
|
return bPriority.compareTo(aPriority);
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.lang.settingsHelpFAQ),
|
title: Text(context.lang.settingsHelpFAQ),
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: ListView.builder(
|
||||||
itemCount: faq.keys.length,
|
itemCount: sortedCategories.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final category = faq.keys.elementAt(index);
|
final categoryData = sortedCategories[index].value;
|
||||||
final categoryData = faq[category];
|
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
child: ExpansionTile(
|
child: ExpansionTile(
|
||||||
title: Text(categoryData['meta']['title'] as String),
|
title: Text(categoryData['meta']['title'] as String),
|
||||||
subtitle: Text(categoryData['meta']['desc'] as String),
|
subtitle: Text(categoryData['meta']['desc'] as String),
|
||||||
|
shape: const RoundedRectangleBorder(),
|
||||||
|
backgroundColor: context.color.surfaceContainer,
|
||||||
|
collapsedShape: const RoundedRectangleBorder(),
|
||||||
children:
|
children:
|
||||||
categoryData['questions'].map<Widget>((question) {
|
categoryData['questions'].map<Widget>((question) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(question['title'] as String),
|
title: Text(question['title'] as String),
|
||||||
onTap: () => _launchURL(question['path'] as String),
|
onTap: () => context.navPush(
|
||||||
|
FaqMarkdownView(
|
||||||
|
markdown: question['body'] as String,
|
||||||
|
title: question['title'] as String,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}).toList()
|
}).toList()
|
||||||
as List<Widget>,
|
as List<Widget>,
|
||||||
|
|
@ -102,12 +127,4 @@ class _FaqViewState extends State<FaqView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _launchURL(String path) async {
|
|
||||||
try {
|
|
||||||
await launchUrl(Uri.parse('$domain$path'));
|
|
||||||
} catch (e) {
|
|
||||||
Log.error('Could not launch $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
|
||||||
|
class FaqMarkdownView extends StatelessWidget {
|
||||||
|
const FaqMarkdownView({
|
||||||
|
required this.markdown,
|
||||||
|
required this.title,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String markdown;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(substringBy(title, 30)),
|
||||||
|
),
|
||||||
|
body: Markdown(
|
||||||
|
data: markdown,
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 16,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
bottom: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,9 @@ import 'dart:async';
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/user_discovery/types.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/user_discovery/types.pb.dart';
|
||||||
|
|
@ -160,7 +162,7 @@ class _UserDiscoveryEnabledCompState extends State<UserDiscoveryEnabledComp> {
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
context.lang.userDiscoveryEnabledFaq,
|
context.lang.userDiscoveryEnabledFaq,
|
||||||
),
|
),
|
||||||
// onTap: _disableUserDiscovery,
|
onTap: () => context.push(Routes.settingsHelpFaq),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|
|
||||||
15
pubspec.lock
15
pubspec.lock
|
|
@ -760,6 +760,13 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_markdown_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "dependencies/flutter_markdown_plus"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "1.0.7"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1261,6 +1268,14 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "3.3.2"
|
version: "3.3.2"
|
||||||
|
markdown:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: markdown
|
||||||
|
sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.3.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ dependencies:
|
||||||
hand_signature: ^3.0.3
|
hand_signature: ^3.0.3
|
||||||
flutter_sharing_intent: ^2.0.4
|
flutter_sharing_intent: ^2.0.4
|
||||||
no_screenshot: ^0.3.1
|
no_screenshot: ^0.3.1
|
||||||
|
flutter_markdown_plus: ^1.0.7
|
||||||
|
|
||||||
# With high download. (But should be checked nonetheless.)
|
# With high download. (But should be checked nonetheless.)
|
||||||
app_links: ^7.0.0 # 1.6 mio
|
app_links: ^7.0.0 # 1.6 mio
|
||||||
|
|
@ -153,6 +154,8 @@ dependency_overrides:
|
||||||
path: ./dependencies/qr_flutter
|
path: ./dependencies/qr_flutter
|
||||||
no_screenshot:
|
no_screenshot:
|
||||||
path: ./dependencies/no_screenshot
|
path: ./dependencies/no_screenshot
|
||||||
|
flutter_markdown_plus:
|
||||||
|
path: ./dependencies/flutter_markdown_plus
|
||||||
camera_android_camerax:
|
camera_android_camerax:
|
||||||
# path: ../flutter-packages/packages/camera/camera_android_camerax
|
# path: ../flutter-packages/packages/camera/camera_android_camerax
|
||||||
git:
|
git:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue