aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--templates/web/base/offline/service_worker.html27
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js6
-rw-r--r--web/cobrands/fixmystreet/offline.js52
3 files changed, 76 insertions, 9 deletions
diff --git a/templates/web/base/offline/service_worker.html b/templates/web/base/offline/service_worker.html
index 3c480f9b1..71e5c282a 100644
--- a/templates/web/base/offline/service_worker.html
+++ b/templates/web/base/offline/service_worker.html
@@ -52,15 +52,30 @@ addEventListener('fetch', fetchEvent => {
}
catch {
fetchEvent.waitUntil(async function() {
- var text = await request.text();
- let formData = new URLSearchParams(text);
- formData.set('save', 2);
- formData.set('saved_at', Math.floor(+new Date() / 1000));
- formData = formData.toString();
+ var request_buffer = await request.arrayBuffer();
+ var headers = request.headers;
+ let formData = {};
+ formData.contentType = headers.get('Content-Type');
+ let boundary_re = /.*boundary=(.*)/;
+ let bound = formData.contentType.match(boundary_re);
+
+ let saved = '--' + bound[1] + "\r\nContent-Disposition: form-data; name=\"saved_at\"\r\n\r\n" + Math.floor(+new Date() / 1000) + "\r\n";
+ var savedBuffer = new ArrayBuffer(saved.length);
+ var bufView = new Uint8Array(savedBuffer);
+ for (var i=0; i<saved.length; i++) {
+ bufView[i] = saved.charCodeAt(i);
+ }
+
+ var tmp = new Uint8Array(request_buffer.byteLength + savedBuffer.byteLength);
+ tmp.set(new Uint8Array(request_buffer), 0);
+ tmp.set(bufView, 0);
+ tmp.set(new Uint8Array(request_buffer), savedBuffer.byteLength);
+
+ formData.text = tmp.buffer;
var data = await idbKeyval.get('offlineData') || { cachedReports: {}, forms: [] };
var forms = data.forms;
- if (!forms.length || formData != forms[forms.length - 1][1]) {
+ if (!forms.length || tmp.toString() != new Uint8Array(forms[forms.length - 1][1].text).toString()) {
forms.push([request.url, formData]);
}
return idbKeyval.set('offlineData', data);
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index 8073530c6..ad29ab470 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -666,6 +666,12 @@ $.extend(fixmystreet.set_up, {
return;
}
+ // we don't want to create this if we're offline (e.g using the inspector
+ // panel to add a photo) as the server side bit does not work.
+ if (!navigator.onLine) {
+ return;
+ }
+
// Pass a jQuery element, eg $('.foobar'), into this function
// to limit all the selectors to that element. Handy if you want
// to only bind/detect Dropzones in a particular part of the page,
diff --git a/web/cobrands/fixmystreet/offline.js b/web/cobrands/fixmystreet/offline.js
index 908326a69..c6609fe1a 100644
--- a/web/cobrands/fixmystreet/offline.js
+++ b/web/cobrands/fixmystreet/offline.js
@@ -28,6 +28,23 @@ fixmystreet.offlineBanner = (function() {
$('.top_banner--offline').slideUp();
}
+ // Compare two typed arrays for equality
+ function isEqual(view1, view2) {
+ for (var i=0; i != view1.byteLength; i++) {
+ if (view1[i] != view2[i]) return false;
+ }
+ return true;
+ }
+
+ // Create a Uint8Array of a string
+ function makeView(str) {
+ var view = new Uint8Array(str.length);
+ for (var i=0; i<str.length; i++) {
+ view[i] = str.charCodeAt(i);
+ }
+ return view;
+ }
+
return {
make: function(offline) {
fixmystreet.offlineData.getFormsLength().then(function(num) {
@@ -61,7 +78,13 @@ fixmystreet.offlineBanner = (function() {
}
function postForm(url, data) {
- return $.ajax({ url: url, data: data, type: 'POST' }).done(nextForm);
+ return $.ajax({
+ url: url,
+ contentType: data.contentType,
+ data: data.text,
+ type: 'POST',
+ processData: false
+ }).done(nextForm);
}
$(document).on('click', '#oFN', function(e) {
@@ -83,8 +106,23 @@ fixmystreet.offlineBanner = (function() {
if (!token) {
return nextForm();
}
- var param = form[1].replace(/&token=[^&]*/, '&token=' + token);
- return postForm(form[0], param).fail(nextForm);
+
+ var tokenView = makeView(token);
+ var tokenName = makeView('name="token"\r\n\r\n');
+
+ // Make a typed array to update the request body with
+ // This only works because tokens are always the same length
+ var curView = new Uint8Array(form[1].text);
+
+ // Find the spot at which the token is in the buffer
+ var idxS = curView.findIndex(function isToken(element, i, array) {
+ var sl = array.slice(i, i+tokenName.byteLength);
+ return isEqual(sl, tokenName);
+ });
+ // Replace the old token with the new one in the right spot
+ curView.set(tokenView, idxS + tokenName.byteLength);
+
+ return postForm(form[0], form[1]).fail(nextForm);
});
});
});
@@ -302,6 +340,14 @@ fixmystreet.offline = (function() {
$('.moderate-display.segmented-control, .shadow-wrap, #update_form, #report-cta, .mysoc-footer, .nav-wrapper').hide();
$('.js-back-to-report-list').attr('href', '/my/planned');
+ // On iOS we want to hide the photo fields on the offline inspector
+ // form because including a photo entirely breaks the form submission.
+ if (/iPad|iPhone|iPod/.test(navigator.platform) ||
+ (/Mac/.test(navigator.userAgent) && 'ontouchend' in document)) // iPadOS 13 pretends to be a desktop Mac
+ {
+ $("#form_photos, label[for=form_photo]").hide();
+ }
+
// Refill form with saved data if there is any
fixmystreet.offlineData.getForms().then(function(forms) {
var savedForm;