augment.dart 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/widgets.dart';
  4. import 'package:http/http.dart' as http;
  5. import 'dart:convert';
  6. import 'dart:ui_web' as ui;
  7. import 'dart:html' as html;
  8. import 'dart:js' as js;
  9. import 'api_service.dart';
  10. class EmailToolbar extends StatefulWidget {
  11. final Function(String) onJumpToSpan;
  12. final VoidCallback onButtonPressed;
  13. EmailToolbar(
  14. {Key? key, required this.onButtonPressed, required this.onJumpToSpan})
  15. : super(key: key);
  16. @override
  17. _DynamicClassesAugment createState() => _DynamicClassesAugment();
  18. }
  19. class _DynamicClassesAugment extends State<EmailToolbar> {
  20. String selectedClass = 'Class 1';
  21. // TextEditingController _jumpController = TextEditingController();
  22. // late final FocusNode _JumpItemfocusNode;
  23. // late final FocusNode _viewSpecsfocusNode;
  24. // bool _jumpItemHasFocus = false;
  25. // bool _viewSpecsHasFocus = false;
  26. @override
  27. void initState() {
  28. super.initState();
  29. // _JumpItemfocusNode = FocusNode();
  30. // _viewSpecsfocusNode = FocusNode();
  31. // _JumpItemfocusNode.addListener(() {
  32. // setState(() => _jumpItemHasFocus = _JumpItemfocusNode.hasFocus);
  33. // });
  34. // _viewSpecsfocusNode.addListener(() {
  35. // setState(() => _viewSpecsHasFocus = _viewSpecsfocusNode.hasFocus);
  36. // });
  37. }
  38. @override
  39. void dispose() {
  40. // _JumpItemfocusNode.dispose();
  41. // _viewSpecsfocusNode.dispose();
  42. // _jumpController.dispose();
  43. super.dispose();
  44. }
  45. @override
  46. Widget build(BuildContext context) {
  47. // const animationDuration = Duration(milliseconds: 250);
  48. return Column(children: [
  49. Row(
  50. children: [
  51. ElevatedButton(
  52. onPressed: () => AugmentClasses.handleHome(context),
  53. child: Text('Home'),
  54. ),
  55. SizedBox(width: 8),
  56. ElevatedButton(
  57. onPressed: AugmentClasses.handleReload,
  58. child: Text('Reload'),
  59. ),
  60. ElevatedButton(
  61. onPressed: AugmentClasses.handleImages,
  62. child: Text('Images'),
  63. ),
  64. SizedBox(width: 8),
  65. ElevatedButton(
  66. onPressed: AugmentClasses.handleOpen,
  67. child: Text('Open'),
  68. ),
  69. // SizedBox(width: 8),
  70. ElevatedButton(
  71. onPressed: AugmentClasses.handleFind,
  72. child: Text('Find'),
  73. ),
  74. // SizedBox(width: 8),
  75. ElevatedButton(
  76. onPressed: AugmentClasses.handleStop,
  77. child: Text('Stop'),
  78. ),
  79. Spacer(),
  80. PopupMenuButton<String>(
  81. onSelected: (String value) {
  82. setState(() {
  83. selectedClass = value;
  84. print(selectedClass);
  85. });
  86. },
  87. itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
  88. const PopupMenuItem<String>(
  89. value: 'Class 1',
  90. child: Text('Class 1'),
  91. ),
  92. const PopupMenuItem<String>(
  93. value: 'Class 2',
  94. child: Text('Class 2'),
  95. ),
  96. const PopupMenuItem<String>(
  97. value: 'Turbo 3',
  98. child: Text('Turbo 3'),
  99. ),
  100. ],
  101. // child: ElevatedButton(
  102. // onPressed: () {},
  103. child: Text('Options'),
  104. ),
  105. ],
  106. ),
  107. if (selectedClass == 'Class 2')
  108. Stack(children: [
  109. Row(
  110. children: [
  111. ElevatedButton(
  112. onPressed: () => AugmentClasses.JumpButton(context),
  113. child: Text('JumpItem:'),
  114. ),
  115. // SizedBox(
  116. // width: 8,
  117. // ),
  118. Container(
  119. width: 50,
  120. height: 30,
  121. child: TextField(
  122. // controller: _jumpController,
  123. decoration: InputDecoration(
  124. border: OutlineInputBorder(),
  125. // suffixIcon: Icon(Icons.search)
  126. ),
  127. onSubmitted: (value) {
  128. print("onSubmitted");
  129. if (value.isNotEmpty) {
  130. widget.onJumpToSpan(value);
  131. }
  132. },
  133. ),
  134. ),
  135. //TODO: Make an animation to make the button a textfield
  136. // AnimatedSwitcher(
  137. // duration: animationDuration,
  138. // transitionBuilder: (Widget child, Animation<double> animation) {
  139. // return FadeTransition(opacity: animation, child: child);
  140. // },
  141. // child: _jumpItemHasFocus
  142. // ? Container(
  143. // key: ValueKey('TextField1'),
  144. // width: 150,
  145. // child: TextField(
  146. // focusNode: _JumpItemfocusNode,
  147. // decoration: InputDecoration(
  148. // hintText: 'Enter Text',
  149. // border: OutlineInputBorder(),
  150. // ),
  151. // ),
  152. // )
  153. // : Container(
  154. // key: ValueKey('Button1'),
  155. // child: ElevatedButton(
  156. // onPressed: () => _JumpItemfocusNode.requestFocus(),
  157. // child: Text('Jump Item:'),
  158. // ),
  159. // ),
  160. // ),
  161. SizedBox(width: 8),
  162. ElevatedButton(
  163. onPressed: () => AugmentClasses.ViewSpecsButton(context),
  164. child: Text('ViewSpecs:')),
  165. Container(
  166. width: 50,
  167. height: 30,
  168. child: TextField(
  169. decoration: InputDecoration(
  170. labelText: '',
  171. border: OutlineInputBorder(),
  172. // suffixIcon: Icon(Icons.style_rounded)
  173. ),
  174. ),
  175. ),
  176. ElevatedButton(
  177. onPressed: () => AugmentClasses.FilterButton(context),
  178. child: Text('Filter'),
  179. ),
  180. SizedBox(width: 8),
  181. ElevatedButton(
  182. onPressed: AugmentClasses.handleOpen,
  183. child: Text('Lookup'),
  184. ),
  185. // SizedBox(width: 8),
  186. ElevatedButton(
  187. onPressed: AugmentClasses.handleFind,
  188. child: Text('Create Link'),
  189. ),
  190. ElevatedButton(
  191. onPressed: AugmentClasses.handleFind,
  192. child: Text('Paste Link'),
  193. ),
  194. ],
  195. )
  196. ])
  197. ]);
  198. }
  199. }
  200. class AugmentClasses {
  201. static void handleHome(BuildContext context) {
  202. Navigator.of(context).popUntil((route) => route.isFirst);
  203. }
  204. static void handleReload() {
  205. print("reload");
  206. }
  207. static void handleImages() {
  208. print("Images button pressed");
  209. }
  210. static void handleOpen() {
  211. print("Open button pressed");
  212. }
  213. static void handleFind() {
  214. print("Find button pressed");
  215. }
  216. static void handleStop() {
  217. print("Stop button pressed");
  218. }
  219. static void handleJump(String spanId) {
  220. String js_code = '''
  221. var iframe = document.getElementsByTagName('iframe')[0]; // 0 for the first iframe, 1 for the second, etc.
  222. // Check if the iframe is loaded and has content
  223. if (iframe && iframe.contentDocument) {
  224. // Access the document inside the iframe
  225. var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
  226. // Find the element with the specific id inside the iframe
  227. var targetElement = iframeDoc.getElementById("$spanId"); // Replace '36 ' with the actual id of the target element
  228. // If the element exists, scroll to it
  229. if (targetElement) {
  230. targetElement.scrollIntoView();
  231. console.log('Scrolled to element with id "$spanId" inside the iframe.');
  232. } else {
  233. console.log('Element with id "$spanId" not found inside the iframe.');
  234. }
  235. } else {
  236. console.log('Iframe not found or not loaded.');
  237. }
  238. ''';
  239. js.context.callMethod('eval', [js_code]);
  240. }
  241. static void invisibility(String htmlClass) {}
  242. static Future<void> JumpButton(BuildContext context) async {
  243. // FocusNode textFieldFocusNode = FocusNode();
  244. AugmentClasses.disableIframePointerEvents();
  245. await showDialog(
  246. barrierDismissible: true,
  247. // barrierColor: Colors.yellow,
  248. context: context,
  249. builder: (context) => AlertDialog(
  250. title: Text('Jump Item:'),
  251. content: Container(
  252. width: 300,
  253. height: 170,
  254. child: Column(
  255. mainAxisSize: MainAxisSize.min,
  256. children: [
  257. Text('Jump (to) Item (at)'),
  258. SizedBox(height: 10),
  259. TextField(
  260. autofocus: true,
  261. decoration: InputDecoration(
  262. labelText: '...',
  263. border: OutlineInputBorder(),
  264. suffixIcon: Icon(Icons.search)),
  265. onSubmitted: (value) {
  266. print("onSubmitted: $value");
  267. if (value.isNotEmpty) {
  268. handleJump(value);
  269. Navigator.of(context).pop();
  270. }
  271. },
  272. ),
  273. Spacer(
  274. flex: 5,
  275. ),
  276. Row(
  277. mainAxisSize: MainAxisSize.min,
  278. children: [
  279. ElevatedButton(
  280. onPressed: () => AugmentClasses.ViewSpecsButton(context),
  281. child: Text("Viewspecs:"),
  282. ),
  283. SizedBox(
  284. width: 150,
  285. child: TextField(
  286. maxLines: 1,
  287. decoration: InputDecoration(
  288. labelText: '',
  289. border: OutlineInputBorder(),
  290. suffixIcon: Icon(Icons.search)),
  291. onSubmitted: (value) {
  292. print("onSubmitted: $value");
  293. if (value.isNotEmpty) {
  294. handleJump(value);
  295. Navigator.of(context).pop();
  296. }
  297. },
  298. ),
  299. ),
  300. ],
  301. ),
  302. ],
  303. ),
  304. ),
  305. actions: [
  306. ElevatedButton(
  307. onPressed: () {
  308. //TODO: Grab both textfields and call both of the functions handles
  309. },
  310. child: Text('OK')),
  311. TextButton(
  312. onPressed: () {
  313. Navigator.of(context).pop();
  314. // print('close pressed');
  315. },
  316. child: Text('Cancel'),
  317. ),
  318. ElevatedButton(
  319. onPressed: () {
  320. //TODO: in the ui demo didn't see it
  321. },
  322. child: Text('Help'))
  323. ],
  324. ),
  325. ).then((_) {
  326. AugmentClasses.enableIframePointerEvents();
  327. });
  328. }
  329. static Future<void> ViewSpecsButton(context) async {
  330. //TODO: finish it
  331. bool blankLines = false;
  332. bool numbering = false;
  333. bool statementSignatures = false;
  334. AugmentClasses.disableIframePointerEvents();
  335. await showDialog(
  336. context: context,
  337. builder: (context) => Container(
  338. height: 150,
  339. width: 300,
  340. child: AlertDialog(
  341. title: Text('Viewspecs(short)'),
  342. content: Container(
  343. width: 400, // Set the width to simulate the Windows style
  344. child: Column(
  345. mainAxisSize: MainAxisSize.min,
  346. crossAxisAlignment: CrossAxisAlignment.start,
  347. children: [
  348. Row(
  349. children: [
  350. // First section: Checkboxes for "Show"
  351. Expanded(
  352. child: Column(
  353. crossAxisAlignment: CrossAxisAlignment.start,
  354. children: [
  355. Text('Show'),
  356. Row(
  357. children: [
  358. Text("y z"),
  359. Checkbox(
  360. value: blankLines,
  361. onChanged: (bool? value) {
  362. blankLines = value!;
  363. },
  364. ),
  365. Text('Blank lines'),
  366. ],
  367. ),
  368. Row(
  369. children: [
  370. Text('m n'),
  371. Checkbox(
  372. value: numbering,
  373. onChanged: (bool? value) {
  374. numbering = value!;
  375. },
  376. ),
  377. Text('Numbering'),
  378. ],
  379. ),
  380. Row(
  381. children: [
  382. Text('K L'),
  383. Checkbox(
  384. value: statementSignatures,
  385. onChanged: (bool? value) {
  386. statementSignatures = value!;
  387. },
  388. ),
  389. Text('Statement signatures'),
  390. ],
  391. ),
  392. ],
  393. ),
  394. ),
  395. // Second section: Numeric input for Outline, Levels, and Lines
  396. Expanded(
  397. child: Column(
  398. crossAxisAlignment: CrossAxisAlignment.start,
  399. children: [
  400. Text('Outline'),
  401. Row(
  402. children: [
  403. Text('Levels'),
  404. SizedBox(width: 10),
  405. Container(
  406. width: 40,
  407. child: TextField(
  408. decoration: InputDecoration(
  409. isDense: true,
  410. border: OutlineInputBorder(),
  411. ),
  412. keyboardType: TextInputType.number,
  413. ),
  414. ),
  415. ],
  416. ),
  417. Row(
  418. children: [
  419. Text('Lines'),
  420. SizedBox(width: 10),
  421. Container(
  422. width: 40,
  423. child: TextField(
  424. decoration: InputDecoration(
  425. isDense: true,
  426. border: OutlineInputBorder(),
  427. ),
  428. keyboardType: TextInputType.number,
  429. ),
  430. ),
  431. ],
  432. ),
  433. ],
  434. ),
  435. ),
  436. ],
  437. ),
  438. SizedBox(height: 20),
  439. Row(
  440. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  441. children: [
  442. ElevatedButton(onPressed: () {}, child: Text('OK')),
  443. ElevatedButton(
  444. onPressed: () {
  445. AugmentClasses.disableIframePointerEvents();
  446. Navigator.of(context).pop();
  447. },
  448. child: Text('Cancel')),
  449. ElevatedButton(
  450. onPressed: () {}, child: Text('Reset')),
  451. ElevatedButton(onPressed: () {}, child: Text('Help')),
  452. ],
  453. ),
  454. ],
  455. ),
  456. ),
  457. ),
  458. )).then((_) {
  459. AugmentClasses.enableIframePointerEvents(); // may be useless?
  460. });
  461. }
  462. void handleFilter() {}
  463. static Future<void> FilterButton(context) async {
  464. //this is literally ctrl+F :skull:
  465. //idea is to search in file, extract the <p> tags that contain these
  466. //words and highlight, then when zoom, you just jump to that paragraph
  467. AugmentClasses.disableIframePointerEvents();
  468. await showDialog(
  469. context: context,
  470. builder: (context) => Container(
  471. height: 150,
  472. width: 300,
  473. child: AlertDialog(
  474. title: Text('Filter'),
  475. content: Container(
  476. width: 400, // Set the width to simulate the Windows style
  477. child: Column(
  478. mainAxisSize: MainAxisSize.min,
  479. crossAxisAlignment: CrossAxisAlignment.start,
  480. children: [
  481. Text('Set filter:'),
  482. SizedBox(
  483. width: 175,
  484. child: TextField(
  485. maxLines: 1,
  486. decoration: InputDecoration(
  487. border: OutlineInputBorder(),
  488. ),
  489. ),
  490. )
  491. ],
  492. )))));
  493. }
  494. static void disableIframePointerEvents() {
  495. final iframes = html.document.getElementsByTagName('iframe');
  496. for (var iframe in iframes) {
  497. if (iframe is html.Element) {
  498. iframe.style.pointerEvents = 'none'; // Disable pointer events
  499. }
  500. }
  501. }
  502. static void enableIframePointerEvents() {
  503. final iframes = html.document.getElementsByTagName('iframe');
  504. for (var iframe in iframes) {
  505. if (iframe is html.Element) {
  506. iframe.style.pointerEvents = 'auto'; // Re-enable pointer events
  507. }
  508. }
  509. }
  510. }