mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge pull request #237 from Human-Connection/236-list-social-media-accounts
List socialMedia links
This commit is contained in:
commit
73f2ff59e4
@ -10,8 +10,8 @@
|
|||||||
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -e js,graphql",
|
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -e js,graphql",
|
||||||
"lint": "eslint src --config .eslintrc.js",
|
"lint": "eslint src --config .eslintrc.js",
|
||||||
"test": "run-s test:jest test:cucumber",
|
"test": "run-s test:jest test:cucumber",
|
||||||
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev",
|
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev 2> /dev/null",
|
||||||
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev",
|
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev 2> /dev/null",
|
||||||
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
|
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
|
||||||
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
||||||
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import reports from './resolvers/reports.js'
|
|||||||
import posts from './resolvers/posts.js'
|
import posts from './resolvers/posts.js'
|
||||||
import moderation from './resolvers/moderation.js'
|
import moderation from './resolvers/moderation.js'
|
||||||
import rewards from './resolvers/rewards.js'
|
import rewards from './resolvers/rewards.js'
|
||||||
|
import socialMedia from './resolvers/socialMedia.js'
|
||||||
import notifications from './resolvers/notifications'
|
import notifications from './resolvers/notifications'
|
||||||
|
|
||||||
export const typeDefs = fs
|
export const typeDefs = fs
|
||||||
@ -27,6 +28,7 @@ export const resolvers = {
|
|||||||
...posts.Mutation,
|
...posts.Mutation,
|
||||||
...moderation.Mutation,
|
...moderation.Mutation,
|
||||||
...rewards.Mutation,
|
...rewards.Mutation,
|
||||||
|
...socialMedia.Mutation,
|
||||||
...notifications.Mutation
|
...notifications.Mutation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,12 +10,14 @@ import permissionsMiddleware from './permissionsMiddleware'
|
|||||||
import userMiddleware from './userMiddleware'
|
import userMiddleware from './userMiddleware'
|
||||||
import includedFieldsMiddleware from './includedFieldsMiddleware'
|
import includedFieldsMiddleware from './includedFieldsMiddleware'
|
||||||
import orderByMiddleware from './orderByMiddleware'
|
import orderByMiddleware from './orderByMiddleware'
|
||||||
|
import validUrlMiddleware from './validUrlMiddleware'
|
||||||
import notificationsMiddleware from './notificationsMiddleware'
|
import notificationsMiddleware from './notificationsMiddleware'
|
||||||
|
|
||||||
export default schema => {
|
export default schema => {
|
||||||
let middleware = [
|
let middleware = [
|
||||||
passwordMiddleware,
|
passwordMiddleware,
|
||||||
dateTimeMiddleware,
|
dateTimeMiddleware,
|
||||||
|
validUrlMiddleware,
|
||||||
sluggifyMiddleware,
|
sluggifyMiddleware,
|
||||||
excerptMiddleware,
|
excerptMiddleware,
|
||||||
xssMiddleware,
|
xssMiddleware,
|
||||||
|
|||||||
@ -74,6 +74,7 @@ const permissions = shield({
|
|||||||
UpdateBadge: isAdmin,
|
UpdateBadge: isAdmin,
|
||||||
DeleteBadge: isAdmin,
|
DeleteBadge: isAdmin,
|
||||||
AddUserBadges: isAdmin,
|
AddUserBadges: isAdmin,
|
||||||
|
CreateSocialMedia: isAuthenticated,
|
||||||
// AddBadgeRewarded: isAdmin,
|
// AddBadgeRewarded: isAdmin,
|
||||||
// RemoveBadgeRewarded: isAdmin,
|
// RemoveBadgeRewarded: isAdmin,
|
||||||
reward: isAdmin,
|
reward: isAdmin,
|
||||||
|
|||||||
18
backend/src/middleware/validUrlMiddleware.js
Normal file
18
backend/src/middleware/validUrlMiddleware.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const validURL = str => {
|
||||||
|
const isValid = str.match(/^(?:https?:\/\/)(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g)
|
||||||
|
return !!isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Mutation: {
|
||||||
|
CreateSocialMedia: async (resolve, root, args, context, info) => {
|
||||||
|
let socialMedia
|
||||||
|
if (validURL(args.url)) {
|
||||||
|
socialMedia = await resolve(root, args, context, info)
|
||||||
|
} else {
|
||||||
|
throw Error('Input is not a URL')
|
||||||
|
}
|
||||||
|
return socialMedia
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
backend/src/resolvers/socialMedia.js
Normal file
21
backend/src/resolvers/socialMedia.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Mutation: {
|
||||||
|
CreateSocialMedia: async (object, params, context, resolveInfo) => {
|
||||||
|
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, true)
|
||||||
|
const session = context.driver.session()
|
||||||
|
await session.run(
|
||||||
|
`MATCH (owner:User {id: $userId}), (socialMedia:SocialMedia {id: $socialMediaId})
|
||||||
|
MERGE (socialMedia)<-[:OWNED]-(owner)
|
||||||
|
RETURN owner`, {
|
||||||
|
userId: context.user.id,
|
||||||
|
socialMediaId: socialMedia.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
return socialMedia
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
backend/src/resolvers/socialMedia.spec.js
Normal file
49
backend/src/resolvers/socialMedia.spec.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import Factory from '../seed/factories'
|
||||||
|
import { GraphQLClient } from 'graphql-request'
|
||||||
|
import { host, login } from '../jest/helpers'
|
||||||
|
|
||||||
|
const factory = Factory()
|
||||||
|
|
||||||
|
describe('CreateSocialMedia', () => {
|
||||||
|
let client
|
||||||
|
let headers
|
||||||
|
const mutation = `
|
||||||
|
mutation($url: String!) {
|
||||||
|
CreateSocialMedia(url: $url) {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
beforeEach(async () => {
|
||||||
|
await factory.create('User', {
|
||||||
|
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||||
|
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||||
|
name: 'Matilde Hermiston',
|
||||||
|
slug: 'matilde-hermiston',
|
||||||
|
role: 'user',
|
||||||
|
email: 'test@example.org',
|
||||||
|
password: '1234'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await factory.cleanDatabase()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||||
|
client = new GraphQLClient(host, { headers })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects empty string', async () => {
|
||||||
|
const variables = { url: '' }
|
||||||
|
await expect(client.request(mutation, variables)).rejects.toThrow('Input is not a URL')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('validates URLs', async () => {
|
||||||
|
const variables = { url: 'not-a-url' }
|
||||||
|
await expect(client.request(mutation, variables)).rejects.toThrow('Input is not a URL')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -128,6 +128,7 @@ type User {
|
|||||||
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
||||||
locationName: String
|
locationName: String
|
||||||
about: String
|
about: String
|
||||||
|
socialMedia: [SocialMedia]! @relation(name: "OWNED", direction: "OUT")
|
||||||
|
|
||||||
createdAt: String
|
createdAt: String
|
||||||
updatedAt: String
|
updatedAt: String
|
||||||
@ -318,3 +319,10 @@ type SharedInboxEndpoint {
|
|||||||
id: ID!
|
id: ID!
|
||||||
uri: String
|
uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SocialMedia {
|
||||||
|
id: ID!
|
||||||
|
url: String
|
||||||
|
ownedBy: [User]! @relation(name: "OWNED", direction: "IN")
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -61,3 +61,52 @@ Then(
|
|||||||
'I can see my new name {string} when I click on my profile picture in the top right',
|
'I can see my new name {string} when I click on my profile picture in the top right',
|
||||||
name => matchNameInUserMenu(name)
|
name => matchNameInUserMenu(name)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
When('I click on the {string} link', link => {
|
||||||
|
cy.get('a')
|
||||||
|
.contains(link)
|
||||||
|
.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
Then('I should be on the {string} page', page => {
|
||||||
|
cy.location()
|
||||||
|
.should(loc => {
|
||||||
|
expect(loc.pathname).to.eq(page)
|
||||||
|
})
|
||||||
|
.get('h3')
|
||||||
|
.should('contain', 'Social media')
|
||||||
|
})
|
||||||
|
|
||||||
|
Then('I add a social media link', () => {
|
||||||
|
cy.get("input[name='social-media']")
|
||||||
|
.type('https://freeradical.zone/peter-pan')
|
||||||
|
.get('button')
|
||||||
|
.contains('Add link')
|
||||||
|
.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
Then('it gets saved successfully', () => {
|
||||||
|
cy.get('.iziToast-message')
|
||||||
|
.should('contain', 'Updated user')
|
||||||
|
})
|
||||||
|
|
||||||
|
Then('the new social media link shows up on the page', () => {
|
||||||
|
cy.get('a[href="https://freeradical.zone/peter-pan"]')
|
||||||
|
.should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
Given('I have added a social media link', () => {
|
||||||
|
cy.openPage('/settings/my-social-media')
|
||||||
|
.get("input[name='social-media']")
|
||||||
|
.type('https://freeradical.zone/peter-pan')
|
||||||
|
.get('button')
|
||||||
|
.contains('Add link')
|
||||||
|
.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
Then('they should be able to see my social media links', () => {
|
||||||
|
cy.get('.ds-card-content')
|
||||||
|
.contains('Where else can I find Peter Pan?')
|
||||||
|
.get('a[href="https://freeradical.zone/peter-pan"]')
|
||||||
|
.should('have.length', 1)
|
||||||
|
})
|
||||||
|
|||||||
21
cypress/integration/user_profile/SocialMedia.feature
Normal file
21
cypress/integration/user_profile/SocialMedia.feature
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Feature: List Social Media Accounts
|
||||||
|
As a User
|
||||||
|
I'd like to enter my social media
|
||||||
|
So I can show them to other users to get in contact
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given I have a user account
|
||||||
|
And I am logged in
|
||||||
|
|
||||||
|
Scenario: Adding Social Media
|
||||||
|
Given I am on the "settings" page
|
||||||
|
And I click on the "Social media" link
|
||||||
|
Then I should be on the "/settings/my-social-media" page
|
||||||
|
When I add a social media link
|
||||||
|
Then it gets saved successfully
|
||||||
|
And the new social media link shows up on the page
|
||||||
|
|
||||||
|
Scenario: Other user's viewing my Social Media
|
||||||
|
Given I have added a social media link
|
||||||
|
When people visit my profile page
|
||||||
|
Then they should be able to see my social media links
|
||||||
@ -98,6 +98,10 @@ export default app => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
socialMedia {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|||||||
@ -15,7 +15,8 @@
|
|||||||
"followers": "Folgen",
|
"followers": "Folgen",
|
||||||
"following": "Folgt",
|
"following": "Folgt",
|
||||||
"shouted": "Empfohlen",
|
"shouted": "Empfohlen",
|
||||||
"commented": "Kommentiert"
|
"commented": "Kommentiert",
|
||||||
|
"socialMedia": "Wo sonst finde ich"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "Suchen",
|
"placeholder": "Suchen",
|
||||||
@ -51,6 +52,11 @@
|
|||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"name": "Sprachen"
|
"name": "Sprachen"
|
||||||
|
},
|
||||||
|
"social-media": {
|
||||||
|
"name": "Soziale Medien",
|
||||||
|
"submit": "Link hinzufügen",
|
||||||
|
"success": "Profil aktualisiert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
|
|||||||
@ -15,7 +15,8 @@
|
|||||||
"followers": "Followers",
|
"followers": "Followers",
|
||||||
"following": "Following",
|
"following": "Following",
|
||||||
"shouted": "Shouted",
|
"shouted": "Shouted",
|
||||||
"commented": "Commented"
|
"commented": "Commented",
|
||||||
|
"socialMedia": "Where else can I find"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "Search",
|
"placeholder": "Search",
|
||||||
@ -51,6 +52,11 @@
|
|||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"name": "Languages"
|
"name": "Languages"
|
||||||
|
},
|
||||||
|
"social-media": {
|
||||||
|
"name": "Social media",
|
||||||
|
"submit": "Add link",
|
||||||
|
"success": "Updated user profile"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
|
|||||||
@ -202,6 +202,37 @@
|
|||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</ds-card>
|
</ds-card>
|
||||||
|
<ds-space
|
||||||
|
v-if="user.socialMedia && user.socialMedia.length"
|
||||||
|
margin="large"
|
||||||
|
>
|
||||||
|
<ds-card style="position: relative; height: auto;">
|
||||||
|
<ds-space
|
||||||
|
margin="x-small"
|
||||||
|
>
|
||||||
|
<ds-text
|
||||||
|
tag="h5"
|
||||||
|
color="soft"
|
||||||
|
>
|
||||||
|
{{ $t('profile.socialMedia') }} {{ user.name | truncate(15) }}?
|
||||||
|
</ds-text>
|
||||||
|
<template>
|
||||||
|
<ds-space
|
||||||
|
v-for="link in socialMediaLinks"
|
||||||
|
:key="link.username"
|
||||||
|
margin="x-small"
|
||||||
|
>
|
||||||
|
<a :href="link.url">
|
||||||
|
<ds-avatar
|
||||||
|
:image="link.favicon"
|
||||||
|
/>
|
||||||
|
{{ link.username }}
|
||||||
|
</a>
|
||||||
|
</ds-space>
|
||||||
|
</template>
|
||||||
|
</ds-space>
|
||||||
|
</ds-card>
|
||||||
|
</ds-space>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
|
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
|
||||||
<ds-flex
|
<ds-flex
|
||||||
@ -348,6 +379,19 @@ export default {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return this.uniq(this.user.contributions.filter(post => !post.deleted))
|
return this.uniq(this.user.contributions.filter(post => !post.deleted))
|
||||||
|
},
|
||||||
|
socialMediaLinks() {
|
||||||
|
const { socialMedia = [] } = this.user
|
||||||
|
return socialMedia.map(socialMedia => {
|
||||||
|
const { url } = socialMedia
|
||||||
|
const matches = url.match(
|
||||||
|
/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:\/\n?]+)/g
|
||||||
|
)
|
||||||
|
const [domain] = matches || []
|
||||||
|
const favicon = domain ? `${domain}/favicon.ico` : null
|
||||||
|
const username = url.split('/').pop()
|
||||||
|
return { url, username, favicon }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@ -34,6 +34,10 @@ export default {
|
|||||||
{
|
{
|
||||||
name: this.$t('settings.security.name'),
|
name: this.$t('settings.security.name'),
|
||||||
path: `/settings/security`
|
path: `/settings/security`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: this.$t('settings.social-media.name'),
|
||||||
|
path: `/settings/my-social-media`
|
||||||
}
|
}
|
||||||
// TODO implement
|
// TODO implement
|
||||||
/* {
|
/* {
|
||||||
@ -59,6 +63,7 @@ export default {
|
|||||||
/* {
|
/* {
|
||||||
name: this.$t('settings.languages.name'),
|
name: this.$t('settings.languages.name'),
|
||||||
path: `/settings/languages`
|
path: `/settings/languages`
|
||||||
|
},
|
||||||
} */
|
} */
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
88
webapp/pages/settings/my-social-media.spec.js
Normal file
88
webapp/pages/settings/my-social-media.spec.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { mount, createLocalVue } from '@vue/test-utils'
|
||||||
|
import MySocialMedia from './my-social-media.vue'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import Styleguide from '@human-connection/styleguide'
|
||||||
|
|
||||||
|
const localVue = createLocalVue()
|
||||||
|
|
||||||
|
localVue.use(Vuex)
|
||||||
|
localVue.use(Styleguide)
|
||||||
|
|
||||||
|
describe('my-social-media.vue', () => {
|
||||||
|
let wrapper
|
||||||
|
let Wrapper
|
||||||
|
let store
|
||||||
|
let mocks
|
||||||
|
let getters
|
||||||
|
let input
|
||||||
|
let submitBtn
|
||||||
|
const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks = {
|
||||||
|
$t: jest.fn(),
|
||||||
|
$apollo: {
|
||||||
|
mutate: jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue({ message: 'Ouch!' })
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
data: { CreateSocialMeda: { id: 's1', url: socialMediaUrl } }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
$toast: {
|
||||||
|
error: jest.fn(),
|
||||||
|
success: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getters = {
|
||||||
|
'auth/user': () => {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
const Wrapper = () => {
|
||||||
|
store = new Vuex.Store({
|
||||||
|
getters
|
||||||
|
})
|
||||||
|
return mount(MySocialMedia, { store, mocks, localVue })
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders', () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.contains('div')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given currentUser has a social media account linked', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
getters = {
|
||||||
|
'auth/user': () => {
|
||||||
|
return {
|
||||||
|
socialMedia: [{ id: 's1', url: socialMediaUrl }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("displays a link to the currentUser's social media", () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
const socialMediaLink = wrapper.find('a').attributes().href
|
||||||
|
expect(socialMediaLink).toBe(socialMediaUrl)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('currentUser does not have a social media account linked', () => {
|
||||||
|
it('allows a user to add a social media link', () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
input = wrapper.find({ name: 'social-media' })
|
||||||
|
input.element.value = socialMediaUrl
|
||||||
|
input.trigger('input')
|
||||||
|
submitBtn = wrapper.find('.ds-button')
|
||||||
|
submitBtn.trigger('click')
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
107
webapp/pages/settings/my-social-media.vue
Normal file
107
webapp/pages/settings/my-social-media.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<ds-card :header="$t('settings.social-media.name')">
|
||||||
|
<ds-space
|
||||||
|
v-if="socialMediaLinks"
|
||||||
|
margin-top="base"
|
||||||
|
margin="x-small"
|
||||||
|
>
|
||||||
|
<ds-list>
|
||||||
|
<ds-list-item
|
||||||
|
v-for="link in socialMediaLinks"
|
||||||
|
:key="link.url"
|
||||||
|
>
|
||||||
|
<a :href="link.url">
|
||||||
|
<img
|
||||||
|
:src="link.favicon"
|
||||||
|
alt="Social Media link"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
>
|
||||||
|
{{ link.url }}
|
||||||
|
</a>
|
||||||
|
</ds-list-item>
|
||||||
|
</ds-list>
|
||||||
|
</ds-space>
|
||||||
|
<div>
|
||||||
|
<ds-input
|
||||||
|
v-model="value"
|
||||||
|
placeholder="Add social media url"
|
||||||
|
name="social-media"
|
||||||
|
:schema="{type: 'url'}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ds-space margin-top="base">
|
||||||
|
<div>
|
||||||
|
<ds-button
|
||||||
|
primary
|
||||||
|
@click="handleAddSocialMedia"
|
||||||
|
>
|
||||||
|
{{ $t('settings.social-media.submit') }}
|
||||||
|
</ds-button>
|
||||||
|
</div>
|
||||||
|
</ds-space>
|
||||||
|
</ds-card>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import { mapGetters, mapMutations } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'auth/user'
|
||||||
|
}),
|
||||||
|
socialMediaLinks() {
|
||||||
|
const { socialMedia = [] } = this.currentUser
|
||||||
|
return socialMedia.map(socialMedia => {
|
||||||
|
const { url } = socialMedia
|
||||||
|
const matches = url.match(
|
||||||
|
/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:\/\n?]+)/g
|
||||||
|
)
|
||||||
|
const [domain] = matches || []
|
||||||
|
const favicon = domain ? `${domain}/favicon.ico` : null
|
||||||
|
return { url, favicon }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations({
|
||||||
|
setCurrentUser: 'auth/SET_USER'
|
||||||
|
}),
|
||||||
|
handleAddSocialMedia() {
|
||||||
|
this.$apollo
|
||||||
|
.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
mutation($url: String!) {
|
||||||
|
CreateSocialMedia(url: $url) {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
url: this.value
|
||||||
|
},
|
||||||
|
update: (store, { data }) => {
|
||||||
|
const socialMedia = [
|
||||||
|
...this.currentUser.socialMedia,
|
||||||
|
data.CreateSocialMedia
|
||||||
|
]
|
||||||
|
this.setCurrentUser({
|
||||||
|
...this.currentUser,
|
||||||
|
socialMedia
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
this.$toast.success(this.$t('settings.social-media.success')),
|
||||||
|
(this.value = '')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -83,6 +83,10 @@ export const actions = {
|
|||||||
role
|
role
|
||||||
about
|
about
|
||||||
locationName
|
locationName
|
||||||
|
socialMedia {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user