hym_ui/lib/augment.dart
2025-04-24 00:52:42 -04:00

634 lines
22 KiB
Dart

import 'package:crab_ui/api_service.dart';
import 'package:crab_ui/attachmentDownload.dart';
import 'package:crab_ui/structs.dart';
import 'package:flutter/material.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'dart:html' as html;
import 'dart:js' as js;
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'attachmentWidget.dart';
class EmailToolbar extends StatefulWidget {
final Function(String) onJumpToSpan;
final VoidCallback onButtonPressed;
EmailToolbar(
{Key? key, required this.onButtonPressed, required this.onJumpToSpan})
: super(key: key);
@override
_DynamicClassesAugment createState() => _DynamicClassesAugment();
}
class _DynamicClassesAugment extends State<EmailToolbar> {
String selectedClass = 'Class 1';
// TextEditingController _jumpController = TextEditingController();
// late final FocusNode _JumpItemfocusNode;
// late final FocusNode _viewSpecsfocusNode;
// bool _jumpItemHasFocus = false;
// bool _viewSpecsHasFocus = false;
@override
void initState() {
super.initState();
// _JumpItemfocusNode = FocusNode();
// _viewSpecsfocusNode = FocusNode();
// _JumpItemfocusNode.addListener(() {
// setState(() => _jumpItemHasFocus = _JumpItemfocusNode.hasFocus);
// });
// _viewSpecsfocusNode.addListener(() {
// setState(() => _viewSpecsHasFocus = _viewSpecsfocusNode.hasFocus);
// });
}
@override
void dispose() {
// _JumpItemfocusNode.dispose();
// _viewSpecsfocusNode.dispose();
// _jumpController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// const animationDuration = Duration(milliseconds: 250);
return Column(children: [
Row(
children: [
ElevatedButton(
onPressed: () => AugmentClasses.handleHome(context),
child: Text('Home'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleReload,
child: Text('Reload'),
),
ElevatedButton(
onPressed: () => AugmentClasses.handleImages(context),
child: Text('Attachments'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleOpen,
child: Text('Open'),
),
// SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleFind,
child: Text('Find'),
),
// SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleStop,
child: Text('Stop'),
),
Spacer(),
PopupMenuButton<String>(
onSelected: (String value) {
setState(() {
selectedClass = value;
print(selectedClass);
});
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
const PopupMenuItem<String>(
value: 'Class 1',
child: Text('Class 1'),
),
const PopupMenuItem<String>(
value: 'Class 2',
child: Text('Class 2'),
),
const PopupMenuItem<String>(
value: 'Turbo 3',
child: Text('Turbo 3'),
),
],
// child: ElevatedButton(
// onPressed: () {},
child: Text('Options'),
),
],
),
if (selectedClass == 'Class 2')
Stack(children: [
Row(
children: [
ElevatedButton(
onPressed: () => AugmentClasses.JumpButton(context),
child: Text('JumpItem:'),
),
// SizedBox(
// width: 8,
// ),
Container(
width: 50,
height: 30,
child: TextField(
// controller: _jumpController,
decoration: InputDecoration(
border: OutlineInputBorder(),
// suffixIcon: Icon(Icons.search)
),
onSubmitted: (value) {
print("onSubmitted");
if (value.isNotEmpty) {
widget.onJumpToSpan(value);
}
},
),
),
//TODO: Make an animation to make the button a textfield
// AnimatedSwitcher(
// duration: animationDuration,
// transitionBuilder: (Widget child, Animation<double> animation) {
// return FadeTransition(opacity: animation, child: child);
// },
// child: _jumpItemHasFocus
// ? Container(
// key: ValueKey('TextField1'),
// width: 150,
// child: TextField(
// focusNode: _JumpItemfocusNode,
// decoration: InputDecoration(
// hintText: 'Enter Text',
// border: OutlineInputBorder(),
// ),
// ),
// )
// : Container(
// key: ValueKey('Button1'),
// child: ElevatedButton(
// onPressed: () => _JumpItemfocusNode.requestFocus(),
// child: Text('Jump Item:'),
// ),
// ),
// ),
SizedBox(width: 8),
ElevatedButton(
onPressed: () => AugmentClasses.ViewSpecsButton(context),
child: Text('ViewSpecs:')),
Container(
width: 50,
height: 30,
child: TextField(
decoration: InputDecoration(
labelText: '',
border: OutlineInputBorder(),
// suffixIcon: Icon(Icons.style_rounded)
),
),
),
ElevatedButton(
onPressed: () => AugmentClasses.FilterButton(context),
child: Text('Filter'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleOpen,
child: Text('Lookup'),
),
// SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleFind,
child: Text('Create Link'),
),
ElevatedButton(
onPressed: AugmentClasses.handleFind,
child: Text('Paste Link'),
),
],
)
])
]);
}
}
class AugmentClasses {
ApiService _apiService = ApiService();
static OverlayEntry? _overlayEntry;
static void handleHome(BuildContext context) {
Navigator.of(context).popUntil((route) => route.isFirst);
}
static void handleReload() {
print("reload");
}
static void handleImages(BuildContext context) {
print("Images button pressed");
final overlay = Overlay.of(context);
final renderBox = context.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
_overlayEntry = OverlayEntry(
builder: (context) => Stack(
children: [
// Dimmed background
Container(
color: Colors.black54,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
),
// Focused content window
PointerInterceptor(
child: Center(
child: Material(
elevation: 8,
borderRadius: BorderRadius.circular(12),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 400,
maxHeight: 500,
),
child: Column(
children: [
_buildHeader(context),
const Divider(height: 1),
Expanded(
child: ListView(
children: _buildMenuItem(context),
),
),
],
),
),
),
),
),
],
),
);
if (_overlayEntry != null) {
overlay.insert(_overlayEntry!);
}
}
// Add missing widget builder methods
static Widget _buildHeader(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16.0),
child:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(
'Thread Attachments',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
CloseButton(
onPressed: () {
_overlayEntry?.remove();
},
),
]));
}
static List<Widget> _buildMenuItem(BuildContext context) {
List<Widget> listOfFiles = [];
for (AttachmentResponse file in ApiService.threadAttachments) {
listOfFiles.add(
ListTile (
leading: Icon(Icons.file_present),
title: Text(file.name.toString()),
trailing: GestureDetector(
child: Icon(Icons.download),
onTap: () => Attachmentdownload().saveFile(file),
),
onTap: () {
_overlayEntry?.remove();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AttachmentWidget(attachment: file)));
}));
}
return listOfFiles;
}
static void handleOpen() {
print("Open button pressed");
}
static void handleFind() {
print("Find button pressed");
}
static void handleStop() {
print("Stop button pressed");
}
static void handleJump(String spanId) {
String js_code = '''
var iframe = document.getElementsByTagName('iframe')[0]; // 0 for the first iframe, 1 for the second, etc.
// Check if the iframe is loaded and has content
if (iframe && iframe.contentDocument) {
// Access the document inside the iframe
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Find the element with the specific id inside the iframe
var targetElement = iframeDoc.getElementById("$spanId"); // Replace '36 ' with the actual id of the target element
// If the element exists, scroll to it
if (targetElement) {
targetElement.scrollIntoView();
console.log('Scrolled to element with id "$spanId" inside the iframe.');
} else {
console.log('Element with id "$spanId" not found inside the iframe.');
}
} else {
console.log('Iframe not found or not loaded.');
}
''';
js.context.callMethod('eval', [js_code]);
}
static void invisibility(String htmlClass) {}
static Future<void> JumpButton(BuildContext context) async {
// FocusNode textFieldFocusNode = FocusNode();
AugmentClasses.disableIframePointerEvents();
await showDialog(
barrierDismissible: true,
// barrierColor: Colors.yellow,
context: context,
builder: (context) => AlertDialog(
title: Text('Jump Item:'),
content: Container(
width: 300,
height: 170,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Jump (to) Item (at)'),
SizedBox(height: 10),
TextField(
autofocus: true,
decoration: InputDecoration(
labelText: '...',
border: OutlineInputBorder(),
suffixIcon: Icon(Icons.search)),
onSubmitted: (value) {
print("onSubmitted: $value");
if (value.isNotEmpty) {
handleJump(value);
Navigator.of(context).pop();
}
},
),
Spacer(
flex: 5,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () => AugmentClasses.ViewSpecsButton(context),
child: Text("Viewspecs:"),
),
SizedBox(
width: 150,
child: TextField(
maxLines: 1,
decoration: InputDecoration(
labelText: '',
border: OutlineInputBorder(),
suffixIcon: Icon(Icons.search)),
onSubmitted: (value) {
print("onSubmitted: $value");
if (value.isNotEmpty) {
handleJump(value);
Navigator.of(context).pop();
}
},
),
),
],
),
],
),
),
actions: [
ElevatedButton(
onPressed: () {
//TODO: Grab both textfields and call both of the functions handles
},
child: Text('OK')),
TextButton(
onPressed: () {
Navigator.of(context).pop();
// print('close pressed');
},
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () {
//TODO: in the ui demo didn't see it
},
child: Text('Help'))
],
),
).then((_) {
AugmentClasses.enableIframePointerEvents();
});
}
static Future<void> ViewSpecsButton(context) async {
//TODO: finish it
bool blankLines = false;
bool numbering = false;
bool statementSignatures = false;
AugmentClasses.disableIframePointerEvents();
await showDialog(
context: context,
builder: (context) => Container(
height: 150,
width: 300,
child: AlertDialog(
title: Text('Viewspecs(short)'),
content: Container(
width: 400, // Set the width to simulate the Windows style
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// First section: Checkboxes for "Show"
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Show'),
Row(
children: [
Text("y z"),
Checkbox(
value: blankLines,
onChanged: (bool? value) {
blankLines = value!;
},
),
Text('Blank lines'),
],
),
Row(
children: [
Text('m n'),
Checkbox(
value: numbering,
onChanged: (bool? value) {
numbering = value!;
},
),
Text('Numbering'),
],
),
Row(
children: [
Text('K L'),
Checkbox(
value: statementSignatures,
onChanged: (bool? value) {
statementSignatures = value!;
},
),
Text('Statement signatures'),
],
),
],
),
),
// Second section: Numeric input for Outline, Levels, and Lines
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Outline'),
Row(
children: [
Text('Levels'),
SizedBox(width: 10),
Container(
width: 40,
child: TextField(
decoration: InputDecoration(
isDense: true,
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
],
),
Row(
children: [
Text('Lines'),
SizedBox(width: 10),
Container(
width: 40,
child: TextField(
decoration: InputDecoration(
isDense: true,
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
],
),
],
),
),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(onPressed: () {}, child: Text('OK')),
ElevatedButton(
onPressed: () {
AugmentClasses.disableIframePointerEvents();
Navigator.of(context).pop();
},
child: Text('Cancel')),
ElevatedButton(
onPressed: () {}, child: Text('Reset')),
ElevatedButton(onPressed: () {}, child: Text('Help')),
],
),
],
),
),
),
)).then((_) {
AugmentClasses.enableIframePointerEvents(); // may be useless?
});
}
void handleFilter() {}
static Future<void> FilterButton(context) async {
//this is literally ctrl+F :skull:
//idea is to search in file, extract the <p> tags that contain these
//words and highlight, then when zoom, you just jump to that paragraph
AugmentClasses.disableIframePointerEvents();
await showDialog(
context: context,
builder: (context) => Container(
height: 150,
width: 300,
child: AlertDialog(
title: Text('Filter'),
content: Container(
width: 400, // Set the width to simulate the Windows style
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Set filter:'),
SizedBox(
width: 175,
child: TextField(
maxLines: 1,
decoration: InputDecoration(
border: OutlineInputBorder(),
),
),
)
],
)))));
}
static void disableIframePointerEvents() {
//pretty sure these dont work
final iframes = html.document.getElementsByTagName('iframe');
for (var iframe in iframes) {
if (iframe is html.Element) {
iframe.style.pointerEvents = 'none'; // Disable pointer events
}
}
}
static void enableIframePointerEvents() {
final iframes = html.document.getElementsByTagName('iframe');
for (var iframe in iframes) {
if (iframe is html.Element) {
iframe.style.pointerEvents = 'auto'; // Re-enable pointer events
}
}
}
}