summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2019-03-10 20:18:14 +0100
committerGitHub <noreply@github.com>2019-03-10 20:18:14 +0100
commit7cefc8d04fe85f5fd39dddeef1842b4d85da4392 (patch)
tree6eeaf451ce548601180ef8d3287b17e1308f894e
parent7898d34a2b1240294764f8531d83734e063effbe (diff)
parent857cdebbeda28c05b8a527c57f79fedf404e8b30 (diff)
Merge pull request #981 from nextcloud/fix/conflict
Fix/conflict
-rw-r--r--css/ContactDetails.scss5
-rw-r--r--css/icons.scss18
-rw-r--r--img/delete.svg1
-rw-r--r--img/download.svg1
-rw-r--r--img/upload.svg1
-rw-r--r--src/components/ContactDetails.vue15
-rw-r--r--src/components/Properties/PropertyGroups.vue2
-rw-r--r--src/services/parseVcf.js4
-rw-r--r--src/services/validate.js6
-rw-r--r--src/store/contacts.js26
-rw-r--r--src/views/Contacts.vue6
-rw-r--r--webpack.common.js1
12 files changed, 66 insertions, 20 deletions
diff --git a/css/ContactDetails.scss b/css/ContactDetails.scss
index e4b8c8bc..85f18a80 100644
--- a/css/ContactDetails.scss
+++ b/css/ContactDetails.scss
@@ -72,6 +72,11 @@
position: relative;
height: 44px;
width: 44px;
+ // ! override default server class
+ > .icon-more-white {
+ // using #fffffe to trick the accessibility dark theme icon invert
+ @include icon-color('more', 'actions', '#fffffe', 1, true);
+ }
}
.header-icon {
height: 44px;
diff --git a/css/icons.scss b/css/icons.scss
index 60cb2b32..6192e631 100644
--- a/css/icons.scss
+++ b/css/icons.scss
@@ -40,17 +40,27 @@
@include icon-color('up', 'contacts', $color-black, 1);
}
+.icon-up-force-white {
+ // using #fffffe to trick the accessibility dark theme icon invert
+ @include icon-color('up', 'contacts', '#fffffe', 1);
+}
+
+.icon-history-force-white {
+ // using #fffffe to trick the accessibility dark theme icon invert
+ @include icon-color('history', 'actions', '#fffffe', 1, true);
+}
+
.icon-delete-force-white {
// using #fffffe to trick the accessibility dark theme icon invert
- @include icon-color('delete', 'contacts', '#fffffe', 1);
+ @include icon-color('delete', 'actions', '#fffffe', 1, true);
}
.icon-download-force-white {
// using #fffffe to trick the accessibility dark theme icon invert
- @include icon-color('download', 'contacts', '#fffffe', 1);
+ @include icon-color('download', 'actions', '#fffffe', 1, true);
}
.icon-upload-force-white {
// using #fffffe to trick the accessibility dark theme icon invert
- @include icon-color('upload', 'contacts', '#fffffe', 1);
-} \ No newline at end of file
+ @include icon-color('upload', 'actions', '#fffffe', 1, true);
+}
diff --git a/img/delete.svg b/img/delete.svg
deleted file mode 100644
index 53f0b020..00000000
--- a/img/delete.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M6.5 1L6 2H3c-.554 0-1 .446-1 1v1h12V3c0-.554-.446-1-1-1h-3l-.5-1zM3 5l.875 9c.06.55.573 1 1.125 1h6c.552 0 1.064-.45 1.125-1L13 5z"/></svg>
diff --git a/img/download.svg b/img/download.svg
deleted file mode 100644
index dd2389b2..00000000
--- a/img/download.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M6 1h4v7h5l-7 7-7-7h5z"/></svg>
diff --git a/img/upload.svg b/img/upload.svg
deleted file mode 100644
index 92ca7920..00000000
--- a/img/upload.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M8 1L2 7h4v4h4V7h4zM2 13v2h12v-2z"/></svg>
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index c5f2bfd8..b740e0d5 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -77,7 +77,7 @@
content: conflict,
show: true,
trigger: 'manual',
- }" class="header-icon header-icon--pulse icon-history-white"
+ }" class="header-icon header-icon--pulse icon-history-force-white"
@click="refreshContact" />
<div class="menu-icon">
<div v-click-outside="closeMenu" class="header-icon icon-more-white" @click="toggleMenu" />
@@ -127,6 +127,7 @@ import debounce from 'debounce'
import PQueue from 'p-queue'
import rfcProps from 'Models/rfcProps'
+import validate from 'Services/validate'
import ContactProperty from './ContactDetails/ContactDetailsProperty'
import AddNewProp from './ContactDetails/ContactDetailsAddNewProp'
@@ -162,6 +163,8 @@ export default {
data() {
return {
+ // if true, the local contact have been fixed and requires a push
+ fixed: false,
/**
* Local off-store clone of the selected contact for edition
* because we can't edit contacts data outside the store.
@@ -368,6 +371,7 @@ export default {
* Send the local clone of contact to the store
*/
async updateContact() {
+ this.fixed = false
this.loadingUpdate = true
await this.$store.dispatch('updateContact', this.localContact)
this.loadingUpdate = false
@@ -421,6 +425,9 @@ export default {
Object.create(Object.getPrototypeOf(contact)),
contact
)
+
+ this.fixed = validate(localContact)
+
this.localContact = localContact
this.loadingData = false
} catch (error) {
@@ -438,10 +445,14 @@ export default {
} else {
// create empty contact and copy inner data
// wait for an update to really push the contact on the server!
- this.localContact = Object.assign(
+ let localContact = Object.assign(
Object.create(Object.getPrototypeOf(contact)),
contact
)
+
+ this.fixed = validate(localContact)
+
+ this.localContact = localContact
this.loadingData = false
}
diff --git a/src/components/Properties/PropertyGroups.vue b/src/components/Properties/PropertyGroups.vue
index 0ac44d0b..4cdf296b 100644
--- a/src/components/Properties/PropertyGroups.vue
+++ b/src/components/Properties/PropertyGroups.vue
@@ -50,7 +50,7 @@
<script>
import debounce from 'debounce'
-import Contact from '../../models/contact'
+import Contact from 'Models/contact'
export default {
name: 'PropertyGroups',
diff --git a/src/services/parseVcf.js b/src/services/parseVcf.js
index 184c8e76..f8fc0f0c 100644
--- a/src/services/parseVcf.js
+++ b/src/services/parseVcf.js
@@ -20,8 +20,8 @@
*
*/
-import Contact from '../models/contact'
-import Store from '../store/index'
+import Contact from 'Models/contact'
+import Store from 'Store/index'
export default function parseVcf(data = '', addressbook) {
let regexp = /BEGIN:VCARD[\s\S]*?END:VCARD/mgi
diff --git a/src/services/validate.js b/src/services/validate.js
index c80542f9..e4245c4b 100644
--- a/src/services/validate.js
+++ b/src/services/validate.js
@@ -20,10 +20,11 @@
*
*/
-import Contact from '../models/contact'
+import Contact from 'Models/contact'
import checks from './checks/'
export default function(contact) {
+ let result = false
if (contact instanceof Contact) {
// Going through every checks
@@ -37,6 +38,7 @@ export default function(contact) {
console.warn('The following contact needed a correction that failed:', check.name, contact)
} else {
// SUCCESS 💪
+ result = true
console.info('The following contact has been repaired:', check.name, contact)
}
}
@@ -44,7 +46,7 @@ export default function(contact) {
console.error('Error during the check:', check.name, contact, error)
}
})
-
+ return result
} else {
throw new Error('Invalid contact provided')
}
diff --git a/src/store/contacts.js b/src/store/contacts.js
index e520d160..4b98291f 100644
--- a/src/store/contacts.js
+++ b/src/store/contacts.js
@@ -22,8 +22,8 @@
import Vue from 'vue'
import ICAL from 'ical.js'
-import Contact from '../models/contact'
-import validate from '../services/validate'
+import Contact from 'Models/contact'
+import validate from 'Services/validate'
const state = {
// Using objects for performance
@@ -232,6 +232,22 @@ const mutations = {
*/
setOrder(state, orderKey = 'displayName') {
state.orderKey = orderKey
+ },
+
+ /**
+ * Set a contact as `in conflict` with the server data
+ *
+ * @param {Object} state the store data
+ * @param {Object} data destructuring object
+ * @param {Contact} data.contact the contact to update
+ * @param {String} data.etag the etag to set
+ */
+ setContactAsConflict(state, { contact, etag }) {
+ if (state.contacts[contact.key] && contact instanceof Contact) {
+ state.contacts[contact.key].conflict = etag
+ } else {
+ console.error('Error while handling the following contact', contact)
+ }
}
}
@@ -314,12 +330,14 @@ const actions = {
// all clear, let's update the store
context.commit('updateContact', contact)
})
- .catch((error) => {
+ .catch(error => {
+ console.info(error)
// wrong etag, we most likely have a conflict
if (error && error.status === 412) {
// saving the new etag so that the user can manually
// trigger a fetchCompleteData without any further errors
- contact.conflict = error.xhr.getResponseHeader('etag')
+ context.commit('setContactAsConflict', { contact, etag: error.xhr.getResponseHeader('etag') })
+ console.error('This contact is outdated, the server refused it', contact)
}
})
} else {
diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue
index d785d748..5e6d31de 100644
--- a/src/views/Contacts.vue
+++ b/src/views/Contacts.vue
@@ -24,7 +24,7 @@
<template>
<app-content app-name="contacts" :class="{'icon-loading': loading}">
<!-- new-contact-button + navigation + settings -->
- <template slot="navigation">
+ <app-navigation slot="navigation">
<!-- new-contact-button -->
<app-navigation-new v-if="!loading" button-id="new-contact-button" :text="t('contacts','New contact')"
button-class="icon-add" :disabled="!defaultAddressbook" @click="newContact" />
@@ -38,7 +38,7 @@
<app-navigation-settings v-if="!loading">
<settings-section />
</app-navigation-settings>
- </template>
+ </app-navigation>
<template slot="content">
<!-- go back to list when in details mode -->
@@ -64,6 +64,7 @@
<script>
import {
AppContent,
+ AppNavigation,
AppNavigationItem,
AppNavigationNew,
AppNavigationSettings
@@ -87,6 +88,7 @@ export default {
components: {
AppContent,
+ AppNavigation,
AppNavigationItem,
AppNavigationNew,
AppNavigationSettings,
diff --git a/webpack.common.js b/webpack.common.js
index 5320f430..80c655a4 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -52,6 +52,7 @@ module.exports = {
Mixins: path.resolve(__dirname, 'src/mixins/'),
Models: path.resolve(__dirname, 'src/models/'),
Services: path.resolve(__dirname, 'src/services/'),
+ Store: path.resolve(__dirname, 'src/store/'),
Views: path.resolve(__dirname, 'src/views/')
},
extensions: ['*', '.js', '.vue', '.json']