Skip to content

Commit

Permalink
Sync and cache file data at checkout.
Browse files Browse the repository at this point in the history
  • Loading branch information
jgaehring committed Jun 23, 2023
1 parent 7802128 commit befa006
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 16 deletions.
57 changes: 48 additions & 9 deletions packages/field-kit/src/entities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
} from 'vue';
import { useObjectUrl } from '@vueuse/core';
import {
assoc, clone, complement, compose, curryN, equals, is,
map, mapObjIndexed, pick, propEq, when,
assoc, clone, complement, compose, curryN, equals, filter as rFilter,
is, map, mapObjIndexed, pick, propEq, when,
} from 'ramda';
import { validate, v4 as uuidv4 } from 'uuid';
import { splitFilterByType } from 'farmos';
Expand All @@ -16,12 +16,13 @@ import { syncEntities } from '../http/sync';
import SyncScheduler from '../http/SyncScheduler';
import { getRecords } from '../idb';
import { cacheEntity } from '../idb/cache';
import { cacheFileData, fmtFileData } from '../idb/files';
import { cacheFileData, fmtFileData, loadFileEntity } from '../idb/files';
import { isArrayLike } from '../utils/asArray';
import diff from '../utils/diff';
import parseFilter from '../utils/parseFilter';
import { PromiseQueue } from '../utils/promises';
import flattenEntity from '../utils/flattenEntity';
import upsert from '../utils/upsert';
import { alert } from '../warnings/alert';
import nomenclature from './nomenclature';
import {
Expand Down Expand Up @@ -421,18 +422,53 @@ export default function useEntities(options = {}) {

function useFile(fileData) {
const file = clone(fileData);
if (!file.url && file.data instanceof Blob) file.url = useObjectUrl(file.data);
if (file.data instanceof Blob) file.url = useObjectUrl(file.data);
return reactive(file);
}

function restoreFiles(reference, field) {
const revision = revisions.get(reference);
if (!is(Object, revision.files)) revision.files = {};
if (!Array.isArray(revision.files[field]) || !isProxy(revision.files[field])) {
revision.files[field] = shallowReactive([]);
revision.files[field] = reactive([]);
}
const { files: { [field]: files } } = revision;
return readonly(files);

// A safe helper for getting file entity identifiers from the host entity.
const getResources = state => state?.relationships?.[field] || [];
revision.queue.push(refState => Promise.all(getResources(refState).map(async (resource) => {
const init = await loadFileEntity(resource);
const fileData = init === null ? fmtFileData(null, null, resource) : init;
const file = useFile(fileData);
upsert(revision.files[field], 'id', file);
revision.files[field].push(file);
if (init === null) return cacheFileData(null, null, resource);
return fileData;
})).then(async (fileData) => {
if (validate(fileData.file_entity?.id)) return fileData;
// Normally the fileData's id and type can't be assumed to be the same as
// the file_entity, but if there's no file_entity, then we can be sure the
// resource identifier was provided as the fileData's id and type above.
const { id, type } = fileData;
const { data: [file_entity] } = await farm.file.fetch({ filter: { id, type } });
if (!file_entity?.attributes?.uri?.url) {
return cacheFileData(null, file_entity, { id, type });
}
const { attributes: { uri: { url }, filemime } } = file_entity;
const headers = {
Accept: filemime || 'application/octet-stream',
// 'Accept-Encoding': '*',
// Connection: 'close',
};
const { data } = await farm.remote.request({
headers, responseType: 'blob', url,
});
const updated = fmtFileData(data, file_entity, { id, type });
const file = useFile(updated);
upsert(revision.files[field], 'id', file);
return cacheFileData(data, file_entity, { id, type });
}).then(() => refState).catch(() => refState));

return readonly(revision.files[field]);
}

function attachFile(reference, field, file) {
Expand Down Expand Up @@ -549,8 +585,11 @@ export default function useEntities(options = {}) {
})
.then((cache) => {
const syncOptions = { cache, filter: { id, type } };
const { length: fileCount } = Object.values(revision.files || {}).flat();
if (fileCount > 0) syncOptions.files = { [id]: revision.files };
// File data should only be synced once.
const takeUnsyncedFiles = rFilter(fileData => fileData.file_entity);
const files = map(takeUnsyncedFiles, revision.files);
const { length: fileCount } = Object.values(files || {}).flat();
if (fileCount > 0) syncOptions.files = { [id]: files };
return syncEntities(shortName, syncOptions)
.then(syncHandler(revision))
.then(({ data: [final] = [] } = {}) => final || cache);
Expand Down
16 changes: 14 additions & 2 deletions packages/field-kit/src/idb/files.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { validate, v4 as uuidv4 } from 'uuid';
import { getRecords, saveRecord } from '.';
import { getRecords, getRecordsFromIndex, saveRecord } from '.';
import dbs from './databases.js';

const validStores = dbs.binary_data.stores.map(s => s.name);
Expand Down Expand Up @@ -33,7 +33,7 @@ export function fmtFileData(data = null, file_entity = null, options = {}) {
const {
mime = data?.type || filemime || defaultMime,
references = [],
url = uri.value || null,
url = uri.url || null,
} = options;
let { filename = data?.name || entity_filename, id = entity_id, type } = options;
if (!validate(id)) id = uuidv4();
Expand Down Expand Up @@ -91,3 +91,15 @@ export async function loadFilesByHostId(fileIdentifiers) {
const resultsByHostId = await Promise.all(requestsByHostId);
return Object.fromEntries(resultsByHostId);
}

export function loadFileEntity(fileEntity) {
const db = 'binary_data';
const index = 'file_entity_id';
function loader(entity, stores) {
if (stores.length <= 0) return null;
const [store, ...tail] = stores;
return getRecordsFromIndex(db, store, index, entity.id)
.catch(() => loader(entity, tail));
}
return loader(fileEntity, validStores);
}
13 changes: 8 additions & 5 deletions packages/field-module-observations/src/ObservationsContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@
</div>
</div>

<div class="form-item form-item-name form-group">
<div
v-for="(image, i) in images"
:key="`preview-${i}`"
class="form-item form-item-name form-group">
<!-- NOTE: Display is set to 'none' if the img fails to load. -->
<img
v-for="(image, i) in images"
:src="image.url"
:key="`preview-${i}`"
onerror="this.style.display='none'"
class="preview"/>
</div>
Expand Down Expand Up @@ -105,8 +106,10 @@ export default {
attachFile(current, 'image', filelist);
}
const images = computed(() =>
(current.value ? restoreFiles(current.value, 'image') : []));
const images = computed(() => {
if (!current.value) return [];
return restoreFiles(current.value, 'image');
});
const save = () => commit(current.value);
const cancel = () => { currentID.value = undefined; };
Expand Down

0 comments on commit befa006

Please sign in to comment.