From 3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb Mon Sep 17 00:00:00 2001
From: EMERALD <hudson.samuels@gmail.com>
Date: Thu, 19 Jan 2023 20:00:42 -0600
Subject: Store and plugin installation visual improvements (#343)

* Redesign store, add comments for filtering

* Improve installation/uninstallation modals

* Fix store comment to be easier to fix

* Add source code info to about page
---
 frontend/src/components/store/PluginCard.tsx | 277 +++++++++++++--------------
 1 file changed, 132 insertions(+), 145 deletions(-)

(limited to 'frontend/src/components/store/PluginCard.tsx')

diff --git a/frontend/src/components/store/PluginCard.tsx b/frontend/src/components/store/PluginCard.tsx
index aa5fd1d6..828d3ae9 100644
--- a/frontend/src/components/store/PluginCard.tsx
+++ b/frontend/src/components/store/PluginCard.tsx
@@ -1,15 +1,12 @@
 import {
-  DialogButton,
+  ButtonItem,
   Dropdown,
   Focusable,
-  Navigation,
-  QuickAccessTab,
+  PanelSectionRow,
   SingleDropdownOption,
   SuspensefulImage,
-  joinClassNames,
-  staticClasses,
 } from 'decky-frontend-lib';
-import { FC, useRef, useState } from 'react';
+import { FC, useState } from 'react';
 
 import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
 
@@ -19,172 +16,162 @@ interface PluginCardProps {
 
 const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
   const [selectedOption, setSelectedOption] = useState<number>(0);
-  const buttonRef = useRef<HTMLDivElement>(null);
-  const containerRef = useRef<HTMLDivElement>(null);
+  const root: boolean = plugin.tags.some((tag) => tag === 'root');
+
   return (
     <div
+      className="deckyStoreCard"
       style={{
-        padding: '30px',
-        paddingTop: '10px',
-        paddingBottom: '10px',
+        marginLeft: '20px',
+        marginRight: '20px',
+        marginBottom: '20px',
+        display: 'flex',
+        alignItems: 'center',
       }}
     >
-      {/* TODO: abstract this messy focus hackiness into a custom component in lib */}
-      <Focusable
-        className="deckyStoreCard"
-        ref={containerRef}
-        onActivate={(_: CustomEvent) => {
-          buttonRef.current!.focus();
-        }}
-        onCancel={(_: CustomEvent) => {
-          if (containerRef.current!.querySelectorAll('* :focus').length === 0) {
-            Navigation.NavigateBack();
-            setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
-          } else {
-            containerRef.current!.focus();
-          }
+      <div
+        className="deckyStoreCardImageContainer"
+        style={{
+          width: '320px',
+          height: '200px',
+          position: 'relative',
         }}
+      >
+        <SuspensefulImage
+          className="deckyStoreCardImage"
+          suspenseHeight="200px"
+          suspenseWidth="320px"
+          style={{
+            width: '320px',
+            height: '200px',
+            objectFit: 'cover',
+          }}
+          src={plugin.image_url}
+        />
+      </div>
+      <div
+        className="deckyStoreCardInfo"
         style={{
+          width: 'calc(100% - 320px)', // The calc is here so that the info section doesn't expand into the image
           display: 'flex',
           flexDirection: 'column',
-          background: '#ACB2C924',
-          height: 'unset',
-          marginBottom: 'unset',
-          // boxShadow: var(--gpShadow-Medium);
-          scrollSnapAlign: 'start',
-          boxSizing: 'border-box',
+          height: '100%',
+          marginLeft: '1em',
+          justifyContent: 'center',
         }}
       >
-        <div className="deckyStoreCardHeader" style={{ display: 'flex', alignItems: 'center' }}>
-          <div
-            style={{ fontSize: '18pt', padding: '10px' }}
-            className={joinClassNames(staticClasses.Text)}
-            // onClick={() => Router.NavigateToExternalWeb('https://github.com/' + plugin.artifact)}
-          >
-            {plugin.name}
-          </div>
-        </div>
-        <div
+        <span
+          className="deckyStoreCardTitle"
           style={{
-            display: 'flex',
-            flexDirection: 'row',
-            margin: '0 0 0 10px',
+            fontSize: '1.25em',
+            fontWeight: 'bold',
+            whiteSpace: 'nowrap',
+            overflow: 'hidden',
+            textOverflow: 'ellipsis',
+            width: '90%',
           }}
-          className="deckyStoreCardBody"
         >
-          <SuspensefulImage
-            className="deckyStoreCardImage"
-            suspenseWidth="256px"
-            style={{
-              width: 'auto',
-              height: '160px',
-              borderRadius: '5px',
-            }}
-            src={plugin.image_url}
-          />
-          <div
+          {plugin.name}
+        </span>
+        <span
+          className="deckyStoreCardAuthor"
+          style={{
+            marginRight: 'auto',
+            fontSize: '1em',
+          }}
+        >
+          {plugin.author}
+        </span>
+        <span
+          className="deckyStoreCardDescription"
+          style={{
+            fontSize: '13px',
+            color: '#969696',
+            WebkitLineClamp: root ? '2' : '3',
+            WebkitBoxOrient: 'vertical',
+            overflow: 'hidden',
+            display: '-webkit-box',
+          }}
+        >
+          {plugin.description ? (
+            plugin.description
+          ) : (
+            <span>
+              <i style={{ color: '#666' }}>No description provided.</i>
+            </span>
+          )}
+        </span>
+        {root && (
+          <span
+            className="deckyStoreCardDescription deckyStoreCardDescriptionRoot"
             style={{
-              display: 'flex',
-              flexDirection: 'column',
+              fontSize: '13px',
+              color: '#fee75c',
             }}
-            className="deckyStoreCardInfo"
           >
-            <p
-              className={joinClassNames(staticClasses.PanelSectionRow)}
-              style={{ marginTop: '0px', marginLeft: '16px' }}
-            >
-              <span style={{ paddingLeft: '0px' }}>Author: {plugin.author}</span>
-            </p>
-            <p
-              className={joinClassNames(staticClasses.PanelSectionRow)}
+            <i>This plugin has full access to your Steam Deck.</i>{' '}
+            <a
+              className="deckyStoreCardDescriptionRootLink"
+              href="https://deckbrew.xyz/root"
+              target="_blank"
               style={{
-                marginLeft: '16px',
-                marginTop: '0px',
-                marginBottom: '0px',
-                marginRight: '16px',
+                color: '#fee75c',
+                textDecoration: 'none',
               }}
             >
-              <span style={{ paddingLeft: '0px' }}>{plugin.description}</span>
-            </p>
-            <p
-              className={joinClassNames('deckyStoreCardTagsContainer', staticClasses.PanelSectionRow)}
-              style={{
-                padding: '0 16px',
-                display: 'flex',
-                flexWrap: 'wrap',
-                gap: '5px 10px',
-              }}
-            >
-              <span style={{ padding: '5px 0' }}>Tags:</span>
-              {plugin.tags.map((tag: string) => (
-                <span
-                  className="deckyStoreCardTag"
-                  style={{
-                    padding: '5px',
-                    borderRadius: '5px',
-                    background: tag == 'root' ? '#842029' : '#ACB2C947',
-                  }}
-                >
-                  {tag == 'root' ? 'Requires root' : tag}
-                </span>
-              ))}
-            </p>
-          </div>
-        </div>
+              deckbrew.xyz/root
+            </a>
+          </span>
+        )}
         <div
-          className="deckyStoreCardActionsContainer"
+          className="deckyStoreCardButtonRow"
           style={{
+            marginTop: '1em',
             width: '100%',
-            alignSelf: 'flex-end',
-            display: 'flex',
-            flexDirection: 'row',
+            overflow: 'hidden',
           }}
         >
-          <Focusable
-            className="deckyStoreCardActions"
-            style={{
-              display: 'flex',
-              flexDirection: 'row',
-              width: '100%',
-              margin: '10px',
-            }}
-          >
-            <div
-              className="deckyStoreCardInstallButtonContainer"
-              style={{
-                flex: '1',
-                margin: '0 10px 0 0',
-              }}
-            >
-              <DialogButton
-                className="deckyStoreCardInstallButton"
-                ref={buttonRef}
-                onClick={() => requestPluginInstall(plugin.name, plugin.versions[selectedOption])}
+          <PanelSectionRow>
+            <Focusable style={{ display: 'flex', maxWidth: '100%' }}>
+              <div
+                className="deckyStoreCardInstallContainer"
+                style={{
+                  paddingTop: '0px',
+                  paddingBottom: '0px',
+                  width: '40%',
+                }}
               >
-                Install
-              </DialogButton>
-            </div>
-            <div
-              className="deckyStoreCardVersionDropdownContainer"
-              style={{
-                flex: '0.2',
-              }}
-            >
-              <Dropdown
-                rgOptions={
-                  plugin.versions.map((version: StorePluginVersion, index) => ({
-                    data: index,
-                    label: version.name,
-                  })) as SingleDropdownOption[]
-                }
-                strDefaultLabel={'Select a version'}
-                selectedOption={selectedOption}
-                onChange={({ data }) => setSelectedOption(data)}
-              />
-            </div>
-          </Focusable>
+                <ButtonItem
+                  bottomSeparator="none"
+                  layout="below"
+                  onClick={() => requestPluginInstall(plugin.name, plugin.versions[selectedOption])}
+                >
+                  <span className="deckyStoreCardInstallText">Install</span>
+                </ButtonItem>
+              </div>
+              <div
+                className="deckyStoreCardVersionContainer"
+                style={{
+                  marginLeft: '5%',
+                  width: '30%',
+                }}
+              >
+                <Dropdown
+                  rgOptions={
+                    plugin.versions.map((version: StorePluginVersion, index) => ({
+                      data: index,
+                      label: version.name,
+                    })) as SingleDropdownOption[]
+                  }
+                  menuLabel="Plugin Version"
+                  selectedOption={selectedOption}
+                  onChange={({ data }) => setSelectedOption(data)}
+                />
+              </div>
+            </Focusable>
+          </PanelSectionRow>
         </div>
-      </Focusable>
+      </div>
     </div>
   );
 };
-- 
cgit v1.2.3

