TF-335 Open links in another tab in email detail view on browser
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart';
|
import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:core/presentation/utils/shims/dart_ui.dart' as ui;
|
import 'package:core/presentation/utils/shims/dart_ui.dart' as ui;
|
||||||
import 'package:core/utils/app_logger.dart' as logger;
|
|
||||||
|
|
||||||
class HtmlContentViewerOnWeb extends StatefulWidget {
|
class HtmlContentViewerOnWeb extends StatefulWidget {
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _getRandString(int len) {
|
String _getRandString(int len) {
|
||||||
var random = Random.secure();
|
var random = math.Random.secure();
|
||||||
var values = List<int>.generate(len, (i) => random.nextInt(255));
|
var values = List<int>.generate(len, (i) => random.nextInt(255));
|
||||||
return base64UrlEncode(values);
|
return base64UrlEncode(values);
|
||||||
}
|
}
|
||||||
@@ -64,6 +63,7 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
final htmlScripts = '''
|
final htmlScripts = '''
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.parent.addEventListener('message', handleMessage, false);
|
window.parent.addEventListener('message', handleMessage, false);
|
||||||
|
window.addEventListener('click', handleOnClickLink, true);
|
||||||
|
|
||||||
function handleMessage(e) {
|
function handleMessage(e) {
|
||||||
if (e && e.data && e.data.includes("toIframe:")) {
|
if (e && e.data && e.data.includes("toIframe:")) {
|
||||||
@@ -87,6 +87,27 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleOnClickLink(e) {
|
||||||
|
var link = e.target;
|
||||||
|
console.log("handleOnClickLink: " + link);
|
||||||
|
if (link && isValidHttpUrl(link)) {
|
||||||
|
window.parent.postMessage(JSON.stringify({"view": "$createdViewId", "type": "toDart: OpenLink", "url": "" + link}), "*");
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidHttpUrl(string) {
|
||||||
|
let url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
url = new URL(string);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.protocol === "http:" || url.protocol === "https:";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
@@ -102,7 +123,9 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
min-height: ${minHeight}px;
|
min-height: ${minHeight}px;
|
||||||
min-width: ${minWidth}px;
|
min-width: ${minWidth}px;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-family: verdana;
|
font-family: Inter;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -137,6 +160,8 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
..srcdoc = _htmlData ?? ''
|
..srcdoc = _htmlData ?? ''
|
||||||
..style.border = 'none'
|
..style.border = 'none'
|
||||||
..style.overflow = 'hidden'
|
..style.overflow = 'hidden'
|
||||||
|
..style.width = '100%'
|
||||||
|
..style.height = '100%'
|
||||||
..onLoad.listen((event) async {
|
..onLoad.listen((event) async {
|
||||||
final dataGetHeight = <String, Object>{'type': 'toIframe: getHeight', 'view' : createdViewId};
|
final dataGetHeight = <String, Object>{'type': 'toIframe: getHeight', 'view' : createdViewId};
|
||||||
final dataGetWidth = <String, Object>{'type': 'toIframe: getWidth', 'view' : createdViewId};
|
final dataGetWidth = <String, Object>{'type': 'toIframe: getWidth', 'view' : createdViewId};
|
||||||
@@ -145,9 +170,6 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
final jsonGetHeight = jsonEncoder.convert(dataGetHeight);
|
final jsonGetHeight = jsonEncoder.convert(dataGetHeight);
|
||||||
final jsonGetWidth = jsonEncoder.convert(dataGetWidth);
|
final jsonGetWidth = jsonEncoder.convert(dataGetWidth);
|
||||||
|
|
||||||
logger.log('HtmlContentViewerOnWeb() | onLoad | jsonGetHeight: $jsonGetHeight');
|
|
||||||
logger.log('HtmlContentViewerOnWeb() | onLoad | jsonGetWidth: $jsonGetWidth');
|
|
||||||
|
|
||||||
html.window.postMessage(jsonGetHeight, '*');
|
html.window.postMessage(jsonGetHeight, '*');
|
||||||
html.window.postMessage(jsonGetWidth, '*');
|
html.window.postMessage(jsonGetWidth, '*');
|
||||||
|
|
||||||
@@ -155,7 +177,6 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
var data = json.decode(event.data);
|
var data = json.decode(event.data);
|
||||||
if (data['type'] != null && data['type'].contains('toDart: htmlHeight') && data['view'] == createdViewId) {
|
if (data['type'] != null && data['type'].contains('toDart: htmlHeight') && data['view'] == createdViewId) {
|
||||||
final docHeight = data['height'] ?? actualHeight;
|
final docHeight = data['height'] ?? actualHeight;
|
||||||
logger.log('HtmlContentViewerOnWeb() | onMessage | docHeight: $docHeight');
|
|
||||||
if (docHeight != null && mounted) {
|
if (docHeight != null && mounted) {
|
||||||
final scrollHeightWithBuffer = docHeight + 30.0;
|
final scrollHeightWithBuffer = docHeight + 30.0;
|
||||||
if (scrollHeightWithBuffer > minHeight) {
|
if (scrollHeightWithBuffer > minHeight) {
|
||||||
@@ -174,7 +195,6 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
|
|
||||||
if (data['type'] != null && data['type'].contains('toDart: htmlWidth') && data['view'] == createdViewId) {
|
if (data['type'] != null && data['type'].contains('toDart: htmlWidth') && data['view'] == createdViewId) {
|
||||||
final docWidth = data['width'] ?? actualWidth;
|
final docWidth = data['width'] ?? actualWidth;
|
||||||
logger.log('HtmlContentViewerOnWeb() | onMessage | docWidth: $docWidth');
|
|
||||||
if (docWidth != null && mounted) {
|
if (docWidth != null && mounted) {
|
||||||
if (docWidth > minWidth) {
|
if (docWidth > minWidth) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -192,6 +212,14 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
curve: Curves.easeIn);
|
curve: Curves.easeIn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data['type'] != null && data['type'].contains('toDart: OpenLink') && data['view'] == createdViewId) {
|
||||||
|
final link = data['url'];
|
||||||
|
if (link != null && mounted) {
|
||||||
|
log('_HtmlContentViewerOnWebState::_setUpWeb(): OpenLink: $link');
|
||||||
|
html.window.open('$link', '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -214,10 +242,6 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
width: actualWidth,
|
width: actualWidth,
|
||||||
child: _buildWebView(),
|
child: _buildWebView(),
|
||||||
),
|
),
|
||||||
PointerInterceptor(child: SizedBox(
|
|
||||||
height: actualHeight,
|
|
||||||
width: actualWidth,
|
|
||||||
)),
|
|
||||||
if (_isLoading) _buildLoadingView()
|
if (_isLoading) _buildLoadingView()
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_fil
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachments_place_holder_loading_widget.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/attachments_place_holder_loading_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_content_item_builder.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_content_place_holder_loading_widget.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/email_content_place_holder_loading_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/user_setting_popup_menu_mixin.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/user_setting_popup_menu_mixin.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
@@ -156,19 +155,27 @@ class EmailView extends GetView with UserSettingPopupMenuMixin {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: Obx(() => emailController.currentEmail != null
|
child: Obx(() {
|
||||||
? SingleChildScrollView(
|
if (emailController.currentEmail != null) {
|
||||||
physics : ClampingScrollPhysics(),
|
if (kIsWeb) {
|
||||||
child: Container(
|
return _buildEmailMessage(context);
|
||||||
margin: EdgeInsets.zero,
|
} else {
|
||||||
width: double.infinity,
|
return SingleChildScrollView(
|
||||||
alignment: Alignment.center,
|
physics : ClampingScrollPhysics(),
|
||||||
padding: EdgeInsets.zero,
|
child: Container(
|
||||||
color: Colors.white,
|
margin: EdgeInsets.zero,
|
||||||
child: _buildEmailMessage(context)
|
width: double.infinity,
|
||||||
))
|
alignment: Alignment.center,
|
||||||
: Center(child: _buildEmailEmpty(context))
|
padding: EdgeInsets.zero,
|
||||||
)
|
color: Colors.white,
|
||||||
|
child: _buildEmailMessage(context)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Center(child: _buildEmailEmpty(context));
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +191,9 @@ class EmailView extends GetView with UserSettingPopupMenuMixin {
|
|||||||
padding: EdgeInsets.only(right: 16),
|
padding: EdgeInsets.only(right: 16),
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
'${emailController.mailboxDashBoardController.selectedEmail.value?.getEmailTitle()}',
|
'${emailController.mailboxDashBoardController.selectedEmail.value?.getEmailTitle()}',
|
||||||
|
maxLines: 3,
|
||||||
|
minLines: 1,
|
||||||
|
cursorColor: AppColor.colorTextButton,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: AppColor.colorNameEmail,
|
color: AppColor.colorNameEmail,
|
||||||
@@ -193,15 +203,12 @@ class EmailView extends GetView with UserSettingPopupMenuMixin {
|
|||||||
|
|
||||||
Widget _buildEmailMessage(BuildContext context) {
|
Widget _buildEmailMessage(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(bottom: 16, left: 16, right: 16, top: 10),
|
padding: EdgeInsets.only(left: 16, right: 16, top: 10),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
@@ -220,7 +227,10 @@ class EmailView extends GetView with UserSettingPopupMenuMixin {
|
|||||||
: SizedBox.shrink()),
|
: SizedBox.shrink()),
|
||||||
_buildDivider(edgeInsets: EdgeInsets.only(top: 8)),
|
_buildDivider(edgeInsets: EdgeInsets.only(top: 8)),
|
||||||
_buildAttachments(context),
|
_buildAttachments(context),
|
||||||
_buildListEmailContent(context, constraints),
|
if (kIsWeb)
|
||||||
|
Expanded(child: _buildEmailContent(context, constraints))
|
||||||
|
else
|
||||||
|
_buildEmailContent(context, constraints),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -502,7 +512,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListEmailContent(BuildContext context, BoxConstraints constraints) {
|
Widget _buildEmailContent(BuildContext context, BoxConstraints constraints) {
|
||||||
return Obx(() => emailController.viewState.value.fold(
|
return Obx(() => emailController.viewState.value.fold(
|
||||||
(failure) => SizedBox.shrink(),
|
(failure) => SizedBox.shrink(),
|
||||||
(success) {
|
(success) {
|
||||||
@@ -510,14 +520,23 @@ class EmailView extends GetView with UserSettingPopupMenuMixin {
|
|||||||
return EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils);
|
return EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils);
|
||||||
} else {
|
} else {
|
||||||
if (emailController.emailContents.isNotEmpty) {
|
if (emailController.emailContents.isNotEmpty) {
|
||||||
return Column(children: [
|
final allEmailContents = emailController.emailContents
|
||||||
...emailController.emailContents.map((content) => EmailContentItemBuilder(
|
.map((emailContent) => emailContent.asHtml)
|
||||||
context,
|
.toList()
|
||||||
content,
|
.join('<br>');
|
||||||
constraints,
|
|
||||||
loadingWidget: EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils)
|
if (kIsWeb) {
|
||||||
).build()).toList(),
|
return HtmlContentViewerOnWeb(
|
||||||
]);
|
widthContent: constraints.maxWidth,
|
||||||
|
heightContent: MediaQuery.of(context).size.height,
|
||||||
|
contentHtml: allEmailContents,
|
||||||
|
controller: HtmlViewerControllerForWeb());
|
||||||
|
} else {
|
||||||
|
return HtmlContentViewer(
|
||||||
|
widthContent: MediaQuery.of(context).size.width,
|
||||||
|
contentHtml: allEmailContents,
|
||||||
|
loadingWidget: EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return SizedBox.shrink();
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,19 +33,14 @@ class EmailContentItemBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildItem() {
|
Widget _buildItem() {
|
||||||
log('EmailContentItemBuilder() | maxWidth: ${_constraints.maxWidth}');
|
|
||||||
log('EmailContentItemBuilder() | heightScreen: ${MediaQuery.of(_context).size.height}');
|
|
||||||
switch(_emailContent.type) {
|
switch(_emailContent.type) {
|
||||||
case EmailContentType.textHtml:
|
case EmailContentType.textHtml:
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
return SingleChildScrollView(
|
return HtmlContentViewerOnWeb(
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: HtmlContentViewerOnWeb(
|
|
||||||
widthContent: _constraints.maxWidth,
|
widthContent: _constraints.maxWidth,
|
||||||
heightContent: MediaQuery.of(_context).size.height,
|
heightContent: MediaQuery.of(_context).size.height,
|
||||||
contentHtml: _emailContent.content,
|
contentHtml: _emailContent.content,
|
||||||
controller: HtmlViewerControllerForWeb()),
|
controller: HtmlViewerControllerForWeb());
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return HtmlContentViewer(
|
return HtmlContentViewer(
|
||||||
widthContent: MediaQuery.of(_context).size.width,
|
widthContent: MediaQuery.of(_context).size.width,
|
||||||
|
|||||||
Reference in New Issue
Block a user