-
Notifications
You must be signed in to change notification settings - Fork 324
/
photos.tsx
130 lines (121 loc) · 3.77 KB
/
photos.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import * as React from "react";
import * as _ from "lodash";
import * as moment from "moment";
import { t } from "i18next";
import { success, error } from "farmbot-toastr";
import { ImageFlipper } from "./image_flipper";
import { PhotosProps } from "./interfaces";
import { getDevice } from "../../device";
import { Content } from "../../constants";
import { selectImage } from "./actions";
import { safeStringFetch } from "../../util";
import { destroy } from "../../api/crud";
import {
downloadProgress
} from "../../devices/components/fbos_settings/os_update_button";
import { JobProgress, TaggedImage } from "farmbot";
interface MetaInfoProps {
/** Default conversion is `attr_name ==> Attr Name`.
* Setting a label property will over ride it to a differrent value.
*/
label?: string;
attr: string;
// tslint:disable-next-line:no-any
obj: any; /** Really, it's OK here! See safeStringFetch */
}
function MetaInfo({ obj, attr, label }: MetaInfoProps) {
const top = label || _.startCase(attr.split("_").join());
const bottom = safeStringFetch(obj, attr);
return <div>
<label>{top}:</label>
<span>{bottom || t("unknown")}</span>
</div>;
}
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>;
const PhotoButtons = (props: {
takePhoto: () => void,
deletePhoto: () => void,
imageJobs: JobProgress[]
}) => {
const imageUploadJobProgress = downloadProgress(props.imageJobs[0]);
return <div className="farmware-button">
<button
className="fb-button green"
onClick={props.takePhoto}>
{t("Take Photo")}
</button>
<button
className="fb-button red"
onClick={props.deletePhoto}>
{t("Delete Photo")}
</button>
<p>
{imageUploadJobProgress &&
`${t("uploading photo")}...${imageUploadJobProgress}`}
</p>
</div>;
};
export const PhotoFooter = ({ image, timeOffset }: {
image: TaggedImage | undefined,
timeOffset: number
}) => {
const created_at = image
? moment(image.body.created_at)
.utcOffset(timeOffset)
.format("MMMM Do, YYYY h:mma")
: "";
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>;
};
export class Photos extends React.Component<PhotosProps, {}> {
takePhoto = () => {
const ok = () => success(t(Content.PROCESSING_PHOTO), t("Success"));
const no = () => error(t("Error taking photo"), t("Error"));
getDevice().takePhoto().then(ok, no);
}
deletePhoto = () => {
const img = this.props.currentImage || this.props.images[0];
if (img && img.uuid) {
this.props.dispatch(destroy(img.uuid))
.then(() => success(t("Image Deleted."), t("Success")))
.catch(() => error(t("Could not delete image."), t("Error")));
}
}
render() {
return <div className="photos">
<PhotoButtons
takePhoto={this.takePhoto}
deletePhoto={this.deletePhoto}
imageJobs={this.props.imageJobs} />
<ImageFlipper
onFlip={id => this.props.dispatch(selectImage(id))}
currentImage={this.props.currentImage}
images={this.props.images} />
<PhotoFooter
image={this.props.currentImage}
timeOffset={this.props.timeOffset} />
</div>;
}
}