Implement MySomethingList for social media, use list item slot

This commit is contained in:
Wolfgang Huß 2021-11-03 13:04:45 +01:00
parent 2988e0c75b
commit d3cc49d37b
6 changed files with 628 additions and 342 deletions

View File

@ -0,0 +1,187 @@
import { mount } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import MySomethingList from './MySomethingList.vue'
import Vuex from 'vuex'
import Vue from 'vue'
const localVue = global.localVue
describe('MySomethingList.vue', () => {
let wrapper
let mocks
let getters
const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
const newSocialMediaUrl = 'https://twitter.com/mattwr18'
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest.fn(),
},
$toast: {
error: jest.fn(),
success: jest.fn(),
},
}
getters = {
'auth/user': () => {
return {}
},
}
})
describe('mount', () => {
let form, input, slots, submitButton
const Wrapper = () => {
const store = new Vuex.Store({
getters,
})
slots = { 'list-item': '<div class="list-item"></div>' }
return mount(MySomethingList, { store, mocks, localVue, slots })
}
describe('adding social media link', () => {
beforeEach(() => {
wrapper = Wrapper()
form = wrapper.find('form')
input = wrapper.find('input#addSocialMedia')
submitButton = wrapper.find('button')
})
it('requires the link to be a valid url', async () => {
input.setValue('some value')
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('displays an error message when not saved successfully', async () => {
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
await flushPromises()
expect(mocks.$toast.error).toHaveBeenCalledTimes(1)
})
describe('success', () => {
beforeEach(async () => {
mocks.$apollo.mutate.mockResolvedValue({
data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
})
it('sends the new url to the backend', () => {
const expected = expect.objectContaining({
variables: { url: newSocialMediaUrl },
})
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('displays a success message', async () => {
await flushPromises()
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
it('clears the form', async () => {
await flushPromises()
expect(input.value).toBe(undefined)
expect(submitButton.vm.$attrs.disabled).toBe(true)
})
})
})
describe('given existing social media links', () => {
beforeEach(() => {
getters = {
'auth/user': () => ({
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}),
}
wrapper = Wrapper()
form = wrapper.find('form')
})
describe('for each item it', () => {
it('displays the item as slot "list-item"', () => {
expect(wrapper.find('.list-item').exists()).toBe(true)
})
it('displays the edit button', () => {
expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true)
})
it('displays the delete button', () => {
expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true)
})
})
it('does not accept a duplicate url', async () => {
wrapper.find('input#addSocialMedia').setValue(socialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
describe('editing social media link', () => {
beforeEach(async () => {
const editButton = wrapper.find('.base-button[data-test="edit-button"]')
editButton.trigger('click')
await Vue.nextTick()
input = wrapper.find('input#editSocialMedia')
})
it('disables adding new links while editing', () => {
const addInput = wrapper.find('input#addSocialMedia')
expect(addInput.exists()).toBe(false)
})
it('sends the new url to the backend', async () => {
const expected = expect.objectContaining({
variables: { id: 's1', url: newSocialMediaUrl },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('allows the user to cancel editing', async () => {
const cancelButton = wrapper.find('button#cancel')
cancelButton.trigger('click')
await Vue.nextTick()
expect(wrapper.find('input#editSocialMedia').exists()).toBe(false)
})
})
describe('deleting social media link', () => {
beforeEach(async () => {
const deleteButton = wrapper.find('.base-button[data-test="delete-button"]')
deleteButton.trigger('click')
await Vue.nextTick()
})
it('sends the link id to the backend', () => {
const expected = expect.objectContaining({
variables: { id: 's1' },
})
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('displays a success message', async () => {
await flushPromises()
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
})
})
})
})

View File

@ -0,0 +1,261 @@
<template>
<base-card>
<ds-heading tag="h2" class="title">{{ $t('settings.social-media.name') }}</ds-heading>
<ds-form
v-model="formData"
:schema="formSchema"
@input="handleInput"
@input-valid="handleInputValid"
@submit="handleSubmitSocialMedia"
>
<div v-if="editingLink.id">
<!-- Wolle translation -->
<ds-space margin="base">
<ds-heading tag="h3" class="undertitle">
{{ /* $t('settings.social-media.name') */ 'Edit "' + editingLink.url + '"' }}
</ds-heading>
</ds-space>
<ds-space v-if="socialMediaLinks" margin-top="base" margin="base">
<ds-input
id="editSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
</ds-space>
</div>
<div v-else>
<ds-space v-if="socialMediaLinks" margin-top="base" margin="small">
<ds-list>
<ds-list-item v-for="link in socialMediaLinks" :key="link.id" class="list-item--high">
<!-- Wolle remove template tag? -->
<template>
<!-- Wolle <a :href="link.url" target="_blank">
<img :src="link.favicon" alt="Link:" height="16" width="16" />
{{ link.url }}
</a> -->
<!-- Wolle <slot name="list-item" :link="link" data-test="item-slot" /> -->
<slot name="list-item" :link="link" />
<span class="divider">|</span>
<base-button
icon="edit"
circle
ghost
@click="handleEditSocialMedia(link)"
:title="$t('actions.edit')"
data-test="edit-button"
/>
<base-button
icon="trash"
circle
ghost
@click="handleDeleteSocialMedia(link)"
:title="$t('actions.delete')"
data-test="delete-button"
/>
</template>
</ds-list-item>
</ds-list>
</ds-space>
</div>
<ds-space margin-top="base">
<ds-input
v-if="!editingLink.id"
id="addSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
<ds-space margin-top="base">
<base-button filled :disabled="disabled" type="submit">
{{ editingLink.id ? $t('actions.save') : $t('settings.social-media.submit') }}
</base-button>
<base-button v-if="editingLink.id" id="cancel" danger @click="handleCancel()">
{{ $t('actions.cancel') }}
</base-button>
</ds-space>
</ds-space>
</ds-form>
</base-card>
</template>
<script>
import unionBy from 'lodash/unionBy'
import gql from 'graphql-tag'
import { mapGetters, mapMutations } from 'vuex'
export default {
name: 'MySomethingList',
data() {
return {
formData: {
socialMediaUrl: '',
},
formSchema: {
socialMediaUrl: {
type: 'url',
message: this.$t('common.validations.url'),
},
},
disabled: true,
editingLink: {},
}
},
computed: {
...mapGetters({
currentUser: 'auth/user',
}),
socialMediaLinks() {
const domainRegex = /^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g
const { socialMedia = [] } = this.currentUser
return socialMedia.map(({ id, url }) => {
const [domain] = url.match(domainRegex) || []
const favicon = domain ? `${domain}/favicon.ico` : null
return { id, url, favicon }
})
},
},
methods: {
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
handleCancel() {
this.editingLink = {}
this.formData.socialMediaUrl = ''
this.disabled = true
},
handleEditSocialMedia(link) {
this.editingLink = link
this.formData.socialMediaUrl = link.url
},
handleInput(data) {
this.disabled = true
},
handleInputValid(data) {
if (data.socialMediaUrl.length < 1) {
this.disabled = true
} else {
this.disabled = false
}
},
async handleDeleteSocialMedia(link) {
try {
await this.$apollo.mutate({
mutation: gql`
mutation($id: ID!) {
DeleteSocialMedia(id: $id) {
id
url
}
}
`,
variables: {
id: link.id,
},
update: (store, { data }) => {
const socialMedia = this.currentUser.socialMedia.filter(
(element) => element.id !== link.id,
)
this.setCurrentUser({
...this.currentUser,
socialMedia,
})
},
})
this.$toast.success(this.$t('settings.social-media.successDelete'))
} catch (err) {
this.$toast.error(err.message)
}
},
async handleSubmitSocialMedia() {
const isEditing = !!this.editingLink.id
const url = this.formData.socialMediaUrl
const duplicateUrl = this.socialMediaLinks.find((link) => link.url === url)
if (duplicateUrl && duplicateUrl.id !== this.editingLink.id) {
return this.$toast.error(this.$t('settings.social-media.requireUnique'))
}
let mutation = gql`
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
`
const variables = { url }
let successMessage = this.$t('settings.social-media.successAdd')
if (isEditing) {
mutation = gql`
mutation($id: ID!, $url: String!) {
UpdateSocialMedia(id: $id, url: $url) {
id
url
}
}
`
variables.id = this.editingLink.id
successMessage = this.$t('settings.data.success')
}
try {
await this.$apollo.mutate({
mutation,
variables,
update: (store, { data }) => {
const newSocialMedia = isEditing ? data.UpdateSocialMedia : data.CreateSocialMedia
this.setCurrentUser({
...this.currentUser,
socialMedia: unionBy([newSocialMedia], this.currentUser.socialMedia, 'id'),
})
},
})
this.$toast.success(successMessage)
this.formData.socialMediaUrl = ''
this.disabled = true
this.editingLink = {}
} catch (err) {
this.$toast.error(err.message)
}
},
},
}
</script>
<style lang="scss" scope>
// Wolle .title {
// font-size: $font-size-xx-large;
// margin-top: $space-small;
// // Wolle margin-bottom: $space-small;
// }
.undertitle {
font-size: $font-size-base;
// margin-top: $space-base;
}
.divider {
opacity: 0.4;
padding: 0 $space-small;
}
.icon-button {
cursor: pointer;
}
.list-item--high {
.ds-list-item-prefix {
align-self: center;
}
.ds-list-item-content {
display: flex;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,37 @@
import { shallowMount } from '@vue/test-utils'
import SocialMediaListItem from './SocialMediaListItem.vue'
describe('SocialMediaListItem.vue', () => {
let wrapper
let propsData
const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
const faviconUrl = 'https://freeradical.zone/favicon.ico'
beforeEach(() => {
propsData = {}
})
describe('shallowMount', () => {
const Wrapper = () => {
return shallowMount(SocialMediaListItem, { propsData })
}
describe('given existing social media links', () => {
beforeEach(() => {
propsData = { link: { id: 's1', url: socialMediaUrl, favicon: faviconUrl } }
wrapper = Wrapper()
})
describe('for each link it', () => {
it('displays the favicon', () => {
expect(wrapper.find(`img[src="${faviconUrl}"]`).exists()).toBe(true)
})
it('displays the url', () => {
expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true)
})
})
})
})
})

View File

@ -0,0 +1,18 @@
<template>
<a :href="link.url" target="_blank">
<img :src="link.favicon" alt="Link:" height="16" width="16" />
{{ link.url }}
</a>
</template>
<script>
export default {
name: 'SocialMediaListItem',
props: {
link: {
type: Object,
default: () => ({}),
},
},
}
</script>

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils'
import flushPromises from 'flush-promises'
// import flushPromises from 'flush-promises'
import MySocialMedia from './my-social-media.vue'
import Vuex from 'vuex'
import Vue from 'vue'
@ -11,19 +11,19 @@ describe('my-social-media.vue', () => {
let mocks
let getters
const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
const newSocialMediaUrl = 'https://twitter.com/mattwr18'
// const newSocialMediaUrl = 'https://twitter.com/mattwr18'
const faviconUrl = 'https://freeradical.zone/favicon.ico'
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest.fn(),
},
$toast: {
error: jest.fn(),
success: jest.fn(),
},
// $apollo: {
// mutate: jest.fn(),
// },
// $toast: {
// error: jest.fn(),
// success: jest.fn(),
// },
}
getters = {
'auth/user': () => {
@ -33,7 +33,7 @@ describe('my-social-media.vue', () => {
})
describe('mount', () => {
let form, input, submitButton
// let form, input, submitButton
const Wrapper = () => {
const store = new Vuex.Store({
getters,
@ -41,60 +41,60 @@ describe('my-social-media.vue', () => {
return mount(MySocialMedia, { store, mocks, localVue })
}
describe('adding social media link', () => {
beforeEach(() => {
wrapper = Wrapper()
form = wrapper.find('form')
input = wrapper.find('input#addSocialMedia')
submitButton = wrapper.find('button')
})
// describe('adding social media link', () => {
// beforeEach(() => {
// wrapper = Wrapper()
// form = wrapper.find('form')
// input = wrapper.find('input#addSocialMedia')
// submitButton = wrapper.find('button')
// })
it('requires the link to be a valid url', async () => {
input.setValue('some value')
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
// it('requires the link to be a valid url', async () => {
// input.setValue('some value')
// form.trigger('submit')
// await Vue.nextTick()
// expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
// })
it('displays an error message when not saved successfully', async () => {
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
await flushPromises()
expect(mocks.$toast.error).toHaveBeenCalledTimes(1)
})
// it('displays an error message when not saved successfully', async () => {
// mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
// input.setValue(newSocialMediaUrl)
// form.trigger('submit')
// await Vue.nextTick()
// await flushPromises()
// expect(mocks.$toast.error).toHaveBeenCalledTimes(1)
// })
describe('success', () => {
beforeEach(async () => {
mocks.$apollo.mutate.mockResolvedValue({
data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
})
// describe('success', () => {
// beforeEach(async () => {
// mocks.$apollo.mutate.mockResolvedValue({
// data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } },
// })
// input.setValue(newSocialMediaUrl)
// form.trigger('submit')
// await Vue.nextTick()
// })
it('sends the new url to the backend', () => {
const expected = expect.objectContaining({
variables: { url: newSocialMediaUrl },
})
// it('sends the new url to the backend', () => {
// const expected = expect.objectContaining({
// variables: { url: newSocialMediaUrl },
// })
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
// expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
// })
it('displays a success message', async () => {
await flushPromises()
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
// it('displays a success message', async () => {
// await flushPromises()
// expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
// })
it('clears the form', async () => {
await flushPromises()
expect(input.value).toBe(undefined)
expect(submitButton.vm.$attrs.disabled).toBe(true)
})
})
})
// it('clears the form', async () => {
// await flushPromises()
// expect(input.value).toBe(undefined)
// expect(submitButton.vm.$attrs.disabled).toBe(true)
// })
// })
// })
describe('given existing social media links', () => {
beforeEach(() => {
@ -105,7 +105,7 @@ describe('my-social-media.vue', () => {
}
wrapper = Wrapper()
form = wrapper.find('form')
// form = wrapper.find('form')
})
describe('for each link it', () => {
@ -117,75 +117,75 @@ describe('my-social-media.vue', () => {
expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true)
})
it('displays the edit button', () => {
expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true)
})
// it('displays the edit button', () => {
// expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true)
// })
it('displays the delete button', () => {
expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true)
})
// it('displays the delete button', () => {
// expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true)
// })
})
it('does not accept a duplicate url', async () => {
wrapper.find('input#addSocialMedia').setValue(socialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
// it('does not accept a duplicate url', async () => {
// wrapper.find('input#addSocialMedia').setValue(socialMediaUrl)
// form.trigger('submit')
// await Vue.nextTick()
// expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
// })
describe('editing social media link', () => {
beforeEach(async () => {
const editButton = wrapper.find('.base-button[data-test="edit-button"]')
editButton.trigger('click')
await Vue.nextTick()
input = wrapper.find('input#editSocialMedia')
})
// describe('editing social media link', () => {
// beforeEach(async () => {
// const editButton = wrapper.find('.base-button[data-test="edit-button"]')
// editButton.trigger('click')
// await Vue.nextTick()
// input = wrapper.find('input#editSocialMedia')
// })
it('disables adding new links while editing', () => {
const addInput = wrapper.find('input#addSocialMedia')
// it('disables adding new links while editing', () => {
// const addInput = wrapper.find('input#addSocialMedia')
expect(addInput.exists()).toBe(false)
})
// expect(addInput.exists()).toBe(false)
// })
it('sends the new url to the backend', async () => {
const expected = expect.objectContaining({
variables: { id: 's1', url: newSocialMediaUrl },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await Vue.nextTick()
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
// it('sends the new url to the backend', async () => {
// const expected = expect.objectContaining({
// variables: { id: 's1', url: newSocialMediaUrl },
// })
// input.setValue(newSocialMediaUrl)
// form.trigger('submit')
// await Vue.nextTick()
// expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
// })
it('allows the user to cancel editing', async () => {
const cancelButton = wrapper.find('button#cancel')
cancelButton.trigger('click')
await Vue.nextTick()
expect(wrapper.find('input#editSocialMedia').exists()).toBe(false)
})
})
// it('allows the user to cancel editing', async () => {
// const cancelButton = wrapper.find('button#cancel')
// cancelButton.trigger('click')
// await Vue.nextTick()
// expect(wrapper.find('input#editSocialMedia').exists()).toBe(false)
// })
// })
describe('deleting social media link', () => {
beforeEach(async () => {
const deleteButton = wrapper.find('.base-button[data-test="delete-button"]')
deleteButton.trigger('click')
await Vue.nextTick()
})
// describe('deleting social media link', () => {
// beforeEach(async () => {
// const deleteButton = wrapper.find('.base-button[data-test="delete-button"]')
// deleteButton.trigger('click')
// await Vue.nextTick()
// })
it('sends the link id to the backend', () => {
const expected = expect.objectContaining({
variables: { id: 's1' },
})
// it('sends the link id to the backend', () => {
// const expected = expect.objectContaining({
// variables: { id: 's1' },
// })
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
// expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
// expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
// })
it('displays a success message', async () => {
await flushPromises()
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
})
// it('displays a success message', async () => {
// await flushPromises()
// expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
// })
// })
})
})
})

View File

@ -1,236 +1,19 @@
<template>
<ds-form
v-model="formData"
:schema="formSchema"
@input="handleInput"
@input-valid="handleInputValid"
@submit="handleSubmitSocialMedia"
>
<base-card>
<h2 class="title">{{ $t('settings.social-media.name') }}</h2>
<ds-space v-if="socialMediaLinks" margin-top="base" margin="x-small">
<ds-list>
<ds-list-item v-for="link in socialMediaLinks" :key="link.id" class="list-item--high">
<ds-input
v-if="editingLink.id === link.id"
id="editSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
<template v-else>
<a :href="link.url" target="_blank">
<img :src="link.favicon" alt="Link:" height="16" width="16" />
{{ link.url }}
</a>
<span class="divider">|</span>
<base-button
icon="edit"
circle
ghost
@click="handleEditSocialMedia(link)"
:title="$t('actions.edit')"
data-test="edit-button"
/>
<base-button
icon="trash"
circle
ghost
@click="handleDeleteSocialMedia(link)"
:title="$t('actions.delete')"
data-test="delete-button"
/>
</template>
</ds-list-item>
</ds-list>
</ds-space>
<ds-space margin-top="base">
<ds-input
v-if="!editingLink.id"
id="addSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
<ds-space margin-top="base">
<base-button filled :disabled="disabled" type="submit">
{{ editingLink.id ? $t('actions.save') : $t('settings.social-media.submit') }}
</base-button>
<base-button v-if="editingLink.id" id="cancel" danger @click="handleCancel()">
{{ $t('actions.cancel') }}
</base-button>
</ds-space>
</ds-space>
</base-card>
</ds-form>
<my-something-list>
<template #list-item="{ link }">
<social-media-list-item :link="link" />
</template>
</my-something-list>
</template>
<script>
import unionBy from 'lodash/unionBy'
import gql from 'graphql-tag'
import { mapGetters, mapMutations } from 'vuex'
import MySomethingList from '~/components/_new/features/MySomethingList/MySomethingList.vue'
import SocialMediaListItem from '~/components/_new/features/SocialMedia/SocialMediaListItem.vue'
export default {
data() {
return {
formData: {
socialMediaUrl: '',
},
formSchema: {
socialMediaUrl: {
type: 'url',
message: this.$t('common.validations.url'),
},
},
disabled: true,
editingLink: {},
}
},
computed: {
...mapGetters({
currentUser: 'auth/user',
}),
socialMediaLinks() {
const domainRegex = /^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g
const { socialMedia = [] } = this.currentUser
return socialMedia.map(({ id, url }) => {
const [domain] = url.match(domainRegex) || []
const favicon = domain ? `${domain}/favicon.ico` : null
return { id, url, favicon }
})
},
},
methods: {
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
handleCancel() {
this.editingLink = {}
this.formData.socialMediaUrl = ''
this.disabled = true
},
handleEditSocialMedia(link) {
this.editingLink = link
this.formData.socialMediaUrl = link.url
},
handleInput(data) {
this.disabled = true
},
handleInputValid(data) {
if (data.socialMediaUrl.length < 1) {
this.disabled = true
} else {
this.disabled = false
}
},
async handleDeleteSocialMedia(link) {
try {
await this.$apollo.mutate({
mutation: gql`
mutation($id: ID!) {
DeleteSocialMedia(id: $id) {
id
url
}
}
`,
variables: {
id: link.id,
},
update: (store, { data }) => {
const socialMedia = this.currentUser.socialMedia.filter(
(element) => element.id !== link.id,
)
this.setCurrentUser({
...this.currentUser,
socialMedia,
})
},
})
this.$toast.success(this.$t('settings.social-media.successDelete'))
} catch (err) {
this.$toast.error(err.message)
}
},
async handleSubmitSocialMedia() {
const isEditing = !!this.editingLink.id
const url = this.formData.socialMediaUrl
const duplicateUrl = this.socialMediaLinks.find((link) => link.url === url)
if (duplicateUrl && duplicateUrl.id !== this.editingLink.id) {
return this.$toast.error(this.$t('settings.social-media.requireUnique'))
}
let mutation = gql`
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
`
const variables = { url }
let successMessage = this.$t('settings.social-media.successAdd')
if (isEditing) {
mutation = gql`
mutation($id: ID!, $url: String!) {
UpdateSocialMedia(id: $id, url: $url) {
id
url
}
}
`
variables.id = this.editingLink.id
successMessage = this.$t('settings.data.success')
}
try {
await this.$apollo.mutate({
mutation,
variables,
update: (store, { data }) => {
const newSocialMedia = isEditing ? data.UpdateSocialMedia : data.CreateSocialMedia
this.setCurrentUser({
...this.currentUser,
socialMedia: unionBy([newSocialMedia], this.currentUser.socialMedia, 'id'),
})
},
})
this.$toast.success(successMessage)
this.formData.socialMediaUrl = ''
this.disabled = true
this.editingLink = {}
} catch (err) {
this.$toast.error(err.message)
}
},
components: {
MySomethingList,
SocialMediaListItem,
},
}
</script>
<style lang="scss">
.divider {
opacity: 0.4;
padding: 0 $space-small;
}
.icon-button {
cursor: pointer;
}
.list-item--high {
.ds-list-item-prefix {
align-self: center;
}
.ds-list-item-content {
display: flex;
align-items: center;
}
}
</style>