2017-06-29 12:54:02 -06:00
|
|
|
import * as React from "react";
|
2019-02-04 13:54:17 -07:00
|
|
|
import moment from "moment";
|
2019-06-24 15:39:49 -06:00
|
|
|
import { success, error } from "../../toast/toast";
|
2017-07-28 15:24:21 -06:00
|
|
|
import { ImageFlipper } from "./image_flipper";
|
2019-02-10 22:10:58 -07:00
|
|
|
import { PhotosProps, PhotoButtonsProps } from "./interfaces";
|
2017-10-10 11:59:08 -06:00
|
|
|
import { getDevice } from "../../device";
|
2018-06-21 15:04:21 -06:00
|
|
|
import { Content } from "../../constants";
|
2017-07-28 15:24:21 -06:00
|
|
|
import { selectImage } from "./actions";
|
2019-04-09 23:17:03 -06:00
|
|
|
import { safeStringFetch, timeFormatString } from "../../util";
|
2017-08-29 21:01:15 -06:00
|
|
|
import { destroy } from "../../api/crud";
|
2018-11-15 16:52:50 -07:00
|
|
|
import {
|
|
|
|
downloadProgress
|
|
|
|
} from "../../devices/components/fbos_settings/os_update_button";
|
2019-02-10 22:10:58 -07:00
|
|
|
import { TaggedImage } from "farmbot";
|
2019-02-04 07:32:26 -07:00
|
|
|
import { startCase } from "lodash";
|
2019-02-10 22:10:58 -07:00
|
|
|
import { MustBeOnline } from "../../devices/must_be_online";
|
2019-04-02 13:59:37 -06:00
|
|
|
import { t } from "../../i18next_wrapper";
|
2019-04-09 23:17:03 -06:00
|
|
|
import { TimeSettings } from "../../interfaces";
|
2019-12-27 11:37:54 -07:00
|
|
|
import {
|
|
|
|
cameraBtnProps
|
|
|
|
} from "../../devices/components/fbos_settings/camera_selection";
|
2017-07-07 04:49:03 -06:00
|
|
|
|
|
|
|
interface MetaInfoProps {
|
|
|
|
/** Default conversion is `attr_name ==> Attr Name`.
|
2018-12-18 11:25:17 -07:00
|
|
|
* Setting a label property will over ride it to a different value.
|
2017-07-07 04:49:03 -06:00
|
|
|
*/
|
|
|
|
label?: string;
|
|
|
|
attr: string;
|
2017-08-28 06:23:53 -06:00
|
|
|
// tslint:disable-next-line:no-any
|
2017-07-07 04:49:03 -06:00
|
|
|
obj: any; /** Really, it's OK here! See safeStringFetch */
|
|
|
|
}
|
|
|
|
|
|
|
|
function MetaInfo({ obj, attr, label }: MetaInfoProps) {
|
2019-02-04 07:32:26 -07:00
|
|
|
const top = label || startCase(attr.split("_").join());
|
2017-08-28 05:49:13 -06:00
|
|
|
const bottom = safeStringFetch(obj, attr);
|
2018-01-20 07:46:44 -07:00
|
|
|
return <div>
|
|
|
|
<label>{top}:</label>
|
2018-07-19 23:48:32 -06:00
|
|
|
<span>{bottom || t("unknown")}</span>
|
2018-01-20 07:46:44 -07:00
|
|
|
</div>;
|
2017-07-07 04:49:03 -06:00
|
|
|
}
|
2017-06-29 12:54:02 -06:00
|
|
|
|
2018-11-15 16:52:50 -07:00
|
|
|
const PhotoMetaData = ({ image }: { image: TaggedImage | undefined }) =>
|
|
|
|
<div className="image-metadata">
|
|
|
|
{image
|
|
|
|
? Object.keys(image.body.meta)
|
|
|
|
.filter(key => ["x", "y", "z"].includes(key))
|
|
|
|
.sort()
|
|
|
|
.map((key, index) =>
|
|
|
|
<MetaInfo key={index} attr={key} obj={image.body.meta} />)
|
|
|
|
: <MetaInfo
|
|
|
|
label={t("Image")}
|
|
|
|
attr={"image"}
|
|
|
|
obj={{ image: t("No meta data.") }} />}
|
|
|
|
</div>;
|
|
|
|
|
2019-02-10 22:10:58 -07:00
|
|
|
const PhotoButtons = (props: PhotoButtonsProps) => {
|
2018-11-15 16:52:50 -07:00
|
|
|
const imageUploadJobProgress = downloadProgress(props.imageJobs[0]);
|
2019-12-27 11:37:54 -07:00
|
|
|
const camDisabled = cameraBtnProps(props.env);
|
2018-11-15 16:52:50 -07:00
|
|
|
return <div className="farmware-button">
|
2019-02-10 22:10:58 -07:00
|
|
|
<MustBeOnline
|
|
|
|
syncStatus={props.syncStatus}
|
|
|
|
networkState={props.botToMqttStatus}
|
|
|
|
hideBanner={true}
|
|
|
|
lockOpen={process.env.NODE_ENV !== "production"}>
|
|
|
|
<button
|
2019-12-27 11:37:54 -07:00
|
|
|
className={`fb-button green ${camDisabled.class}`}
|
|
|
|
title={camDisabled.title}
|
|
|
|
onClick={camDisabled.click || props.takePhoto}>
|
2019-02-10 22:10:58 -07:00
|
|
|
{t("Take Photo")}
|
|
|
|
</button>
|
|
|
|
</MustBeOnline>
|
2018-11-15 16:52:50 -07:00
|
|
|
<button
|
|
|
|
className="fb-button red"
|
|
|
|
onClick={props.deletePhoto}>
|
|
|
|
{t("Delete Photo")}
|
|
|
|
</button>
|
|
|
|
<p>
|
|
|
|
{imageUploadJobProgress &&
|
|
|
|
`${t("uploading photo")}...${imageUploadJobProgress}`}
|
|
|
|
</p>
|
|
|
|
</div>;
|
|
|
|
};
|
|
|
|
|
2019-04-09 23:17:03 -06:00
|
|
|
interface PhotoFooterProps {
|
|
|
|
image: TaggedImage | undefined;
|
|
|
|
timeSettings: TimeSettings;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const PhotoFooter = ({ image, timeSettings }: PhotoFooterProps) => {
|
2018-11-15 16:52:50 -07:00
|
|
|
const created_at = image
|
|
|
|
? moment(image.body.created_at)
|
2019-04-09 23:17:03 -06:00
|
|
|
.utcOffset(timeSettings.utcOffset)
|
|
|
|
.format(`MMMM Do, YYYY ${timeFormatString(timeSettings)}`)
|
2018-11-15 16:52:50 -07:00
|
|
|
: "";
|
|
|
|
return <div className="photos-footer">
|
|
|
|
{/** Separated from <MetaInfo /> for stylistic purposes. */}
|
|
|
|
{image ?
|
|
|
|
<div className="image-created-at">
|
|
|
|
<label>{t("Created At:")}</label>
|
|
|
|
<span>
|
|
|
|
{created_at}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
: ""}
|
|
|
|
<PhotoMetaData image={image} />
|
|
|
|
</div>;
|
|
|
|
};
|
|
|
|
|
2017-06-30 10:07:30 -06:00
|
|
|
export class Photos extends React.Component<PhotosProps, {}> {
|
2017-06-29 12:54:02 -06:00
|
|
|
|
|
|
|
takePhoto = () => {
|
2019-07-02 11:03:16 -06:00
|
|
|
const ok = () => success(t(Content.PROCESSING_PHOTO));
|
|
|
|
const no = () => error(t("Error taking photo"));
|
2017-10-10 11:59:08 -06:00
|
|
|
getDevice().takePhoto().then(ok, no);
|
2017-06-29 12:54:02 -06:00
|
|
|
}
|
|
|
|
|
2018-11-15 16:52:50 -07:00
|
|
|
deletePhoto = () => {
|
2017-08-29 21:01:15 -06:00
|
|
|
const img = this.props.currentImage || this.props.images[0];
|
2020-01-03 13:04:45 -07:00
|
|
|
if (img?.uuid) {
|
2017-08-29 21:01:15 -06:00
|
|
|
this.props.dispatch(destroy(img.uuid))
|
2019-07-02 11:03:16 -06:00
|
|
|
.then(() => success(t("Image Deleted.")))
|
|
|
|
.catch(() => error(t("Could not delete image.")));
|
2017-08-29 21:01:15 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 12:54:02 -06:00
|
|
|
render() {
|
2018-06-21 15:04:21 -06:00
|
|
|
return <div className="photos">
|
2018-11-15 16:52:50 -07:00
|
|
|
<PhotoButtons
|
2019-02-10 22:10:58 -07:00
|
|
|
syncStatus={this.props.syncStatus}
|
|
|
|
botToMqttStatus={this.props.botToMqttStatus}
|
2018-11-15 16:52:50 -07:00
|
|
|
takePhoto={this.takePhoto}
|
|
|
|
deletePhoto={this.deletePhoto}
|
2019-12-27 11:37:54 -07:00
|
|
|
env={this.props.env}
|
2018-11-15 16:52:50 -07:00
|
|
|
imageJobs={this.props.imageJobs} />
|
2018-06-21 15:04:21 -06:00
|
|
|
<ImageFlipper
|
2018-11-15 16:52:50 -07:00
|
|
|
onFlip={id => this.props.dispatch(selectImage(id))}
|
2018-06-21 15:04:21 -06:00
|
|
|
currentImage={this.props.currentImage}
|
|
|
|
images={this.props.images} />
|
2018-11-15 16:52:50 -07:00
|
|
|
<PhotoFooter
|
|
|
|
image={this.props.currentImage}
|
2019-04-09 23:17:03 -06:00
|
|
|
timeSettings={this.props.timeSettings} />
|
2018-06-21 15:04:21 -06:00
|
|
|
</div>;
|
2017-06-29 12:54:02 -06:00
|
|
|
}
|
|
|
|
}
|