🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-09-26 17:31:49.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-09-26 17:58:18.0 +01:00:00
commit
2e028992a37e1a6ed1f00110f8a1c3bfd54b3b69 [patch]
tree
d3174244ea81b3b3beb26080f5811da2d4671712
parent
d084d8bbc5c1dc5da2998d9a979ef68008b37507
download
2e028992a37e1a6ed1f00110f8a1c3bfd54b3b69.tar.gz

Use placeholders to prevent layout flow disruptions



Diff

 chartered-frontend/package-lock.json                             | 15 +++++++++++++++
 chartered-frontend/package.json                                  |  1 +
 chartered-frontend/src/index.sass                                |  1 +
 chartered-frontend/src/util.tsx                                  | 17 +++++++++++++++++
 chartered-frontend/src/pages/crate/CrateView.tsx                 |  8 +++++---
 chartered-frontend/src/pages/crate/Members.tsx                   | 12 +++++++-----
 chartered-frontend/src/pages/crate/OrganisationView.tsx          | 61 +++++++++++++++++++++++++++++++++++++++++++++++++------------
 chartered-frontend/src/pages/organisations/ListOrganisations.tsx |  7 ++-----
 8 files changed, 85 insertions(+), 37 deletions(-)

diff --git a/chartered-frontend/package-lock.json b/chartered-frontend/package-lock.json
index 4a3e4f3..2b644f3 100644
--- a/chartered-frontend/package-lock.json
+++ a/chartered-frontend/package-lock.json
@@ -18,6 +18,7 @@
        "react-dom": "^17.0.2",
        "react-human-time": "^1.2.0",
        "react-markdown": "^7.0.1",
        "react-placeholder": "^4.1.0",
        "react-router-dom": "^5.3.0",
        "react-syntax-highlighter": "^15.4.4",
        "remark-gfm": "^2.0.0",
@@ -9081,6 +9082,14 @@
        "react-dom": ">=16.3.0"
      }

    },

    "node_modules/react-placeholder": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/react-placeholder/-/react-placeholder-4.1.0.tgz",
      "integrity": "sha512-z1HGD86NWJTYTQumHsmGH9jkozv4QHa9dju/vHVUd4f1svu23pf5v7QoBLBfs3kA1S9GLJaCeRMHLbO2SCdz5A==",
      "peerDependencies": {
        "react": "^16.8.0 || ^17"
      }

    },

    "node_modules/react-popper": {
      "version": "1.3.11",
      "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
@@ -18803,6 +18812,12 @@
        "uncontrollable": "^7.2.1",
        "warning": "^4.0.3"
      }

    },

    "react-placeholder": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/react-placeholder/-/react-placeholder-4.1.0.tgz",
      "integrity": "sha512-z1HGD86NWJTYTQumHsmGH9jkozv4QHa9dju/vHVUd4f1svu23pf5v7QoBLBfs3kA1S9GLJaCeRMHLbO2SCdz5A==",
      "requires": {}
    },

    "react-popper": {
      "version": "1.3.11",
diff --git a/chartered-frontend/package.json b/chartered-frontend/package.json
index 8f4910c..171a23b 100644
--- a/chartered-frontend/package.json
+++ a/chartered-frontend/package.json
@@ -32,6 +32,7 @@
    "react-dom": "^17.0.2",
    "react-human-time": "^1.2.0",
    "react-markdown": "^7.0.1",
    "react-placeholder": "^4.1.0",
    "react-router-dom": "^5.3.0",
    "react-syntax-highlighter": "^15.4.4",
    "remark-gfm": "^2.0.0",
diff --git a/chartered-frontend/src/index.sass b/chartered-frontend/src/index.sass
index db918e5..99df259 100644
--- a/chartered-frontend/src/index.sass
+++ a/chartered-frontend/src/index.sass
@@ -1,7 +1,8 @@
$primary: #0d6efd
$font-family-monospace: "Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace
$enable-cssgrid: true

@import "../node_modules/react-placeholder/lib/reactPlaceholder.css"
@import "~bootstrap/scss/bootstrap.scss"
@import "../node_modules/source-code-pro/source-code-pro.css"

diff --git a/chartered-frontend/src/util.tsx b/chartered-frontend/src/util.tsx
index 8b3da03..23b861c 100644
--- a/chartered-frontend/src/util.tsx
+++ a/chartered-frontend/src/util.tsx
@@ -1,4 +1,5 @@
import React = require("react");
import ReactPlaceholder from "react-placeholder";
import { AuthContext } from "./useAuth";

export const BASE_URL = "http://localhost:8888";
@@ -37,4 +38,20 @@
  }, reloadOn);

  return { response, error };
}

export function RoundedPicture({ src, height, width, className }: { src: string, height: string, width: string, className?: string }) {
  const [imageLoaded, setImageLoaded] = React.useState(false);

  return (
    <div className={`position-relative d-inline-block ${className || ''}`} style={{height, width}}>
      <ReactPlaceholder showLoadingAnimation type="round" style={{height, width, position: "absolute"}} ready={imageLoaded}><></></ReactPlaceholder>
      <img
        style={{visibility: imageLoaded ? "visible" : "hidden", height, width}}
        src={src}
        onLoad={() => setImageLoaded(true)}
        className="rounded-circle"
      />
    </div>
  );
}
diff --git a/chartered-frontend/src/pages/crate/CrateView.tsx b/chartered-frontend/src/pages/crate/CrateView.tsx
index c0d5f96..c0eae91 100644
--- a/chartered-frontend/src/pages/crate/CrateView.tsx
+++ a/chartered-frontend/src/pages/crate/CrateView.tsx
@@ -19,7 +19,7 @@
  Square,
} from "react-bootstrap-icons";
import { useParams, NavLink, Redirect, Link } from "react-router-dom";
import { authenticatedEndpoint, useAuthenticatedRequest } from "../../util";
import { authenticatedEndpoint, RoundedPicture, RoundedPicture, useAuthenticatedRequest } from "../../util";

import Prism from "react-syntax-highlighter/dist/cjs/prism";
import ReactMarkdown from "react-markdown";
@@ -374,9 +374,11 @@
              <div>
                <div className="d-inline-block">
                  By
                  <img
                  <RoundedPicture
                    src="http://placekitten.com/22/22"
                    className="rounded-circle ms-1 me-1"
                    height="22px"
                    width="22px"
                    className="ms-1 me-1"
                  />
                  {version.uploader}
                </div>
diff --git a/chartered-frontend/src/pages/crate/Members.tsx b/chartered-frontend/src/pages/crate/Members.tsx
index 40df340..0ab68c1 100644
--- a/chartered-frontend/src/pages/crate/Members.tsx
+++ a/chartered-frontend/src/pages/crate/Members.tsx
@@ -7,12 +7,13 @@
  Save,
  PlusLg,
} from "react-bootstrap-icons";
import { authenticatedEndpoint, useAuthenticatedRequest } from "../../util";
import { authenticatedEndpoint, RoundedPicture, RoundedPicture, useAuthenticatedRequest } from "../../util";
import { useAuth } from "../../useAuth";
import { Button, Modal } from "react-bootstrap";
import { AsyncTypeahead } from "react-bootstrap-typeahead";
import { debounce } from "lodash";
import _ = require("lodash");
import ReactPlaceholder from "react-placeholder";

interface Member {
  uuid: string;
@@ -203,7 +204,7 @@

      <tr>
        <td className="align-middle fit">
          <img src="http://placekitten.com/48/48" className="rounded-circle" />
          <RoundedPicture src="http://placekitten.com/48/48" height="48px" width="48px" />
        </td>

        <td className="align-middle">
@@ -310,10 +311,11 @@
          ref={searchRef}
          renderMenuItemChildren={(option, props) => (
            <>
              <img
                alt={option.username}
              <RoundedPicture
                src="http://placekitten.com/24/24"
                className="rounded-circle me-2"
                height="24px"
                width="24px"
                className="me-2"
              />
              <span>{option.username}</span>
            </>
diff --git a/chartered-frontend/src/pages/crate/OrganisationView.tsx b/chartered-frontend/src/pages/crate/OrganisationView.tsx
index 1e69361..2020605 100644
--- a/chartered-frontend/src/pages/crate/OrganisationView.tsx
+++ a/chartered-frontend/src/pages/crate/OrganisationView.tsx
@@ -1,10 +1,10 @@
import React = require("react");
import { useState, useEffect } from "react";
import { Link, useParams } from "react-router-dom";

import Nav from "../../sections/Nav";
import { useAuth } from "../../useAuth";
import { useAuthenticatedRequest, authenticatedEndpoint } from "../../util";
import { useAuthenticatedRequest, authenticatedEndpoint, RoundedPicture } from "../../util";

import { BoxSeam, Plus, Trash } from "react-bootstrap-icons";
import {
@@ -19,6 +19,7 @@
import ErrorPage from "../ErrorPage";
import Loading from "../Loading";
import Members from "./Members";
import ReactPlaceholder from "react-placeholder";

interface OrganisationDetails {
  possible_permissions?: string[];
@@ -60,10 +61,11 @@

  if (error) {
    return <ErrorPage message={error} />;
  } else if (!organisationDetails) {
    return <Loading />;
  }

  const ready = !!organisationDetails;
  const [imageLoaded, setImageLoaded] = useState(false);

  return (
    <div className="text-white">
      <Nav />
@@ -74,14 +76,13 @@
            <div className="card border-0 shadow-sm text-black h-100">
              <div className="card-body">
                <div className="d-flex flex-row align-items-center">
                  <img
                    src="http://placekitten.com/96/96"
                    className="rounded-circle"
                  />
                  <RoundedPicture src="http://placekitten.com/96/96" height="96px" width="96px" />

                  <div className="px-2">
                    <h1 className="text-primary my-0">{organisation}</h1>
                    <p className="m-0">{organisationDetails.description}</p>
                    <ReactPlaceholder showLoadingAnimation type="text" rows={1} ready={ready} style={{height: "1.4rem"}}>
                      <p className="m-0">{organisationDetails?.description || <i>No description given.</i>}</p>
                    </ReactPlaceholder>
                  </div>
                </div>
              </div>
@@ -113,23 +114,35 @@
                </ul>
              </div>

              {activeTab == "crates" ? (
                <ListCrates
                  organisation={organisation}
                  crates={organisationDetails.crates}
                />
              ) : (
                <></>
              )}
              {activeTab == "members" ? (
                <ListMembers
                  organisation={organisation}
                  members={organisationDetails.members}
                  possiblePermissions={organisationDetails.possible_permissions}
                  reload={() => setReload(reload + 1)}
                />
              {!ready ? (
                <div className="card-body">
                  <div className="d-flex justify-content-center align-items-center">
                    <div className="spinner-border text-primary" role="status">
                      <span className="visually-hidden">Loading...</span>
                    </div>
                  </div>
                </div>
              ) : (
                <></>
                <>
                  {activeTab == "crates" ? (
                    <ListCrates
                      organisation={organisation}
                      crates={organisationDetails.crates}
                    />
                  ) : (
                    <></>
                  )}
                  {activeTab == "members" ? (
                    <ListMembers
                      organisation={organisation}
                      members={organisationDetails.members}
                      possiblePermissions={organisationDetails.possible_permissions}
                      reload={() => setReload(reload + 1)}
                    />
                  ) : (
                    <></>
                  )}
                </>
              )}
            </div>
          </div>
diff --git a/chartered-frontend/src/pages/organisations/ListOrganisations.tsx b/chartered-frontend/src/pages/organisations/ListOrganisations.tsx
index d878cc7..6ee604f 100644
--- a/chartered-frontend/src/pages/organisations/ListOrganisations.tsx
+++ a/chartered-frontend/src/pages/organisations/ListOrganisations.tsx
@@ -1,9 +1,9 @@
import React = require("react");
import { Link } from "react-router-dom";

import Nav from "../../sections/Nav";
import { useAuth } from "../../useAuth";
import { useAuthenticatedRequest } from "../../util";
import { RoundedPicture, useAuthenticatedRequest } from "../../util";
import ErrorPage from "../ErrorPage";
import Loading from "../Loading";

@@ -48,10 +48,7 @@
                {list.organisations.map((v, i) => (
                  <tr key={i}>
                    <td className="align-middle fit">
                      <img
                        src="http://placekitten.com/48/48"
                        className="rounded-circle"
                      />
                      <RoundedPicture src="http://placekitten.com/48/48" height="48px" width="48px" />
                    </td>

                    <td className="align-middle" style={{ lineHeight: "1.1" }}>