Add opening tags in app feature

merge-requests/67/head
Hank Grabowski 2024-12-23 17:21:48 -05:00
rodzic d891d6fc53
commit 4f882df541
8 zmienionych plików z 109 dodań i 11 usunięć

Wyświetl plik

@ -20,9 +20,11 @@ import '../../riverpod_controllers/settings_services.dart';
import '../../riverpod_controllers/timeline_entry_filter_services.dart';
import '../../riverpod_controllers/timeline_entry_services.dart';
import '../../riverpod_controllers/timeline_services.dart';
import '../../routes.dart';
import '../../utils/clipboard_utils.dart';
import '../../utils/filter_runner.dart';
import '../../utils/html_to_edit_text_helper.dart';
import '../../utils/network_utils.dart';
import '../../utils/responsive_sizes_calculator.dart';
import '../../utils/snackbar_builder.dart';
import '../../utils/url_opening_utils.dart';
@ -198,7 +200,7 @@ class _StatusControlState extends ConsumerState<FlattenedTreeEntryControl> {
child: Text(
'Content Summary: ${entry.spoilerText} (Click to ${showContent ? "Hide" : "Show"})')),
if (showContent || !showSpoilerControl) ...[
buildContentField(context, entry),
buildContentField(context, profile, entry),
const VerticalPadding(
height: 5,
),
@ -245,11 +247,28 @@ class _StatusControlState extends ConsumerState<FlattenedTreeEntryControl> {
);
}
Widget buildContentField(BuildContext context, TimelineEntry entry) {
Widget buildContentField(
BuildContext context, Profile profile, TimelineEntry entry) {
return HtmlTextViewerControl(
content: entry.body,
onTapUrl: (url) async =>
await openUrlStringInSystembrowser(context, url, 'link'),
onTapUrl: (url) async {
await processUrlStringForTag(url).withResultAsync((tagName) async {
final openInApp = ref.read(openTagsInAppProvider);
if (openInApp) {
await context.push('${ScreenPaths.tagView}/$tagName');
return;
}
final usersTagSearchPage =
generateTagSearchFromProfile(profile, tagName);
await openUrlStringInSystembrowser(
context, usersTagSearchPage, 'link');
}).withErrorAsync((_) async {
await openUrlStringInSystembrowser(context, url, 'link');
});
return true;
},
);
}

Wyświetl plik

@ -4,6 +4,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
const deepLinkScheme = 'relatica';
const macOsGroupId = 'T69YZGT58U.relatica';
String randomId() => const Uuid().v4().toString();

Wyświetl plik

@ -232,3 +232,19 @@ class ColorBlindnessTestingModeSetting
);
}
}
@Riverpod(keepAlive: true)
class OpenTagsInApp extends _$SpoilerHidingSetting {
static const _openTagsInAppKey = 'OpenTagsInApp';
@override
bool build() {
return ref.watch(sharedPreferencesProvider).getBool(_openTagsInAppKey) ??
true;
}
set value(bool value) {
ref.read(sharedPreferencesProvider).setBool(_openTagsInAppKey, value);
state = value;
}
}

Wyświetl plik

@ -156,5 +156,20 @@ final colorBlindnessTestingModeSettingProvider = NotifierProvider<
);
typedef _$ColorBlindnessTestingModeSetting = Notifier<ColorBlindnessType>;
String _$openTagsInAppHash() => r'4aeee38bd8de61c8fdf84cd6cb30bc61fbeeca1b';
/// See also [OpenTagsInApp].
@ProviderFor(OpenTagsInApp)
final openTagsInAppProvider = NotifierProvider<OpenTagsInApp, bool>.internal(
OpenTagsInApp.new,
name: r'openTagsInAppProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$openTagsInAppHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$OpenTagsInApp = Notifier<bool>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

Wyświetl plik

@ -45,7 +45,7 @@ class ScreenPaths {
static String gallery = '/gallery';
static String notifications = '/notifications';
static String signin = '/signin';
static String manageProfiles = '/switchProfiles';
static String manageProfiles = '/switch_profiles';
static String circleManagement = '/circle_management';
static String signup = '/signup';
static String userProfile = '/user_profile';
@ -53,8 +53,8 @@ class ScreenPaths {
static String likes = '/likes';
static String reshares = '/reshares';
static String explore = '/explore';
static String logViewer = '/logViewer';
static String tagView = '/tagView';
static String logViewer = '/log_viewer';
static String tagView = '/tag_view';
}
bool needAuthChangeInitialized = true;

Wyświetl plik

@ -34,6 +34,7 @@ class SettingsScreen extends ConsumerWidget {
_NetworkTimeoutWidget(),
_NotificationGroupingWidget(),
_SpoilerHidingWidget(),
_OpenTagsInAppWidget(),
_ThemeWidget(),
if (!kReleaseMode) _ColorBlindnessWidget(),
_ClearCachesWidget(),
@ -223,6 +224,24 @@ class _SpoilerHidingWidget extends ConsumerWidget {
}
}
class _OpenTagsInAppWidget extends ConsumerWidget {
const _OpenTagsInAppWidget();
@override
Widget build(BuildContext context, WidgetRef ref) {
final openTagsInApp = ref.watch(openTagsInAppProvider);
return ListTile(
title: const Text('Open Tags In App'),
trailing: Switch(
onChanged: (value) {
ref.read(openTagsInAppProvider.notifier).value = value;
},
value: openTagsInApp,
),
);
}
}
class _ThemeWidget extends ConsumerWidget {
const _ThemeWidget();

Wyświetl plik

@ -35,7 +35,7 @@ void _updateSwapTagLinks(Profile profile, Node node, List<String> tags) {
.firstWhere((t) => t.toLowerCase() == tagLowercase, orElse: () => '')
.isNotEmpty;
if (hasExpectedTag) {
final newTagUrl = generateTagUrlFromProfile(profile, tag);
final newTagUrl = generateTagUriFromProfile(profile, tag);
node.attributes['href'] = newTagUrl.toString();
}
}

Wyświetl plik

@ -1,5 +1,32 @@
import '../models/auth/profile.dart';
import 'package:result_monad/result_monad.dart';
Uri generateTagUrlFromProfile(Profile profile, String tag) {
return Uri.https(profile.serverName, '/search', {'tag': tag});
import '../globals.dart';
import '../models/auth/profile.dart';
import '../routes.dart';
Uri generateTagUriFromProfile(Profile profile, String tag) {
return Uri.parse('relatica://route${ScreenPaths.tagView}/$tag');
}
Result<String, bool> processUrlStringForTag(String url) {
final uri = Uri.parse(url);
if (uri.scheme == deepLinkScheme &&
uri.host == 'route' &&
uri.pathSegments.length == 2) {
final route = '/${uri.pathSegments[0]}';
final tag = uri.pathSegments[1];
if (route == ScreenPaths.tagView) {
return Result.ok(tag);
}
}
return Result.error(false);
}
String generateTagSearchFromProfile(Profile profile, String tagName) {
return Uri.https(
profile.serverName,
'/search',
{'tag': tagName},
).toString();
}