You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

249 lines
8.7 KiB

/** @odoo-module **/
import { isMobileOS } from "@web/core/browser/feature_detection";
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { url } from "@web/core/utils/urls";
import { isBinarySize } from "@web/core/utils/binary";
import { jsonrpc } from "@web/core/network/rpc_service";
import { FileUploader } from "@web/views/fields/file_handler";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { Component, useState, onWillUpdateProps,useRef} from "@odoo/owl";
const { DateTime } = luxon;
export const fileTypeMagicWordMap = {
"/": "jpg",
R: "gif",
i: "png",
P: "svg+xml",
};
const placeholder = "/web/static/img/placeholder.png";
export function imageCacheKey(value) {
if (value instanceof DateTime) {
return value.ts;
}
return "";
}
class imageCapture extends Component {
static template = "CaptureImage";
static components = {
FileUploader,};
static props = {
...standardFieldProps,
enableZoom: { type: Boolean, optional: true },
zoomDelay: { type: Number, optional: true },
previewImage: { type: String, optional: true },
acceptedFileExtensions: { type: String, optional: true },
width: { type: Number, optional: true },
height: { type: Number, optional: true },
reload: { type: Boolean, optional: true },
};
static defaultProps = {
acceptedFileExtensions: "image/*",
reload: true,
};
setup() {
this.notification = useService("notification");
this.orm = useService("orm")
this.isMobile = isMobileOS();
this.state = useState({
isValid: true,
stream: null,
});
this.player = useRef("player");
this.capture = useRef("capture");
this.camera = useRef("camera");
this.save_image = useRef("save_image");
this.rawCacheKey = this.props.record.data.write_date;
onWillUpdateProps((nextProps) => {
const { record } = this.props;
const { record: nextRecord } = nextProps;
if (record.resId !== nextRecord.resId || nextRecord.mode === "readonly") {
this.rawCacheKey = nextRecord.data.write_date;
}
});
}
get sizeStyle() {
// For getting image style details
let style = "";
if (this.props.width) {
style += `max-width: ${this.props.width}px;`;
}
if (this.props.height) {
style += `max-height: ${this.props.height}px;`;
}
return style;
}
get hasTooltip() {
return (
this.props.enableZoom && this.props.readonly && this.props.record.data[this.props.name]
);
}
getUrl(previewFieldName) {
// getting the details and url of the image
if (!this.props.reload && this.lastURL) {
return this.lastURL;
}
if (this.state.isValid && this.props.record.data[this.props.name]) {
if (isBinarySize(this.props.record.data[this.props.name])) {
if (!this.rawCacheKey) {
this.rawCacheKey = this.props.record.data.write_date;
}
this.lastURL = url("/web/image", {
model: this.props.record.resModel,
id: this.props.record.resId,
field: previewFieldName,
unique: imageCacheKey(this.rawCacheKey),
});
} else {
// Use magic-word technique for detecting image type
const magic =
fileTypeMagicWordMap[this.props.record.data[this.props.name][0]] || "png";
this.lastURL = `data:image/${magic};base64,${
this.props.record.data[this.props.name]
}`;
}
return this.lastURL;
}
return placeholder;
}
onFileRemove() {
// removing the images
this.state.isValid = true;
this.props.record.update({ [this.props.name]: false });
}
async onFileUploaded(info) {
// Upload the images
this.state.isValid = true;
this.rawCacheKey = null;
this.props.record.update({ [this.props.name]: info.data });
}
onFileCaptureImage() {
// Open a window for open the image and capture it
var field = this.props.name;
var id = this.props.record.data.id;
var model = this.props.record.resModel;
}
async OnClickOpenCamera() {
// opening the camera for capture the image
this.player.el.classList.remove('d-none');
this.capture.el.classList.remove('d-none');
this.camera.el.classList.add('d-none');
this.state.stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
this.player.el.srcObject = this.state.stream;
}
stopTracksOnMediaStream(mediaStream) {
for (const track of mediaStream.getTracks()) {
track.stop();
}
}
async OnClickCaptureImage() {
// Capture the image from webcam and close the webcam
var context = snapshot.getContext('2d');
var canvas = document.getElementById('snapshot')
var image = document.getElementById('image');
this.save_image.el.classList.remove('d-none');
context.drawImage(player, 0, 0, 320, 240);
image.value = context.canvas.toDataURL();
canvas.classList.remove('d-none');
this.url = context.canvas.toDataURL();
}
async OnClickSaveImage(){
// Saving the image to that field
var self = this
await jsonrpc('/web/dataset/call_kw', {
model: 'image.capture',
method: 'action_save_image',
args: [[], this.url],
kwargs: {}
}).then(function(results){
self.props.value = results
var data = {
data: results,
name : "ImageFile.png",
objectUrl: null,
size : 106252,
type: "image/png"
}
self.onFileUploaded(data)
})
this.player.el.classList.add('d-none');
var snapshot = document.getElementById('snapshot')
snapshot.classList.add('d-none');
this.capture.el.classList.add('d-none');
this.save_image.el.classList.add('d-none');
this.camera.el.classList.remove('d-none');
this.player.el.srcObject = null;
if (!this.state.stream) {
return;
}
this.stopTracksOnMediaStream(this.state.stream);
this.state.stream = null;
}
onLoadFailed() {
this.state.isValid = false;
this.notification.add(this.env._t("Could not display the selected image"), {
type: "danger",
});
}
}
export const ImageCapture = {
component: imageCapture,
displayName: _t("Image"),
supportedOptions: [
{
label: _t("Reload"),
name: "reload",
type: "boolean",
default: true,
},
{
label: _t("Enable zoom"),
name: "zoom",
type: "boolean",
},
{
label: _t("Zoom delay"),
name: "zoom_delay",
type: "number",
help: _t("Delay the apparition of the zoomed image with a value in milliseconds"),
},
{
label: _t("Accepted file extensions"),
name: "accepted_file_extensions",
type: "string",
},
{
label: _t("Size"),
name: "size",
type: "selection",
choices: [
{ label: _t("Small"), value: "[0,90]" },
{ label: _t("Medium"), value: "[0,180]" },
{ label: _t("Large"), value: "[0,270]" },
],
},
{
label: _t("Preview image"),
name: "preview_image",
type: "field",
availableTypes: ["binary"],
},
],
supportedTypes: ["binary"],
fieldDependencies: [{ name: "write_date", type: "datetime" }],
isEmpty: () => false,
extractProps: ({ attrs, options }) => ({
enableZoom: options.zoom,
zoomDelay: options.zoom_delay,
previewImage: options.preview_image,
acceptedFileExtensions: options.accepted_file_extensions,
width: options.size && Boolean(options.size[0]) ? options.size[0] : attrs.width,
height: options.size && Boolean(options.size[1]) ? options.size[1] : attrs.height,
reload: "reload" in options ? Boolean(options.reload) : true,
}),
};
registry.category("fields").add("capture_image", ImageCapture);