FarmBot/Farmbot-Web-App

View on GitHub
frontend/photos/images/image_flipper.tsx

Summary

Maintainability
A
2 hrs
Test Coverage
import React from "react";
import {
  ImageFlipperProps, ImageFlipperState, PlaceholderImgProps,
} from "./interfaces";
import { Content, Actions } from "../../constants";
import { t } from "../../i18next_wrapper";
import { FlipperImage } from "./flipper_image";
import { selectImage, setShownMapImages } from "./actions";
import { TaggedImage } from "farmbot";
import { UUID } from "../../resources/interfaces";

export const PLACEHOLDER_FARMBOT = "/placeholder_farmbot.jpg";
export const PLACEHOLDER_FARMBOT_DARK = "/placeholder_farmbot_dark.jpg";

export const getIndexOfUuid = (images: TaggedImage[], uuid: UUID | undefined) =>
  uuid ? images.map(x => x.uuid).indexOf(uuid) : 0;

export const getNextIndexes = (
  images: TaggedImage[],
  currentImageUuid: UUID | undefined,
  increment: -1 | 1,
) => {
  const currentIndex = getIndexOfUuid(images, currentImageUuid);
  const nextIndex = currentIndex + increment;
  const indexAfterNext = currentIndex + (increment * 2);
  return { nextIndex, indexAfterNext };
};

export const selectNextImage = (images: TaggedImage[], index: number) =>
  (dispatch: Function) => {
    const nextImageUuid = images.map(x => x.uuid)[index];
    dispatch(selectImage(nextImageUuid));
    dispatch(setShownMapImages(nextImageUuid));
  };

/** Placeholder image with text overlay. */
export const PlaceholderImg = (props: PlaceholderImgProps) =>
  <div className={"no-flipper-image-container"}>
    <p>{t(props.textOverlay)}</p>
    <img className={"image-flipper-image placeholder"}
      src={props.dark ? PLACEHOLDER_FARMBOT_DARK : PLACEHOLDER_FARMBOT}
      width={props.width} height={props.height} />
  </div>;

export class ImageFlipper extends
  React.Component<ImageFlipperProps, ImageFlipperState> {
  state: ImageFlipperState = { disableNext: true, disablePrev: false };

  onImageLoad = (img: HTMLImageElement) => {
    this.props.dispatch({
      type: Actions.SET_IMAGE_SIZE,
      payload: { width: img.naturalWidth, height: img.naturalHeight }
    });
  };

  get uuids() { return this.props.images.map(x => x.uuid); }

  go = (increment: -1 | 1) => () => {
    const currentImageUuid = this.props.currentImage?.uuid;
    const { nextIndex, indexAfterNext } =
      getNextIndexes(this.props.images, currentImageUuid, increment);
    const tooHigh = (index: number): boolean => index > this.uuids.length - 1;
    const tooLow = (index: number): boolean => index < 0;
    if (!tooHigh(nextIndex) && !tooLow(nextIndex)) {
      this.props.flipActionOverride
        ? this.props.flipActionOverride(nextIndex)
        : this.props.dispatch(selectNextImage(this.props.images, nextIndex));
    }
    this.setState({
      disableNext: tooLow(indexAfterNext),
      disablePrev: tooHigh(indexAfterNext),
    });
  };

  render() {
    const { images, currentImage } = this.props;
    const multipleImages = images.length > 1;
    const dark = this.props.id === "fullscreen-flipper";
    return <div className={`image-flipper ${this.props.id}`} id={this.props.id}
      onKeyDown={e => {
        if (["ArrowLeft", "ArrowRight"].includes(e.key)) {
          this.go(e.key == "ArrowLeft" ? 1 : -1)();
        }
      }}>
      {currentImage && images.length > 0
        ? <FlipperImage
          key={currentImage.body.attachment_url}
          crop={this.props.crop}
          transformImage={this.props.transformImage}
          dispatch={this.props.dispatch}
          getConfigValue={this.props.getConfigValue}
          flipperId={this.props.id}
          env={this.props.env}
          target={this.props.target}
          hover={this.props.hover}
          onImageLoad={this.onImageLoad}
          dark={dark}
          image={currentImage} />
        : <PlaceholderImg textOverlay={Content.NO_IMAGES_YET}
          dark={dark} />}
      {multipleImages && !this.state.disablePrev &&
        <button
          onClick={this.go(1)}
          autoFocus={this.props.autoFocus}
          title={t("previous image")}
          className="image-flipper-left fb-button">
          <i className={"fa fa-chevron-left"} />
        </button>}
      {multipleImages && !this.state.disableNext &&
        <button
          onClick={this.go(-1)}
          title={t("next image")}
          className="image-flipper-right fb-button">
          <i className={"fa fa-chevron-right"} />
        </button>}
    </div>;
  }
}