long press copy link text
This commit is contained in:
@@ -36,6 +36,13 @@ extension HitResultJson on HitResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension HitResultX on HitResult {
|
extension HitResultX on HitResult {
|
||||||
|
String? getLinkText() {
|
||||||
|
return switch (this) {
|
||||||
|
UnknownHitResult(linkText: final linkText) => linkText,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Uri? tryGetLink() {
|
Uri? tryGetLink() {
|
||||||
const maxTitleLength = 2500;
|
const maxTitleLength = 2500;
|
||||||
|
|
||||||
@@ -91,6 +98,8 @@ extension HitResultX on HitResult {
|
|||||||
|
|
||||||
bool get hasLink => tryGetLink() != null;
|
bool get hasLink => tryGetLink() != null;
|
||||||
|
|
||||||
|
bool get hasLinkText => getLinkText()?.isNotEmpty == true;
|
||||||
|
|
||||||
bool isImage() {
|
bool isImage() {
|
||||||
return (this is ImageHitResult || this is ImageSrcHitResult) && hasSrc;
|
return (this is ImageHitResult || this is ImageSrcHitResult) && hasSrc;
|
||||||
}
|
}
|
||||||
@@ -131,7 +140,10 @@ extension HitResultX on HitResult {
|
|||||||
|
|
||||||
HitResult withCleanedLink(String cleanedUrl) {
|
HitResult withCleanedLink(String cleanedUrl) {
|
||||||
return switch (this) {
|
return switch (this) {
|
||||||
UnknownHitResult() => UnknownHitResult(src: cleanedUrl),
|
UnknownHitResult(:final linkText) => UnknownHitResult(
|
||||||
|
src: cleanedUrl,
|
||||||
|
linkText: linkText,
|
||||||
|
),
|
||||||
ImageSrcHitResult(:final src) => ImageSrcHitResult(
|
ImageSrcHitResult(:final src) => ImageSrcHitResult(
|
||||||
src: src,
|
src: src,
|
||||||
uri: cleanedUrl,
|
uri: cleanedUrl,
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 Fabian Freund.
|
||||||
|
*
|
||||||
|
* This file is part of WebLibre
|
||||||
|
* (see https://weblibre.eu).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart';
|
||||||
|
|
||||||
|
class CopyLinkText extends HookConsumerWidget {
|
||||||
|
final HitResult hitResult;
|
||||||
|
|
||||||
|
const CopyLinkText({super.key, required this.hitResult});
|
||||||
|
|
||||||
|
static bool isSupported(HitResult hitResult) {
|
||||||
|
return hitResult.isUri() && hitResult.hasLinkText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(MdiIcons.textShort),
|
||||||
|
title: const Text('Copy link text'),
|
||||||
|
onTap: () async {
|
||||||
|
await hitResult.getLinkText().mapNotNull((linkText) async {
|
||||||
|
await Clipboard.setData(ClipboardData(text: linkText));
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import 'package:weblibre/features/geckoview/features/contextmenu/presentation/ca
|
|||||||
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_image.dart';
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_image.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_image_location.dart';
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_image_location.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_link.dart';
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_link.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/copy_link_text.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/launch_external.dart';
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/launch_external.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart';
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/open_in_container.dart';
|
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/open_in_container.dart';
|
||||||
@@ -112,6 +113,8 @@ class ContextMenuDialog extends HookConsumerWidget {
|
|||||||
if (showContainerUi && OpenInContainer.isSupported(effective))
|
if (showContainerUi && OpenInContainer.isSupported(effective))
|
||||||
OpenInContainer(hitResult: effective),
|
OpenInContainer(hitResult: effective),
|
||||||
if (CopyLink.isSupported(effective)) CopyLink(hitResult: effective),
|
if (CopyLink.isSupported(effective)) CopyLink(hitResult: effective),
|
||||||
|
if (CopyLinkText.isSupported(effective))
|
||||||
|
CopyLinkText(hitResult: effective),
|
||||||
if (SaveFile.isSupported(effective)) SaveFile(hitResult: effective),
|
if (SaveFile.isSupported(effective)) SaveFile(hitResult: effective),
|
||||||
if (ShareLink.isSupported(effective)) ShareLink(hitResult: effective),
|
if (ShareLink.isSupported(effective)) ShareLink(hitResult: effective),
|
||||||
if (ShareImage.isSupported(effective)) ShareImage(hitResult: effective),
|
if (ShareImage.isSupported(effective)) ShareImage(hitResult: effective),
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd
|
|||||||
is HitResult.IMAGE -> ImageHitResult(result.src, result.title)
|
is HitResult.IMAGE -> ImageHitResult(result.src, result.title)
|
||||||
is HitResult.IMAGE_SRC -> ImageSrcHitResult(result.src, result.uri)
|
is HitResult.IMAGE_SRC -> ImageSrcHitResult(result.src, result.uri)
|
||||||
is HitResult.PHONE -> PhoneHitResult(result.src)
|
is HitResult.PHONE -> PhoneHitResult(result.src)
|
||||||
is HitResult.UNKNOWN -> UnknownHitResult(result.src)
|
is HitResult.UNKNOWN -> UnknownHitResult(result.src, result.linkText)
|
||||||
is HitResult.VIDEO -> VideoHitResult(result.src, result.title)
|
is HitResult.VIDEO -> VideoHitResult(result.src, result.title)
|
||||||
}
|
}
|
||||||
) { _ -> }
|
) { _ -> }
|
||||||
|
|||||||
+5
-2
@@ -2468,18 +2468,21 @@ sealed class HitResult
|
|||||||
* Generated class from Pigeon that represents data sent in messages.
|
* Generated class from Pigeon that represents data sent in messages.
|
||||||
*/
|
*/
|
||||||
data class UnknownHitResult (
|
data class UnknownHitResult (
|
||||||
val src: String
|
val src: String,
|
||||||
|
val linkText: String? = null
|
||||||
) : HitResult()
|
) : HitResult()
|
||||||
{
|
{
|
||||||
companion object {
|
companion object {
|
||||||
fun fromList(pigeonVar_list: List<Any?>): UnknownHitResult {
|
fun fromList(pigeonVar_list: List<Any?>): UnknownHitResult {
|
||||||
val src = pigeonVar_list[0] as String
|
val src = pigeonVar_list[0] as String
|
||||||
return UnknownHitResult(src)
|
val linkText = pigeonVar_list[1] as String?
|
||||||
|
return UnknownHitResult(src, linkText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun toList(): List<Any?> {
|
fun toList(): List<Any?> {
|
||||||
return listOf(
|
return listOf(
|
||||||
src,
|
src,
|
||||||
|
linkText,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|||||||
@@ -3009,13 +3009,17 @@ sealed class HitResult {
|
|||||||
class UnknownHitResult extends HitResult {
|
class UnknownHitResult extends HitResult {
|
||||||
UnknownHitResult({
|
UnknownHitResult({
|
||||||
required this.src,
|
required this.src,
|
||||||
|
this.linkText,
|
||||||
});
|
});
|
||||||
|
|
||||||
String src;
|
String src;
|
||||||
|
|
||||||
|
String? linkText;
|
||||||
|
|
||||||
List<Object?> _toList() {
|
List<Object?> _toList() {
|
||||||
return <Object?>[
|
return <Object?>[
|
||||||
src,
|
src,
|
||||||
|
linkText,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3026,6 +3030,7 @@ class UnknownHitResult extends HitResult {
|
|||||||
result as List<Object?>;
|
result as List<Object?>;
|
||||||
return UnknownHitResult(
|
return UnknownHitResult(
|
||||||
src: result[0]! as String,
|
src: result[0]! as String,
|
||||||
|
linkText: result[1] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -968,8 +968,9 @@ sealed class HitResult {}
|
|||||||
/// Default type if we're unable to match the type to anything. It may or may not have a src.
|
/// Default type if we're unable to match the type to anything. It may or may not have a src.
|
||||||
class UnknownHitResult extends HitResult {
|
class UnknownHitResult extends HitResult {
|
||||||
final String src;
|
final String src;
|
||||||
|
final String? linkText;
|
||||||
|
|
||||||
UnknownHitResult(this.src);
|
UnknownHitResult(this.src, {this.linkText});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the HTML element was of type 'HTMLImageElement'.
|
/// If the HTML element was of type 'HTMLImageElement'.
|
||||||
|
|||||||
Reference in New Issue
Block a user