From d9d7f256927bf8289230f1292a483de0759d1b24 Mon Sep 17 00:00:00 2001
From: sneurlax <sneurlax@gmail.com>
Date: Fri, 21 Jul 2023 18:30:41 -0500
Subject: [PATCH] add desktop ordinals list, card, and details view

---
 .../desktop_ordinal_details_view.dart         | 269 ++++++++++++++++++
 .../subwidgets/desktop_ordinal_card.dart      |  56 ++++
 .../subwidgets/desktop_ordinals_list.dart     |   4 +-
 lib/route_generator.dart                      |  16 ++
 4 files changed, 343 insertions(+), 2 deletions(-)
 create mode 100644 lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart
 create mode 100644 lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart

diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart
new file mode 100644
index 000000000..36c8e33cb
--- /dev/null
+++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart
@@ -0,0 +1,269 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:stackwallet/models/ordinal.dart';
+import 'package:stackwallet/notifications/show_flush_bar.dart';
+import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart';
+import 'package:stackwallet/themes/stack_colors.dart';
+import 'package:stackwallet/utilities/assets.dart';
+import 'package:stackwallet/utilities/text_styles.dart';
+import 'package:stackwallet/widgets/background.dart';
+import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
+import 'package:stackwallet/widgets/desktop/primary_button.dart';
+import 'package:stackwallet/widgets/desktop/secondary_button.dart';
+import 'package:stackwallet/widgets/rounded_white_container.dart';
+
+class DesktopOrdinalDetailsView extends StatefulWidget {
+  const DesktopOrdinalDetailsView({
+    Key? key,
+    required this.walletId,
+    required this.ordinal,
+  }) : super(key: key);
+
+  final String walletId;
+  final Ordinal ordinal;
+
+  static const routeName = "/desktopOrdinalDetailsView";
+
+  @override
+  _DesktopOrdinalDetailsViewState createState() => _DesktopOrdinalDetailsViewState();
+}
+
+class _DesktopOrdinalDetailsViewState extends State<DesktopOrdinalDetailsView> {
+  static const _spacing = 12.0;
+
+  @override
+  Widget build(BuildContext context) {
+    return Background(
+      child: SafeArea(
+        child: Scaffold(
+          backgroundColor:
+          Theme.of(context).extension<StackColors>()!.background,
+          appBar: AppBar(
+            backgroundColor:
+            Theme.of(context).extension<StackColors>()!.background,
+            leading: const AppBarBackButton(),
+            title: Text(
+              "Ordinal details",
+              style: STextStyles.navBarTitle(context),
+            ),
+          ),
+          body: SingleChildScrollView(
+            child: Padding(
+              padding: const EdgeInsets.symmetric(horizontal: 16),
+              child: Column(
+                children: [
+                  Padding(
+                    padding: const EdgeInsets.symmetric(
+                      vertical: 12,
+                      horizontal: 39,
+                    ),
+                    child: _OrdinalImageGroup(
+                      ordinal: widget.ordinal,
+                      walletId: widget.walletId,
+                    ),
+                  ),
+                  _DetailsItemWCopy(
+                    title: "Inscription number",
+                    data: widget.ordinal.inscriptionNumber.toString(),
+                  ),
+                  const SizedBox(
+                    height: _spacing,
+                  ),
+                  _DetailsItemWCopy(
+                    title: "ID",
+                    data: widget.ordinal.inscriptionId,
+                  ),
+                  const SizedBox(
+                    height: _spacing,
+                  ),
+                  // todo: add utxo status
+                  const SizedBox(
+                    height: _spacing,
+                  ),
+                  const _DetailsItemWCopy(
+                    title: "Amount",
+                    data: "TODO", // TODO infer from utxo utxoTXID:utxoVOUT
+                  ),
+                  const SizedBox(
+                    height: _spacing,
+                  ),
+                  const _DetailsItemWCopy(
+                    title: "Owner address",
+                    data: "TODO", // infer from address associated w utxoTXID
+                  ),
+                  const SizedBox(
+                    height: _spacing,
+                  ),
+                  _DetailsItemWCopy(
+                    title: "Transaction ID",
+                    data: widget.ordinal.utxoTXID,
+                  ),
+                  const SizedBox(
+                    height: _spacing,
+                  ),
+                ],
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class _DetailsItemWCopy extends StatelessWidget {
+  const _DetailsItemWCopy({
+    Key? key,
+    required this.title,
+    required this.data,
+  }) : super(key: key);
+
+  final String title;
+  final String data;
+
+  @override
+  Widget build(BuildContext context) {
+    return RoundedWhiteContainer(
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              Text(
+                title,
+                style: STextStyles.itemSubtitle(context),
+              ),
+              GestureDetector(
+                onTap: () async {
+                  await Clipboard.setData(ClipboardData(text: data));
+                  if (context.mounted) {
+                    unawaited(
+                      showFloatingFlushBar(
+                        type: FlushBarType.info,
+                        message: "Copied to clipboard",
+                        context: context,
+                      ),
+                    );
+                  }
+                },
+                child: SvgPicture.asset(
+                  Assets.svg.copy,
+                  color:
+                  Theme.of(context).extension<StackColors>()!.infoItemIcons,
+                  width: 12,
+                ),
+              ),
+            ],
+          ),
+          const SizedBox(
+            height: 4,
+          ),
+          SelectableText(
+            data,
+            style: STextStyles.itemSubtitle12(context),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class _OrdinalImageGroup extends StatelessWidget {
+  const _OrdinalImageGroup({
+    Key? key,
+    required this.walletId,
+    required this.ordinal,
+  }) : super(key: key);
+
+  final String walletId;
+  final Ordinal ordinal;
+
+  static const _spacing = 12.0;
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: [
+        // Text(
+        //   "${ordinal.inscriptionId}", // Use any other property you want
+        //   style: STextStyles.w600_16(context),
+        // ),
+        // const SizedBox(
+        //   height: _spacing,
+        // ),
+        AspectRatio(
+          aspectRatio: 1,
+          child: AspectRatio(
+            aspectRatio: 1,
+            child: Container(
+              color: Colors.red,
+              child: Image.network(
+                ordinal.content, // Use the preview URL as the image source
+                fit: BoxFit.cover,
+                filterQuality: FilterQuality.none, // Set the filter mode to nearest
+              ),
+            ),
+          ),
+        ),
+        const SizedBox(
+          height: _spacing,
+        ),
+        Row(
+          children: [
+            Expanded(
+              child: SecondaryButton(
+                label: "Download",
+                icon: SvgPicture.asset(
+                  Assets.svg.arrowDown,
+                  width: 10,
+                  height: 12,
+                  color: Theme.of(context)
+                      .extension<StackColors>()!
+                      .buttonTextSecondary,
+                ),
+                buttonHeight: ButtonHeight.l,
+                iconSpacing: 4,
+                onPressed: () {
+                  // TODO: save and download image to device
+                },
+              ),
+            ),
+            const SizedBox(
+              width: _spacing,
+            ),
+            Expanded(
+              child: PrimaryButton(
+                label: "Send",
+                icon: SvgPicture.asset(
+                  Assets.svg.send,
+                  width: 10,
+                  height: 10,
+                  color: Theme.of(context)
+                      .extension<StackColors>()!
+                      .buttonTextPrimary,
+                ),
+                buttonHeight: ButtonHeight.l,
+                iconSpacing: 4,
+                onPressed: () async {
+                  final response = await showDialog<String?>(
+                    context: context,
+                    builder: (_) => const SendOrdinalUnfreezeDialog(),
+                  );
+                  if (response == "unfreeze") {
+                    // TODO: unfreeze and go to send ord screen
+                  }
+                },
+              ),
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart
new file mode 100644
index 000000000..9d94a4b0a
--- /dev/null
+++ b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+
+import 'package:stackwallet/models/ordinal.dart';
+import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart';
+import 'package:stackwallet/utilities/text_styles.dart';
+import 'package:stackwallet/widgets/rounded_white_container.dart';
+
+class DesktopOrdinalCard extends StatelessWidget {
+  const DesktopOrdinalCard({
+    Key? key,
+    required this.walletId,
+    required this.ordinal,
+  }) : super(key: key);
+
+  final String walletId;
+  final Ordinal ordinal;
+
+  @override
+  Widget build(BuildContext context) {
+    return RoundedWhiteContainer(
+      radiusMultiplier: 2,
+      onPressed: () {
+        Navigator.of(context).pushNamed(
+          DesktopOrdinalDetailsView.routeName,
+          arguments: (walletId: walletId, ordinal: ordinal),
+        );
+      },
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+            AspectRatio(
+              aspectRatio: 1,
+              child: Container(
+              color: Colors.red,
+              child: Image.network(
+                ordinal.content, // Use the preview URL as the image source
+                fit: BoxFit.cover,
+                filterQuality: FilterQuality.none, // Set the filter mode to nearest
+              ),
+            ),
+          ),
+          const Spacer(),
+          Text(
+            'INSC. ${ordinal.inscriptionNumber}', // infer from address associated with utxoTXID
+            style: STextStyles.w500_12(context),
+          ),
+          // const Spacer(),
+          // Text(
+          //   "ID ${ordinal.inscriptionId}",
+          //   style: STextStyles.w500_8(context),
+          // ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart
index f12fe6834..b30e94dd4 100644
--- a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart
+++ b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:stackwallet/models/ordinal.dart';
 
-import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart';
+import 'package:stackwallet/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart';
 
 class DesktopOrdinalsList extends StatelessWidget {
   const DesktopOrdinalsList({
@@ -35,7 +35,7 @@ class DesktopOrdinalsList extends StatelessWidget {
               crossAxisCount: 4,
               childAspectRatio: 6 / 7, // was 3/4, less data displayed now
             ),
-            itemBuilder: (_, i) => OrdinalCard(
+            itemBuilder: (_, i) => DesktopOrdinalCard(
               walletId: walletId,
               ordinal: ordinals[i],
             ),
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
index 5a5c1df17..03c7f13bc 100644
--- a/lib/route_generator.dart
+++ b/lib/route_generator.dart
@@ -147,6 +147,7 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub
 import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
 import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
 import 'package:stackwallet/pages_desktop_specific/notifications/desktop_notifications_view.dart';
+import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart';
 import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinals_view.dart';
 import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
 import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart';
@@ -453,6 +454,21 @@ class RouteGenerator {
         }
         return _routeError("${settings.name} invalid args: ${args.toString()}");
 
+      case DesktopOrdinalDetailsView.routeName:
+        if (args is ({Ordinal ordinal, String walletId})) {
+          return getRoute(
+            shouldUseMaterialRoute: useMaterialPageRoute,
+            builder: (_) => OrdinalDetailsView(
+              walletId: args.walletId,
+              ordinal: args.ordinal,
+            ),
+            settings: RouteSettings(
+              name: settings.name,
+            ),
+          );
+        }
+        return _routeError("${settings.name} invalid args: ${args.toString()}");
+
       case OrdinalsFilterView.routeName:
         return getRoute(
             shouldUseMaterialRoute: useMaterialPageRoute,