tns-template-master-detail-kinvey
Version:
Master-detail interface to display collection of items from Kinvey and inspect and edit selected item properties.
125 lines (98 loc) • 4.71 kB
JavaScript
const observableModule = require("tns-core-modules/data/observable");
const { knownFolders, path } = require("tns-core-modules/file-system");
const { ImageSource } = require("tns-core-modules/image-source");
const imagePicker = require("nativescript-imagepicker");
const CarService = require("../shared/car-service");
const roundingValueConverter = require("./roundingValueConverter");
const visibilityValueConverter = require("./visibilityValueConverter");
const tempImageFolderName = "nsimagepicker";
function CarDetailEditViewModel(carModel) {
const viewModel = observableModule.fromObject({
// car will be fresh editable copy due to the observable.fromObject(...) wrapping
car: observableModule.fromObject(carModel),
isUpdating: false,
// set up value converter to force iOS UISlider to work with discrete steps
roundingValueConverter: roundingValueConverter,
// set up value converter to force visibility binding update in the template
visibilityValueConverter: visibilityValueConverter,
_carService: CarService.getInstance(),
_isCarImageDirty: false,
saveChanges: function () {
let queue = Promise.resolve();
this.set("isUpdating", true);
// TODO: car image should be required field
if (this._isCarImageDirty && this.car.imageUrl) {
queue = queue
.then(() => {
// no need to explicitly delete old image as upload to an existing remote path overwrites it
const localFullPath = this.car.imageUrl;
const remoteFullPath = this.car.imageStoragePath;
return this._carService.uploadImage(remoteFullPath, localFullPath);
})
.then((uploadedFile) => {
// do not raise property change event here
this.car.imageUrl = uploadedFile.url;
this._isCarImageDirty = false;
});
}
return queue
.then(() => this._carService.update(this.car))
.then(() => this.set("isUpdating", false))
.catch((errorMessage) => {
this.set("isUpdating", false);
throw errorMessage;
});
},
onImageAddRemove: function () {
if (this.car.imageUrl) {
this._handleImageChange(null);
return;
}
clearImageTempFolder();
this._pickImage();
},
_pickImage: function () {
const context = imagePicker.create({
mode: "single"
});
context
.authorize()
.then(() => context.present())
.then((selection) => selection.forEach(
(selectedAsset) => {
selectedAsset.options.height = 768;
ImageSource.fromAsset(selectedAsset)
.then((imageSource) => this._handleImageChange(imageSource));
})).catch((errorMessage) => console.log(errorMessage));
},
_handleImageChange: function (source) {
let raisePropertyChange = true;
let tempImagePath = null;
if (source) {
tempImagePath = path.join(getImageTempFolder().path, `${Date.now()}.jpg`);
raisePropertyChange = source.saveToFile(tempImagePath, "jpeg");
}
if (raisePropertyChange) {
// raise property change event here so binding in
// /cars/car-detail-edit-page/my-image-add-remove/MyImageAddRemove.xml works correctly
this.car.set("imageUrl", tempImagePath);
this._isCarImageDirty = true;
}
}
});
viewModel.car.addEventListener(observableModule.Observable.propertyChangeEvent, (propertyChangeData) => {
const propertyName = propertyChangeData.propertyName;
if (propertyName === "name" || propertyName === "imageUrl") {
// update dependent property
viewModel.car.set("isModelValid", !!viewModel.car.name && !!viewModel.car.imageUrl);
}
});
return viewModel;
}
function getImageTempFolder() {
return knownFolders.temp().getFolder(tempImageFolderName);
}
function clearImageTempFolder() {
getImageTempFolder().clear();
}
module.exports = CarDetailEditViewModel;