From 58523f392e45cba8a803dbdef4a5ada2ab4d365e Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Thu, 17 Mar 2022 23:56:44 -0400 Subject: [PATCH 01/16] Copy TasksEdit.vue into field-module-observations. --- .../field-module-observations/TasksEdit.vue | 787 ++++++++++++++++++ 1 file changed, 787 insertions(+) create mode 100644 packages/field-module-observations/TasksEdit.vue diff --git a/packages/field-module-observations/TasksEdit.vue b/packages/field-module-observations/TasksEdit.vue new file mode 100644 index 00000000..33de320c --- /dev/null +++ b/packages/field-module-observations/TasksEdit.vue @@ -0,0 +1,787 @@ + + + + + From 4e9a0f0373cdd84419fb6bc9dcb1e85531f74030 Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Fri, 18 Mar 2022 00:35:08 -0400 Subject: [PATCH 02/16] Revert "Remove camModule." This reverts commit 4e12e119c700b77f5f5f0b1cb3161f8bb2fed4b0. --- .../field-module-observations/TasksEdit.vue | 6 +- .../field-module-observations/camModule.js | 60 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 packages/field-module-observations/camModule.js diff --git a/packages/field-module-observations/TasksEdit.vue b/packages/field-module-observations/TasksEdit.vue index 33de320c..42903e5c 100644 --- a/packages/field-module-observations/TasksEdit.vue +++ b/packages/field-module-observations/TasksEdit.vue @@ -637,11 +637,15 @@ export default { // getPhoto() { // // Obtains an image location from the camera! + // return this.$store.dispatch('getPhotoFromCamera', this.log); // }, // loadPhoto(files) { // for (let i = 0; i < files.length; i += 1) { - // // do something + // this.$store.dispatch('loadPhotoBlob', { + // file: files[i], + // log: this.log, + // }); // } // }, // }, diff --git a/packages/field-module-observations/camModule.js b/packages/field-module-observations/camModule.js new file mode 100644 index 00000000..10439944 --- /dev/null +++ b/packages/field-module-observations/camModule.js @@ -0,0 +1,60 @@ +// A Vuex module & utlities for accessing the camera via Cordova +export default { + actions: { + /* + called when the get photo button is tapped + */ + getPhotoFromCamera({ commit }, log) { + function handleResponse(photoLoc) { + const dataURL = `data:image/jpeg;base64,${photoLoc}`; + const props = { + images: log.images.concat(dataURL), + localID: log.localID, + }; + commit('updateLog', props); + } + function handleError(error) { // eslint-disable-line no-unused-vars + } + getPhotoFromCamera() // eslint-disable-line no-use-before-define + .then(handleResponse, handleError); + }, + loadPhotoBlob({ commit }, { file, log }) { + readFileData(file).then((data) => { // eslint-disable-line no-use-before-define + const props = { + images: log.images.concat(data), + localID: log.localID, + }; + commit('updateLog', props); + }); + }, + }, +}; + +/* +Utilizes the Cordova camera plugin to obtain an image URI +*/ +function getPhotoFromCamera() { + return new Promise((resolve, reject) => { + function onSuccess(imageURI) { + resolve(imageURI); + } + function onFail(message) { + reject(message); + } + + const options = { + quality: 50, + destinationType: Camera.DestinationType.DATA_URL, // eslint-disable-line no-undef + }; + navigator.camera.getPicture(onSuccess, onFail, options); + }); +} + +function readFileData(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = () => reject(reader.error); + reader.readAsDataURL(file); + }); +} From 23f9e14cbfbf5cf3bf4dd870d3f8aed66676d1f9 Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Mon, 9 Jan 2023 12:56:45 -0500 Subject: [PATCH 03/16] Scaffold Observations module. --- package-lock.json | 33 +++++++------------ package.json | 1 + .../module.config.js | 7 ++++ .../field-module-observations/package.json | 31 +++++++++++++++++ .../src/ObservationsContainer.vue | 14 ++++++++ .../src/ObservationsWidget.vue | 14 ++++++++ .../field-module-observations/src/routes.js | 11 +++++++ 7 files changed, 90 insertions(+), 21 deletions(-) create mode 100644 packages/field-module-observations/module.config.js create mode 100644 packages/field-module-observations/package.json create mode 100644 packages/field-module-observations/src/ObservationsContainer.vue create mode 100644 packages/field-module-observations/src/ObservationsWidget.vue create mode 100644 packages/field-module-observations/src/routes.js diff --git a/package-lock.json b/package-lock.json index 68ff14c2..d670a928 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "packages/create-field-module", "packages/field-components", "packages/field-kit", + "packages/field-module-observations", "packages/field-module-tasks", "packages/field-scripts" ], @@ -2319,7 +2320,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9001,7 +9001,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -9017,7 +9016,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -9033,7 +9031,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } @@ -9049,7 +9046,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } @@ -9065,7 +9061,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -9081,7 +9076,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -9097,7 +9091,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9113,7 +9106,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9129,7 +9121,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9145,7 +9136,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9161,7 +9151,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9177,7 +9166,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9193,7 +9181,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9209,7 +9196,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -9225,7 +9211,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -9241,7 +9226,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -9275,7 +9259,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=12" } @@ -9291,7 +9274,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -9307,7 +9289,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -9323,7 +9304,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -10136,6 +10116,10 @@ "integrity": "sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==", "dev": true }, + "node_modules/field-module-observations": { + "resolved": "packages/field-module-observations", + "link": true + }, "node_modules/field-module-tasks": { "resolved": "packages/field-module-tasks", "link": true @@ -18189,6 +18173,13 @@ "node": ">= 16.13.2" } }, + "packages/field-module-observations": { + "version": "2.0.0-alpha.1", + "license": "GPL-3.0-or-later", + "devDependencies": { + "@farmos.org/field-kit": "2.0.0-alpha.8" + } + }, "packages/field-module-tasks": { "version": "2.0.0-beta.2", "license": "GPL-3.0-or-later", diff --git a/package.json b/package.json index e1939214..99f3270b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "packages/create-field-module", "packages/field-components", "packages/field-kit", + "packages/field-module-observations", "packages/field-module-tasks", "packages/field-scripts" ], diff --git a/packages/field-module-observations/module.config.js b/packages/field-module-observations/module.config.js new file mode 100644 index 00000000..8c17622b --- /dev/null +++ b/packages/field-module-observations/module.config.js @@ -0,0 +1,7 @@ +export default { + name: 'observations', + label: 'Observations', + description: 'Take photos and quick notes from the farm.', + widget: './src/ObservationsWidget.vue', + routes: './src/routes', +}; diff --git a/packages/field-module-observations/package.json b/packages/field-module-observations/package.json new file mode 100644 index 00000000..1e56a78a --- /dev/null +++ b/packages/field-module-observations/package.json @@ -0,0 +1,31 @@ +{ + "name": "field-module-observations", + "version": "2.0.0-alpha.1", + "description": "Take photos and quick notes from the farm.", + "main": "module.config.js", + "type": "module", + "scripts": { + "start": "npm run dev", + "dev": "field-scripts develop-module", + "build": "field-scripts build-module --config module.config.js", + "preview": "vite preview", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/farmOS/field-kit.git" + }, + "keywords": [ + "farmOS" + ], + "author": "Jamie Gaehring (https://jgaehring.com)", + "license": "GPL-3.0-or-later", + "dependencies": {}, + "devDependencies": { + "@farmos.org/field-kit": "2.0.0-alpha.8" + }, + "bugs": { + "url": "https://github.com/farmOS/field-kit/issues" + }, + "homepage": "https://github.com/farmOS/field-kit#readme" +} diff --git a/packages/field-module-observations/src/ObservationsContainer.vue b/packages/field-module-observations/src/ObservationsContainer.vue new file mode 100644 index 00000000..6654e97e --- /dev/null +++ b/packages/field-module-observations/src/ObservationsContainer.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/packages/field-module-observations/src/ObservationsWidget.vue b/packages/field-module-observations/src/ObservationsWidget.vue new file mode 100644 index 00000000..f071252f --- /dev/null +++ b/packages/field-module-observations/src/ObservationsWidget.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/packages/field-module-observations/src/routes.js b/packages/field-module-observations/src/routes.js new file mode 100644 index 00000000..510b109e --- /dev/null +++ b/packages/field-module-observations/src/routes.js @@ -0,0 +1,11 @@ +import ObservationsContainer from './ObservationsContainer.vue'; + +const routes = [ + { + path: '/observations', + name: 'observations', + component: ObservationsContainer, + }, +]; + +export default routes; From e4f601cc57844852eb1995683477799cfa6a3cdd Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Wed, 11 Jan 2023 10:08:28 -0500 Subject: [PATCH 04/16] Use what works from old camModule, scrap the rest. --- .../field-module-observations/TasksEdit.vue | 791 ------------------ .../field-module-observations/camModule.js | 60 -- .../src/ObservationsContainer.vue | 83 +- 3 files changed, 80 insertions(+), 854 deletions(-) delete mode 100644 packages/field-module-observations/TasksEdit.vue delete mode 100644 packages/field-module-observations/camModule.js diff --git a/packages/field-module-observations/TasksEdit.vue b/packages/field-module-observations/TasksEdit.vue deleted file mode 100644 index 42903e5c..00000000 --- a/packages/field-module-observations/TasksEdit.vue +++ /dev/null @@ -1,791 +0,0 @@ - - - - - diff --git a/packages/field-module-observations/camModule.js b/packages/field-module-observations/camModule.js deleted file mode 100644 index 10439944..00000000 --- a/packages/field-module-observations/camModule.js +++ /dev/null @@ -1,60 +0,0 @@ -// A Vuex module & utlities for accessing the camera via Cordova -export default { - actions: { - /* - called when the get photo button is tapped - */ - getPhotoFromCamera({ commit }, log) { - function handleResponse(photoLoc) { - const dataURL = `data:image/jpeg;base64,${photoLoc}`; - const props = { - images: log.images.concat(dataURL), - localID: log.localID, - }; - commit('updateLog', props); - } - function handleError(error) { // eslint-disable-line no-unused-vars - } - getPhotoFromCamera() // eslint-disable-line no-use-before-define - .then(handleResponse, handleError); - }, - loadPhotoBlob({ commit }, { file, log }) { - readFileData(file).then((data) => { // eslint-disable-line no-use-before-define - const props = { - images: log.images.concat(data), - localID: log.localID, - }; - commit('updateLog', props); - }); - }, - }, -}; - -/* -Utilizes the Cordova camera plugin to obtain an image URI -*/ -function getPhotoFromCamera() { - return new Promise((resolve, reject) => { - function onSuccess(imageURI) { - resolve(imageURI); - } - function onFail(message) { - reject(message); - } - - const options = { - quality: 50, - destinationType: Camera.DestinationType.DATA_URL, // eslint-disable-line no-undef - }; - navigator.camera.getPicture(onSuccess, onFail, options); - }); -} - -function readFileData(file) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onloadend = () => resolve(reader.result); - reader.onerror = () => reject(reader.error); - reader.readAsDataURL(file); - }); -} diff --git a/packages/field-module-observations/src/ObservationsContainer.vue b/packages/field-module-observations/src/ObservationsContainer.vue index 6654e97e..7ed3806b 100644 --- a/packages/field-module-observations/src/ObservationsContainer.vue +++ b/packages/field-module-observations/src/ObservationsContainer.vue @@ -1,14 +1,91 @@ From 3a12402f5b750af47be18ffa6c1fe7b1860a9199 Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Wed, 25 Jan 2023 20:22:12 -0500 Subject: [PATCH 05/16] Clean up Observations template. --- .../src/ObservationsContainer.vue | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/packages/field-module-observations/src/ObservationsContainer.vue b/packages/field-module-observations/src/ObservationsContainer.vue index 7ed3806b..6e69622b 100644 --- a/packages/field-module-observations/src/ObservationsContainer.vue +++ b/packages/field-module-observations/src/ObservationsContainer.vue @@ -1,37 +1,39 @@ From 612f9a35f3678ad457eef9b9bed0f76b212fd632 Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Thu, 16 Feb 2023 09:44:26 -0500 Subject: [PATCH 07/16] Restructure asArray ESM exports. --- packages/field-kit/src/entities/index.js | 8 ++++---- packages/field-kit/src/http/interceptor.js | 2 +- packages/field-kit/src/utils/asArray.js | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/field-kit/src/entities/index.js b/packages/field-kit/src/entities/index.js index 0687ed8b..b9d4b47a 100644 --- a/packages/field-kit/src/entities/index.js +++ b/packages/field-kit/src/entities/index.js @@ -17,7 +17,7 @@ import SyncScheduler from '../http/SyncScheduler'; import { getRecords } from '../idb'; import { cacheEntity } from '../idb/cache'; import { cacheFileData, fmtFileData } from '../idb/files'; -import asArray, { isArrayLike } from '../utils/asArray'; +import { isArrayLike } from '../utils/asArray'; import diff from '../utils/diff'; import parseFilter from '../utils/parseFilter'; import { PromiseQueue } from '../utils/promises'; @@ -282,9 +282,9 @@ export default function useEntities(options = {}) { const { queue, state, transactions } = revision; queue.push(() => { updateStatus(STATUS_IN_PROGRESS); - return getRecords('entities', _entity, id).then((data) => { - if (data) emit(state, data); - const syncOptions = { cache: asArray(data), filter: { id, type } }; + return getRecords('entities', _entity, id).then((cache) => { + if (cache) emit(state, cache); + const syncOptions = { cache, filter: { id, type } }; return syncEntities(shortName, syncOptions) .then(syncHandler(revision)) .then(({ data: [value] = [] } = {}) => { diff --git a/packages/field-kit/src/http/interceptor.js b/packages/field-kit/src/http/interceptor.js index 77c3842c..8ea85b18 100644 --- a/packages/field-kit/src/http/interceptor.js +++ b/packages/field-kit/src/http/interceptor.js @@ -2,7 +2,7 @@ import { clone, curryN, evolve, partition, reduce, } from 'ramda'; import { getHost } from '../farm/remote'; -import asArray from '../utils/asArray'; +import { asArray } from '../utils/asArray'; import Warning from '../warnings/Warning'; function evaluateResponse(response = {}, errorMsg) { diff --git a/packages/field-kit/src/utils/asArray.js b/packages/field-kit/src/utils/asArray.js index 830cfc75..90ddbe46 100644 --- a/packages/field-kit/src/utils/asArray.js +++ b/packages/field-kit/src/utils/asArray.js @@ -1,5 +1,5 @@ // Wrap a value in an array, unless it's nullish, then return an empty array. -const asArray = value => (value ? [value] : []); +export const asArray = value => (value ? [value] : []); // Like above, but checks if the value is already an array and if so returns it as is. export const asFlatArray = value => (Array.isArray(value) ? value : asArray(value)); @@ -15,5 +15,3 @@ export const isArrayLike = value => typeof value?.[Symbol.iterator] === 'functio export const fromFlatArray = value => (isArrayLike(value) ? Array.from(value) : asArray(value)) .flat(Infinity); - -export default asArray; From 0e171756b76ba362afe7a780f287d1ca8adf19e0 Mon Sep 17 00:00:00 2001 From: Jamie Gaehring Date: Thu, 16 Feb 2023 21:07:35 -0500 Subject: [PATCH 08/16] Checkout and display all recent observation logs. --- .../src/ObservationsContainer.vue | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/packages/field-module-observations/src/ObservationsContainer.vue b/packages/field-module-observations/src/ObservationsContainer.vue index 275037bf..de001c6c 100644 --- a/packages/field-module-observations/src/ObservationsContainer.vue +++ b/packages/field-module-observations/src/ObservationsContainer.vue @@ -5,16 +5,16 @@

{{ $t('Images')}}

- + - +
+ + + {{ log.name }} + +