Merge branch 'master' of github.com:Human-Connection/Human-Connection into 1680-Direct_answer_on_Comment

This commit is contained in:
mattwr18 2020-01-31 13:08:31 +01:00
commit 701411e270
41 changed files with 1031 additions and 1299 deletions

View File

@ -64,7 +64,7 @@ Regular pair programming sessions
* we team up and work on an issue together (often using Visual Studio live sharing sessions)
Open-Source Community Meeting
* every Thursday 13:00
* bi-weekly on Mondays 13:00 (when there is no sprint retrospective)
* the link will be posted in the [discord chat](https://discord.gg/6ub73U3) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project)
* all contributors welcome!
@ -99,3 +99,34 @@ We believe in open source contributions as a learning experience everyone is
We use pair programming sessions as a tool for knowledge sharing. We can learn a lot from each other and only by sharing what we know and overcoming challenges together can we grow as a team and truly own this project collectively.
As a volunteeer you have no commitment except your own self development and your awesomeness by contributing to this free and open-source software project. Cheers to you!
## Open-Source Bounties
There are so many good reasons to contribute to Human Connection
* You learn state-of-the-art technologies
* You build your portfolio
* You contribute to a good cause
Now there is one more good reason: You can receive a small fincancial
compensation for your contribution! :tada:
### How it works
Before you can benefit from the Open-Source bounty program you **must get one
pull request approved and merged for free**. You can choose something really
quick and easy. What's important is starting a working relationship with the
team, learning the workflow, and understanding this contribution guide. You can
filter issues by 'good first issue', to get an idea where to start. Please join
our our [community chat](https://human-connection.org/discord), too.
You can filter Github issues with label [bounty](https://github.com/Human-Connection/Human-Connection/issues?q=is%3Aopen+is%3Aissue+label%3Abounty). These issues should have a second label `€<amount>`
which indicate their respective financial compensation in Euros.
You can bill us after your pull request got approved and merged into `master`.
Payment methods are up to you: Bank transfer or PayPal is fine for us. Just send
us your invoice as .pdf file attached to an E-Mail once you are done.
Our Open-Source bounty program is a work-in-progress. Based on our future
experience we will make changes and improvements. So keep an eye on this
contribution guide.

View File

@ -52,6 +52,10 @@ Check out the [contribution guideline](./CONTRIBUTING.md), too!
[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/0)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/0)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/1)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/1)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/2)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/2)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/3)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/3)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/4)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/4)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/5)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/5)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/6)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/6)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/7)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/7)
## Open-Source Bounties
You can get a small financial compensation for your contribution :moneybag: See
details in our [Contribution Guidelines](./CONTRIBUTING.md#open-source-bounties).
## Attributions

View File

@ -38,7 +38,7 @@
},
"dependencies": {
"@hapi/joi": "^17.1.0",
"@sentry/node": "^5.11.1",
"@sentry/node": "^5.11.2",
"apollo-cache-inmemory": "~1.6.5",
"apollo-client": "~2.6.8",
"apollo-link-context": "~1.0.19",
@ -60,7 +60,7 @@
"graphql-iso-date": "~3.6.1",
"graphql-middleware": "~4.0.2",
"graphql-middleware-sentry": "^3.2.1",
"graphql-shield": "~7.0.8",
"graphql-shield": "~7.0.9",
"graphql-tag": "~2.10.1",
"helmet": "~3.21.2",
"jsonwebtoken": "~8.5.1",
@ -105,7 +105,7 @@
"devDependencies": {
"@babel/cli": "~7.8.3",
"@babel/core": "~7.8.3",
"@babel/node": "~7.8.3",
"@babel/node": "~7.8.4",
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
"@babel/preset-env": "~7.8.3",
"@babel/register": "^7.8.3",
@ -116,7 +116,7 @@
"chai": "~4.2.0",
"cucumber": "~6.0.5",
"eslint": "~6.8.0",
"eslint-config-prettier": "~6.9.0",
"eslint-config-prettier": "~6.10.0",
"eslint-config-standard": "~14.1.0",
"eslint-plugin-import": "~2.20.0",
"eslint-plugin-jest": "~23.6.0",

View File

@ -2,30 +2,44 @@ import { getDriver } from '../../db/neo4j'
export const description = ''
export function up(next) {
export async function up(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
// Implement your migration here.
await transaction.run(``)
await transaction.commit()
next()
} catch (err) {
next(err)
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
} finally {
session.close()
driver.close()
}
}
export function down(next) {
export async function down(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
// Rollback your migration here.
// Implement your migration here.
await transaction.run(``)
await transaction.commit()
next()
} catch (err) {
next(err)
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
} finally {
session.close()
driver.close()
}
}

View File

@ -0,0 +1,46 @@
import { getDriver } from '../../db/neo4j'
export const description = `
This migration creates a MUTED relationship between two edges(:User) that have a pre-existing BLOCKED relationship.
It also sets the createdAt date for the BLOCKED relationship to the datetime the migration was run. This became
necessary after we redefined what it means to block someone, and what it means to mute them. Muting is about filtering
another user's content, whereas blocking means preventing that user from interacting with you/your contributions.
A blocked user will still be able to see your contributions, but will not be able to interact with them and vice versa.
`
export async function up(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
await transaction.run(
`
MATCH (blocker:User)-[blocked:BLOCKED]->(blockee:User)
MERGE (blocker)-[muted:MUTED]->(blockee)
SET muted.createdAt = toString(datetime()), blocked.createdAt = toString(datetime())
`,
)
await transaction.commit()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
} finally {
session.close()
}
}
export function down(next) {
const driver = getDriver()
const session = driver.session()
try {
// Rollback your migration here.
next()
} catch (err) {
next(err)
} finally {
session.close()
}
}

View File

@ -50,7 +50,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
MATCH (post: Post { id: $id })<-[:WROTE]-(author: User)
MATCH (user: User)
WHERE user.id in $idsOfUsers
AND NOT (user)<-[:BLOCKED]-(author)
AND NOT (user)-[:BLOCKED]-(author)
MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user)
`
break
@ -60,8 +60,8 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
MATCH (postAuthor: User)-[:WROTE]->(post: Post)<-[:COMMENTS]-(comment: Comment { id: $id })<-[:WROTE]-(author: User)
MATCH (user: User)
WHERE user.id in $idsOfUsers
AND NOT (user)<-[:BLOCKED]-(author)
AND NOT (user)<-[:BLOCKED]-(postAuthor)
AND NOT (user)-[:BLOCKED]-(author)
AND NOT (user)-[:BLOCKED]-(postAuthor)
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
`
break

View File

@ -102,6 +102,7 @@ export default shield(
PostsEmotionsCountByEmotion: allow,
PostsEmotionsByCurrentUser: isAuthenticated,
mutedUsers: isAuthenticated,
blockedUsers: isAuthenticated,
notifications: isAuthenticated,
Donations: isAuthenticated,
},
@ -139,6 +140,8 @@ export default shield(
RemovePostEmotions: isAuthenticated,
muteUser: isAuthenticated,
unmuteUser: isAuthenticated,
blockUser: isAuthenticated,
unblockUser: isAuthenticated,
markAsRead: isAuthenticated,
AddEmailAddress: isAuthenticated,
VerifyEmailAddress: isAuthenticated,

View File

@ -20,6 +20,7 @@ function clean(dirty) {
'hr',
'b',
'i',
'u',
'em',
'strong',
'a',

View File

@ -77,12 +77,18 @@ export default {
relationship: 'BLOCKED',
target: 'User',
direction: 'out',
properties: {
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
},
},
muted: {
type: 'relationship',
relationship: 'MUTED',
target: 'User',
direction: 'out',
properties: {
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
},
},
notifications: {
type: 'relationship',

View File

@ -20,7 +20,7 @@ export default {
AND NOT (
author.deleted = true OR author.disabled = true
OR resource.deleted = true OR resource.disabled = true
OR (:User { id: $thisUserId })-[:BLOCKED]-(author)
OR (:User {id: $thisUserId})-[:MUTED]->(author)
)
WITH resource, author,
[(resource)<-[:COMMENTS]-(comment:Comment) | comment] as comments,
@ -40,8 +40,7 @@ export default {
YIELD node as resource, score
MATCH (resource)
WHERE score >= 0.5
AND NOT (resource.deleted = true OR resource.disabled = true
OR (:User { id: $thisUserId })-[:BLOCKED]-(resource))
AND NOT (resource.deleted = true OR resource.disabled = true)
RETURN resource {.*, __typename: labels(resource)[0]}
LIMIT $limit
`

View File

@ -23,6 +23,21 @@ export const getMutedUsers = async context => {
return mutedUsers
}
export const getBlockedUsers = async context => {
const { neode } = context
const userModel = neode.model('User')
let blockedUsers = neode
.query()
.match('user', userModel)
.where('user.id', context.user.id)
.relationship(userModel.relationships().get('blocked'))
.to('blocked', userModel)
.return('blocked')
blockedUsers = await blockedUsers.execute()
blockedUsers = blockedUsers.records.map(r => r.get('blocked').properties)
return blockedUsers
}
export default {
Query: {
mutedUsers: async (object, args, context, resolveInfo) => {
@ -32,6 +47,13 @@ export default {
throw new UserInputError(e.message)
}
},
blockedUsers: async (object, args, context, resolveInfo) => {
try {
return getBlockedUsers(context)
} catch (e) {
throw new UserInputError(e.message)
}
},
User: async (object, args, context, resolveInfo) => {
const { email } = args
if (email) {
@ -86,7 +108,7 @@ export default {
const unmutedUser = await neode.find('User', params.id)
return unmutedUser.toJson()
},
block: async (object, args, context, resolveInfo) => {
blockUser: async (object, args, context, resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === args.id) return null
await neode.cypher(
@ -103,7 +125,7 @@ export default {
await user.relateTo(blockedUser, 'blocked')
return blockedUser.toJson()
},
unblock: async (object, args, context, resolveInfo) => {
unblockUser: async (object, args, context, resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === args.id) return null
await neode.cypher(
@ -229,7 +251,7 @@ export default {
boolean: {
followedByCurrentUser:
'MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isBlocked:
blocked:
'MATCH (this)<-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isMuted:
'MATCH (this)<-[:MUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',

View File

@ -68,10 +68,11 @@ type User {
RETURN COUNT(u) >= 1
"""
)
isBlocked: Boolean! @cypher(
blocked: Boolean! @cypher(
statement: """
MATCH (this)<-[: BLOCKED]-(u: User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
@ -207,6 +208,6 @@ type Mutation {
muteUser(id: ID!): User
unmuteUser(id: ID!): User
block(id: ID!): User
unblock(id: ID!): User
blockUser(id: ID!): User
unblockUser(id: ID!): User
}

View File

@ -288,10 +288,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/node@~7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.8.3.tgz#29784d445e135ca7214a9ac40535f2b8d2f980aa"
integrity sha512-GZpHg1gPnZTk1PvHRc4g/M5c50nHERkk3ojb5AuUTZFAjEKzDhBJcqvwWa7NrNT3W3Nf8t8Sj8JjA6rtXJ1z/g==
"@babel/node@~7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.8.4.tgz#59b2ed7e5a9df2224592f83292d77d616fbf1ab8"
integrity sha512-MlczXI/VYRnoaWHjicqrzq2z4DhRPaWQIC+C3ISEQs5z+mEccBsn7IAI5Q97ZDTnFYw6ts5IUTzqArilC/g7nw==
dependencies:
"@babel/register" "^7.8.3"
commander "^4.0.1"
@ -1275,65 +1275,65 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
"@sentry/apm@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/apm/-/apm-5.11.1.tgz#cc89fa4150056fbf009f92eca94fccc3980db34e"
integrity sha512-4iZH11p/7w9IMLT9hqNY1+EqLESltiIoF6/YsbpK93sXWGEs8VQ83IuvGuKWxajvHgDmj4ND0TxIliTsYqTqFw==
"@sentry/apm@5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@sentry/apm/-/apm-5.11.2.tgz#35961b9d2319ad21ae91f1b697998a8c523f1919"
integrity sha512-qn4HiSZ+6b1Gg+DlXdHVpiPPEbRu4IicGSbI8HTJLzrlsjoaBQPPkDwtuQUBVq21tU3RYXnTwrl9m45KuX6alA==
dependencies:
"@sentry/browser" "5.11.1"
"@sentry/hub" "5.11.1"
"@sentry/minimal" "5.11.1"
"@sentry/browser" "5.11.2"
"@sentry/hub" "5.11.2"
"@sentry/minimal" "5.11.2"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/browser@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.11.1.tgz#337ffcb52711b23064c847a07629e966f54a5ebb"
integrity sha512-oqOX/otmuP92DEGRyZeBuQokXdeT9HQRxH73oqIURXXNLMP3PWJALSb4HtT4AftEt/2ROGobZLuA4TaID6My/Q==
"@sentry/browser@5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.11.2.tgz#f0b19bd97e9f09a20e9f93a9835339ed9ab1f5a4"
integrity sha512-ls6ARX5m+23ld8OsuoPnR+kehjR5ketYWRcDYlmJDX2VOq5K4EzprujAo8waDB0o5a92yLXQ0ZSoK/zzAV2VoA==
dependencies:
"@sentry/core" "5.11.1"
"@sentry/core" "5.11.2"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/core@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.11.1.tgz#9e2da485e196ae32971545c1c49ee6fe719930e2"
integrity sha512-BpvPosVNT20Xso4gAV54Lu3KqDmD20vO63HYwbNdST5LUi8oYV4JhvOkoBraPEM2cbBwQvwVcFdeEYKk4tin9A==
"@sentry/core@5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.11.2.tgz#f2d9d37940d291dbcb9a9e4a012f76919474bdf6"
integrity sha512-IFCXGy7ebqIq/Kb8RVryCo/SjwhPcrfBmOjkicr4+DxN1UybLre2N3p9bejQMPIteOfDVHlySLYeipjTf+mxZw==
dependencies:
"@sentry/hub" "5.11.1"
"@sentry/minimal" "5.11.1"
"@sentry/hub" "5.11.2"
"@sentry/minimal" "5.11.2"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/hub@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.11.1.tgz#ddcb865563fae53852d405885c46b4c6de68a91b"
integrity sha512-ucKprYCbGGLLjVz4hWUqHN9KH0WKUkGf5ZYfD8LUhksuobRkYVyig0ZGbshECZxW5jcDTzip4Q9Qimq/PkkXBg==
"@sentry/hub@5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.11.2.tgz#a3b7ec27cd4cea2cddd75c372fbf1b4bc04c6aae"
integrity sha512-5BiDin6ZPsaiTm29rCC41MAjP1vOaKniqfjtXHVPm7FeOBA2bpHm95ncjLkshKGJTPfPZHXTpX/1IZsHrfGVEA==
dependencies:
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/minimal@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.11.1.tgz#0e705d01a567282d8fbbda2aed848b4974cc3cec"
integrity sha512-HK8zs7Pgdq7DsbZQTThrhQPrJsVWzz7MaluAbQA0rTIAJ3TvHKQpsVRu17xDpjZXypqWcKCRsthDrC4LxDM1Bg==
"@sentry/minimal@5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.11.2.tgz#ae417699342266ecd109a97e53cd9519c0893b21"
integrity sha512-oNuJuz3EZhVtamzABmPdr6lcYo06XHLWb2LvgnoNaYcMD1ExUSvhepOSyZ2h5STCMbmVgGVfXBNPV9RUTp8GZg==
dependencies:
"@sentry/hub" "5.11.1"
"@sentry/hub" "5.11.2"
"@sentry/types" "5.11.0"
tslib "^1.9.3"
"@sentry/node@^5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.11.1.tgz#2a9c18cd1209cfdf7a69b9d91303413149d2c910"
integrity sha512-FbJs0blJ36gEzE0rc2yBfA/KE+kXOLl8MUfFTcyJCBdCGF8XMETDCmgINnJ4TyBUJviwKoPw2TCk9TL2pa/A1w==
"@sentry/node@^5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.11.2.tgz#575c320b624c218d2155183f6bbe82b732bfb1f2"
integrity sha512-jYq9u76BdAbOKPuYg39Xh/+797MevzjMkCIC9cw/bQxAm6nHc3FXeKqd79O33jO4Jag0JL+Bz/0JidgrKgKgXg==
dependencies:
"@sentry/apm" "5.11.1"
"@sentry/core" "5.11.1"
"@sentry/hub" "5.11.1"
"@sentry/apm" "5.11.2"
"@sentry/core" "5.11.2"
"@sentry/hub" "5.11.2"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
cookie "^0.3.1"
@ -1607,10 +1607,10 @@
dependencies:
"@types/yargs-parser" "*"
"@types/yup@0.26.28":
version "0.26.28"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.28.tgz#6b8a98fb56ddb5c8a3a7f60996165221671c1ca1"
integrity sha512-DiT584YBKBENDzgk50LwiJLLOh+XgHpiy9p8PwSoOY696LpfrJRIeJ2AoBCG9KyjE8gWL4J64xDZgw15itecag==
"@types/yup@0.26.29":
version "0.26.29"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.29.tgz#5a533ad6f74e442436698e20b1441c68a7a1c931"
integrity sha512-M81oZOgLap0b0I/BySnpLwHjOj1BFxUKV1ytG2Kqj3jmkh8F3H11PEnk658UniftpjTXdueloOL+KZYn+SMQ9w==
"@types/zen-observable@^0.8.0":
version "0.8.0"
@ -3589,10 +3589,10 @@ escodegen@^1.11.1:
optionalDependencies:
source-map "~0.6.1"
eslint-config-prettier@~6.9.0:
version "6.9.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.9.0.tgz#430d24822e82f7deb1e22a435bfa3999fae4ad64"
integrity sha512-k4E14HBtcLv0uqThaI6I/n1LEqROp8XaPu6SO9Z32u5NlGRC07Enu1Bh2KEFw4FNHbekH8yzbIU9kUGxbiGmCA==
eslint-config-prettier@~6.10.0:
version "6.10.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz#7b15e303bf9c956875c948f6b21500e48ded6a7f"
integrity sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==
dependencies:
get-stdin "^6.0.0"
@ -4476,12 +4476,12 @@ graphql-middleware@~4.0.2:
dependencies:
graphql-tools "^4.0.5"
graphql-shield@~7.0.8:
version "7.0.8"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-7.0.8.tgz#5ade058610e1b247b0762cb2424d121e9a5f5b46"
integrity sha512-KxYMhoiv5lsHcO0HZDhYjjWLbwzreDCmqmnkLRsLNY+6P0q81KSowoNVPuoAsItkjr9m5Fa6IDObOVxSTSt5Lw==
graphql-shield@~7.0.9:
version "7.0.9"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-7.0.9.tgz#8248916e9636a7e3c05719a52fd13f2d37ccaeb2"
integrity sha512-2Dfddd2hcObCSqAj64c/Aaxvs7gaoD2QU14crj7H486QjS8jIAtEPUyLVyv8SmJ1ZD7jT6wqx6wrB15Npn5Sgw==
dependencies:
"@types/yup" "0.26.28"
"@types/yup" "0.26.29"
object-hash "^2.0.0"
yup "^0.28.0"

View File

@ -11,15 +11,24 @@ Then("I should have one item in the select dropdown", () => {
});
});
Then("the search has no results", () => {
Then("the search should not contain posts by the annoying user", () => {
cy.get(".searchable-input .ds-select-dropdown").should($li => {
expect($li).to.have.length(1);
});
cy.get(".ds-select-dropdown").should("contain", 'Nothing found');
})
cy.get(".ds-select-dropdown")
.should("not.have.class", '.search-post')
.should("not.contain", 'Spam')
});
Then("the search should contain the annoying user", () => {
cy.get(".searchable-input .ds-select-dropdown").should($li => {
expect($li).to.have.length(1);
})
cy.get(".ds-select-dropdown .user-teaser .slug").should("contain", '@spammy-spammer');
cy.get(".searchable-input .ds-select-search")
.focus()
.type("{esc}");
});
})
Then("I should see the following posts in the select dropdown:", table => {
table.hashes().forEach(({ title }) => {

View File

@ -31,6 +31,7 @@ const narratorParams = {
const annoyingParams = {
email: "spammy-spammer@example.org",
slug: 'spammy-spammer',
password: "1234",
...termsAndConditionsAgreedVersion
};
@ -39,8 +40,12 @@ Given("I am logged in", () => {
cy.login(loginCredentials);
});
Given("I am logged in as the muted user", () => {
cy.login({ email: annoyingParams.email, password: '1234' });
Given("the {string} user searches for {string}", (_, postTitle) => {
cy.logout()
.login({ email: annoyingParams.email, password: '1234' })
.get(".searchable-input .ds-select-search")
.focus()
.type(postTitle);
});
Given("we have a selection of categories", () => {
@ -123,6 +128,12 @@ When("I visit the {string} page", page => {
cy.openPage(page);
});
When("a blocked user visits the post page of one of my authored posts", () => {
cy.logout()
.login({ email: annoyingParams.email, password: annoyingParams.password })
.openPage('/post/previously-created-post')
})
Given("I am on the {string} page", page => {
cy.openPage(page);
});
@ -486,7 +497,7 @@ Given("I follow the user {string}", name => {
});
});
Given('"Spammy Spammer" wrote a post {string}', title => {
Given('{string} wrote a post {string}', (_, title) => {
cy.createCategories("cat21")
.factory()
.create("Post", {
@ -501,7 +512,7 @@ Then("the list of posts of this user is empty", () => {
cy.get(".main-container").find(".ds-space.hc-empty");
});
Then("nobody is following the user profile anymore", () => {
Then("I get removed from his follower collection", () => {
cy.get(".ds-card-content").not(".post-link");
cy.get(".main-container").contains(
".ds-card-content",
@ -533,6 +544,20 @@ When("I mute the user {string}", name => {
});
});
When("I block the user {string}", name => {
cy.neode()
.first("User", {
name
})
.then(blockedUser => {
cy.neode()
.first("User", {
name: narratorParams.name
})
.relateTo(blockedUser, "blocked");
});
});
When("I log in with:", table => {
const [firstRow] = table.hashes();
const {
@ -551,3 +576,11 @@ Then("I see only one post with the title {string}", title => {
.should("have.length", 1);
cy.get(".main-container").contains(".post-link", title);
});
Then("they should not see the comment from", () => {
cy.get(".ds-card-footer").children().should('not.have.class', 'comment-form')
})
Then("they should see a text explaining commenting is not possible", () => {
cy.get('.ds-placeholder').should('contain', "Commenting is not possible at this time on this post.")
})

View File

@ -0,0 +1,46 @@
Feature: Block a User
As a user
I'd like to have a button to block another user
To prevent him from seeing and interacting with my contributions
Background:
Given I have a user account
And there is an annoying user called "Harassing User"
And I am logged in
Scenario: Block a user
Given I am on the profile page of the annoying user
When I click on "Block user" from the content menu in the user info box
And I navigate to my "Blocked users" settings page
Then I can see the following table:
| Avatar | Name |
| | Harassing User |
Scenario: Blocked user cannot interact with my contributions
Given I block the user "Harassing User"
And I previously created a post
And a blocked user visits the post page of one of my authored posts
Then they should not see the comment from
And they should see a text explaining commenting is not possible
Scenario: Block a previously followed user
Given I follow the user "Harassing User"
When I visit the profile page of the annoying user
And I click on "Block user" from the content menu in the user info box
And I get removed from his follower collection
Scenario: Posts of blocked users are not filtered from search results
Given "Harassing User" wrote a post "You can still see my posts"
And I block the user "Harassing User"
When I search for "see"
Then I should see the following posts in the select dropdown:
| title |
| You can still see my posts |
Scenario: Blocked users can still see my posts
Given I previously created a post
And I block the user "Harassing User"
And the "blocked" user searches for "previously created"
Then I should see the following posts in the select dropdown:
| title |
| previously created post |

View File

@ -1,8 +1,7 @@
Feature: Mute a User
As a user
I'd like to have a button to mute another user
To prevent him from seeing and interacting with my contributions and also to avoid seeing his/her posts
To prevent him from seeing and interacting with my contributions
Background:
Given I have a user account
And there is an annoying user called "Spammy Spammer"
@ -22,9 +21,9 @@ Feature: Mute a User
When I visit the profile page of the annoying user
And I click on "Mute user" from the content menu in the user info box
Then the list of posts of this user is empty
And nobody is following the user profile anymore
And I get removed from his follower collection
Scenario: Posts of muted users are filtered from search results
Scenario: Posts of muted users are filtered from search results, users are not
Given we have the following posts in our database:
| id | title | content |
| im-not-muted | Post that should be seen | cause I'm not muted |
@ -36,18 +35,17 @@ Feature: Mute a User
When I mute the user "Spammy Spammer"
And I refresh the page
And I search for "Spam"
Then the search has no results
Then the search should not contain posts by the annoying user
But the search should contain the annoying user
But I search for "not muted"
Then I should see the following posts in the select dropdown:
| title |
| Post that should be seen |
Scenario: Muted users can still see my posts
Given I previously created a post
And I mute the user "Spammy Spammer"
Given I log out
And I am logged in as the muted user
When I search for "previously created"
And the "muted" user searches for "previously created"
Then I should see the following posts in the select dropdown:
| title |
| previously created post |

View File

@ -25,8 +25,8 @@
"release": "standard-version"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/core": "^7.8.4",
"@babel/preset-env": "^7.8.4",
"@babel/register": "^7.8.3",
"auto-changelog": "^1.16.2",
"bcryptjs": "^2.4.3",

View File

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>underline</title>
<path d="M7 4h2v12c0 3.37 2.63 6 6 6s6-2.63 6-6v-12h2v12c0 4.43-3.57 8-8 8s-8-3.57-8-8v-12zM5 26h20v2h-20v-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 282 B

View File

@ -11,7 +11,7 @@
"
@click.prevent="toggleMenu"
>
<user-avatar :user="user" />
<user-avatar :user="user" size="small" />
<base-icon class="dropdown-arrow" name="angle-down" />
</a>
</template>
@ -127,6 +127,10 @@ export default {
display: flex;
align-items: center;
padding-left: $space-xx-small;
> .user-avatar {
margin-right: $space-xx-small;
}
}
.avatar-menu-popover {
padding-top: $space-x-small;

View File

@ -161,7 +161,7 @@ export default {
callback: () => {
this.$emit('unmute', this.resource)
},
icon: 'user-plus',
icon: 'eye',
})
} else {
routes.push({
@ -169,6 +169,23 @@ export default {
callback: () => {
this.$emit('mute', this.resource)
},
icon: 'eye-slash',
})
}
if (this.resource.blocked) {
routes.push({
label: this.$t(`settings.blocked-users.unblock`),
callback: () => {
this.$emit('unblock', this.resource)
},
icon: 'user-plus',
})
} else {
routes.push({
label: this.$t(`settings.blocked-users.block`),
callback: () => {
this.$emit('block', this.resource)
},
icon: 'user-times',
})
}

View File

@ -5,6 +5,12 @@
<menu-bar-button :isActive="isActive.italic()" :onClick="commands.italic" icon="italic" />
<menu-bar-button
:isActive="isActive.underline()"
:onClick="commands.underline"
icon="underline"
/>
<menu-bar-button
ref="linkButton"
:isActive="isActive.link()"

View File

@ -193,6 +193,8 @@ export default {
/* workaround to avoid jumping layout when user-teaser is rendered */
.user-wrapper {
height: 36px;
position: relative;
z-index: $z-index-post-card-link;
}
.content-menu {

View File

@ -1,6 +1,6 @@
<template>
<div class="user-teaser" v-if="displayAnonymous">
<user-avatar v-if="showAvatar" />
<user-avatar v-if="showAvatar" size="small" />
<span class="info anonymous">{{ $t('profile.userAnonym') }}</span>
</div>
<dropdown
@ -158,8 +158,6 @@ export default {
.user-teaser {
display: flex;
flex-wrap: nowrap;
z-index: $z-index-post-card-link;
position: relative;
> .user-avatar {
flex-shrink: 0;

View File

@ -6,7 +6,7 @@
v-else
:src="user.avatar | proxyApiUrl"
class="image"
@error="event.target.style.display = 'none'"
@error="$event.target.style.display = 'none'"
/>
</div>
</template>
@ -75,7 +75,6 @@ export default {
> .image {
position: relative;
z-index: 5;
width: 100%;
object-fit: cover;
object-position: center;

View File

@ -71,7 +71,7 @@ export default {
},
computed: {
emptyText() {
return this.isActive && !this.pending ? this.$t('search.failed') : this.$t('search.hint')
return this.isActive && !this.loading ? this.$t('search.failed') : this.$t('search.hint')
},
isActive() {
return !isEmpty(this.previousSearchTerm)
@ -104,7 +104,7 @@ export default {
*/
onEnter(event) {
clearTimeout(this.searchProcess)
if (!this.pending) {
if (!this.loading) {
this.previousSearchTerm = this.unprocessedSearchInput
this.$emit('query', this.unprocessedSearchInput)
}

View File

@ -29,6 +29,7 @@ export default i18n => {
...user
...userCounts
...locationAndBadges
blocked
}
comments(orderBy: createdAt_asc) {
...comment

View File

@ -24,6 +24,7 @@ export default i18n => {
createdAt
followedByCurrentUser
isMuted
blocked
following(first: 7) {
...user
...userCounts

View File

@ -0,0 +1,43 @@
import gql from 'graphql-tag'
export const blockedUsers = () => {
return gql`
{
blockedUsers {
id
name
slug
avatar
about
disabled
deleted
}
}
`
}
export const blockUser = () => {
return gql`
mutation($id: ID!) {
blockUser(id: $id) {
id
name
blocked
followedByCurrentUser
}
}
`
}
export const unblockUser = () => {
return gql`
mutation($id: ID!) {
unblockUser(id: $id) {
id
name
blocked
followedByCurrentUser
}
}
`
}

View File

@ -70,6 +70,11 @@
"passwordStrength4": "Sehr sicheres Passwort"
}
},
"privacy": {
"name": "Privatsphäre",
"make-shouts-public": "Teile von mir empfohlene Artikel öffentlich auf meinem Profil",
"success-update": "Privatsphäre-Einstellungen gespeichert"
},
"invites": {
"name": "Einladungen"
},
@ -143,27 +148,44 @@
"successDelete": "Social-Media gelöscht. Profil aktualisiert!"
},
"muted-users": {
"name": "Stummgeschaltete Benutzer",
"explanation": {
"intro": "Wenn ein anderer Benutzer von dir stummgeschaltet wurde, dann passiert folgendes:",
"your-perspective": "In deiner Beitragsübersicht tauchen keine Beiträge der stummgeschalteten Person mehr auf.",
"search": "Die Beiträge von stummgeschalteten Personen verschwinden aus deinen Suchergebnissen."
},
"columns": {
"name": "Name",
"slug": "Alias",
"unmute": "Entsperren"
},
"empty": "Bislang hast du niemanden stummgeschaltet.",
"how-to": "Du kannst andere Benutzer auf deren Profilseite über das Inhaltsmenü stummschalten.",
"mute": "Stumm schalten",
"unmute": "Stummschaltung aufheben",
"unmuted": "{name} ist nicht mehr stummgeschaltet"
"name": "Stummgeschaltete Benutzer",
"explanation": {
"intro": "Wenn ein anderer Benutzer von dir stummgeschaltet wurde, dann passiert folgendes:",
"your-perspective": "In deiner Beitragsübersicht tauchen keine Beiträge der stummgeschalteten Person mehr auf.",
"search": "Die Beiträge von stummgeschalteten Personen verschwinden aus deinen Suchergebnissen."
},
"columns": {
"name": "Name",
"slug": "Alias",
"unmute": "Entsperren"
},
"empty": "Bislang hast du niemanden stummgeschaltet.",
"how-to": "Du kannst andere Benutzer auf deren Profilseite über das Inhaltsmenü stummschalten.",
"mute": "Stumm schalten",
"unmute": "Stummschaltung aufheben",
"unmuted": "{name} ist nicht mehr stummgeschaltet"
},
"privacy": {
"name": "Privatsphäre",
"make-shouts-public": "Teile von mir empfohlene Artikel öffentlich auf meinem Profil",
"success-update": "Privatsphäre-Einstellungen gespeichert"
"blocked-users": {
"name": "Blocked users",
"explanation": {
"intro": "Wenn ein anderer Benutzer von dir blockiert wurde, dann passiert folgendes:",
"your-perspective": "Du kannst keine Beiträge der blockierten Person mehr kommentieren.",
"their-perspective": "Die blockierte Person kann deine Beiträge nicht mehr kommentieren",
"notifications": "Von dir blockierte Personen erhalten keine Benachrichtigungen mehr, wenn sie in deinen Beiträgen erwähnt werden.",
"closing": "Das sollte fürs Erste genügen, damit blockierte Benutzer dich nicht mehr länger belästigen können.",
"commenting-disabled": "Du kannst den Beitrag derzeit nicht kommentieren.",
"commenting-explanation": "Dafür kann es mehrere Gründe geben, bitte schau in unsere "
},
"columns": {
"name": "Name",
"slug": "Alias",
"unblock": "Entsperren"
},
"empty": "Bislang hast du niemanden blockiert.",
"how-to": "Du kannst andere Benutzer auf deren Profilseite über das Inhaltsmenü blockieren.",
"block": "Nutzer blockieren",
"unblock": "Nutzer entsperren",
"unblocked": "{name} ist wieder entsperrt"
}
},
"admin": {

View File

@ -329,6 +329,28 @@
"mute": "Mute user",
"unmute": "Unmute user",
"unmuted": "{name} is unmuted again"
},
"blocked-users": {
"name": "Blocked users",
"explanation": {
"intro": "If another user has been blocked by you, this is what happens:",
"your-perspective": "You will no longer be able to interact with their contributions.",
"their-perspective": "Vice versa: The blocked person will also no longer be able to interact with your contributions.",
"notifications": "Blocked users will no longer receive notifications if they mention each other.",
"closing": "This should be sufficient for now so that blocked users can no longer bother you.",
"commenting-disabled": "Commenting is not possible at this time on this post.",
"commenting-explanation": "This can happen for several reasons, please see our "
},
"columns": {
"name": "Name",
"slug": "Slug",
"unblock": "Unblock"
},
"empty": "So far, you have not blocked anybody.",
"how-to": "You can block other users on their profile page via the content menu.",
"block": "Block user",
"unblock": "Unblock user",
"unblocked": "{name} is unblocked again"
}
},
"admin": {

View File

@ -59,15 +59,15 @@
"dependencies": {
"@human-connection/styleguide": "0.5.22",
"@nuxtjs/apollo": "^4.0.0-rc19",
"@nuxtjs/axios": "~5.9.3",
"@nuxtjs/axios": "~5.9.4",
"@nuxtjs/dotenv": "~1.4.1",
"@nuxtjs/pwa": "^3.0.0-beta.19",
"@nuxtjs/sentry": "^3.0.1",
"@nuxtjs/sentry": "^3.1.0",
"@nuxtjs/style-resources": "~1.0.0",
"accounting": "~0.4.1",
"apollo-cache-inmemory": "~1.6.5",
"apollo-client": "~2.6.8",
"cookie-universal-nuxt": "~2.1.0",
"cookie-universal-nuxt": "~2.1.1",
"cropperjs": "^1.5.5",
"cross-env": "~7.0.0",
"date-fns": "2.9.0",
@ -98,8 +98,8 @@
"devDependencies": {
"@babel/core": "~7.8.3",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "~7.8.3",
"@storybook/addon-a11y": "^5.3.8",
"@babel/preset-env": "~7.8.4",
"@storybook/addon-a11y": "^5.3.9",
"@storybook/addon-actions": "^5.3.9",
"@storybook/addon-notes": "^5.3.9",
"@storybook/vue": "~5.3.9",
@ -117,7 +117,7 @@
"core-js": "~2.6.10",
"css-loader": "~3.4.2",
"eslint": "~6.8.0",
"eslint-config-prettier": "~6.9.0",
"eslint-config-prettier": "~6.10.0",
"eslint-config-standard": "~14.1.0",
"eslint-loader": "~3.0.3",
"eslint-plugin-import": "~2.20.0",

View File

@ -92,11 +92,17 @@
/>
<ds-space margin-bottom="large" />
<comment-form
v-if="showNewCommentForm && !post.author.blocked"
ref="commentForm"
v-if="showNewCommentForm"
:post="post"
@createComment="createComment"
/>
<ds-placeholder v-else>
{{ $t('settings.blocked-users.explanation.commenting-disabled') }}
<br />
{{ $t('settings.blocked-users.explanation.commenting-explanation') }}
<a href="https://support.human-connection.org/kb/" target="_blank">FAQ</a>
</ds-placeholder>
</ds-section>
</ds-card>
</transition>
@ -145,6 +151,8 @@ export default {
title: 'loading',
showNewCommentForm: true,
blurred: false,
blocked: null,
postAuthor: null,
}
},
mounted() {
@ -222,6 +230,7 @@ export default {
this.post = Post[0] || {}
this.title = this.post.title
this.blurred = this.post.imageBlurred
this.postAuthor = this.post.author
},
fetchPolicy: 'cache-and-network',
},

View File

@ -24,6 +24,8 @@
class="user-content-menu"
@mute="muteUser"
@unmute="unmuteUser"
@block="blockUser"
@unblock="unblockUser"
/>
</client-only>
<ds-space margin="small">
@ -64,20 +66,21 @@
</client-only>
</ds-flex-item>
</ds-flex>
<ds-space margin="small">
<template v-if="!myProfile">
<hc-follow-button
v-if="!user.isMuted"
:follow-id="user.id"
:is-followed="user.followedByCurrentUser"
@optimistic="optimisticFollow"
@update="updateFollow"
/>
<base-button v-else @click="unmuteUser(user)" class="unblock-user-button">
{{ $t('settings.muted-users.unmute') }}
</base-button>
</template>
</ds-space>
<div v-if="!myProfile" class="action-buttons">
<base-button v-if="user.blocked" @click="unblockUser(user)">
{{ $t('settings.blocked-users.unblock') }}
</base-button>
<base-button v-if="user.isMuted" @click="unmuteUser(user)">
{{ $t('settings.muted-users.unmute') }}
</base-button>
<hc-follow-button
v-if="!(user.blocked || user.isMuted)"
:follow-id="user.id"
:is-followed="user.followedByCurrentUser"
@optimistic="optimisticFollow"
@update="updateFollow"
/>
</div>
<template v-if="user.about">
<hr />
<ds-space margin-top="small" margin-bottom="small">
@ -285,6 +288,7 @@ import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import { profilePagePosts } from '~/graphql/PostQuery'
import UserQuery from '~/graphql/User'
import { muteUser, unmuteUser } from '~/graphql/settings/MutedUsers'
import { blockUser, unblockUser } from '~/graphql/settings/BlockedUsers'
import PostMutations from '~/graphql/PostMutations'
import UpdateQuery from '~/components/utils/UpdateQuery'
@ -417,6 +421,24 @@ export default {
this.$apollo.queries.profilePagePosts.refetch()
}
},
async blockUser(user) {
try {
await this.$apollo.mutate({ mutation: blockUser(), variables: { id: user.id } })
} catch (error) {
this.$toast.error(error.message)
} finally {
this.$apollo.queries.User.refetch()
}
},
async unblockUser(user) {
try {
this.$apollo.mutate({ mutation: unblockUser(), variables: { id: user.id } })
} catch (error) {
this.$toast.error(error.message)
} finally {
this.$apollo.queries.User.refetch()
}
},
pinPost(post) {
this.$apollo
.mutate({
@ -559,8 +581,13 @@ export default {
.profile-post-add-button {
box-shadow: $box-shadow-x-large;
}
.unblock-user-button {
display: block;
width: 100%;
.action-buttons {
margin: $space-small 0;
> .base-button {
display: block;
width: 100%;
margin-bottom: $space-x-small;
}
}
</style>

View File

@ -43,6 +43,10 @@ export default {
name: this.$t('settings.muted-users.name'),
path: `/settings/muted-users`,
},
{
name: this.$t('settings.blocked-users.name'),
path: `/settings/blocked-users`,
},
{
name: this.$t('settings.embeds.name'),
path: `/settings/embeds`,

View File

@ -0,0 +1,69 @@
import { config, mount, createLocalVue } from '@vue/test-utils'
import BlockedUsers from './blocked-users.vue'
import Styleguide from '@human-connection/styleguide'
import Filters from '~/plugins/vue-filters'
import { unblockUser } from '~/graphql/settings/BlockedUsers'
const localVue = createLocalVue()
localVue.use(Styleguide)
localVue.use(Filters)
config.stubs['nuxt-link'] = '<span><slot /></span>'
describe('blocked-users.vue', () => {
let wrapper
let mocks
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest.fn(),
queries: {
blockedUsers: {
refetch: jest.fn(),
},
},
},
$toast: {
error: jest.fn(),
success: jest.fn(),
},
}
})
describe('mount', () => {
const Wrapper = () => {
return mount(BlockedUsers, { mocks, localVue })
}
beforeEach(() => {
wrapper = Wrapper()
})
it('renders', () => {
expect(wrapper.is('div')).toBe(true)
})
describe('given a list of blocked users', () => {
beforeEach(() => {
const blockedUsers = [{ id: 'u1', name: 'John Doe', slug: 'john-doe', avatar: '' }]
wrapper.setData({ blockedUsers })
})
describe('click unblock', () => {
beforeEach(() => {
wrapper.find('.base-button').trigger('click')
})
it('calls unblock mutation with given user', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
mutation: unblockUser(),
variables: { id: 'u1' },
})
})
})
})
})
})

View File

@ -0,0 +1,118 @@
<template>
<div>
<ds-space>
<ds-card :header="$t('settings.blocked-users.name')">
<ds-text>
{{ $t('settings.blocked-users.explanation.intro') }}
</ds-text>
<ds-list>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.your-perspective') }}
</ds-list-item>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.their-perspective') }}
</ds-list-item>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.notifications') }}
</ds-list-item>
</ds-list>
</ds-card>
</ds-space>
<ds-card v-if="blockedUsers && blockedUsers.length">
<ds-table :data="blockedUsers" :fields="fields" condensed>
<template #avatar="scope">
<nuxt-link
:to="{
name: 'profile-id-slug',
params: { id: scope.row.id, slug: scope.row.slug },
}"
>
<user-avatar :user="scope.row" size="small" />
</nuxt-link>
</template>
<template #name="scope">
<nuxt-link
:to="{
name: 'profile-id-slug',
params: { id: scope.row.id, slug: scope.row.slug },
}"
>
<b>{{ scope.row.name | truncate(20) }}</b>
</nuxt-link>
</template>
<template #slug="scope">
<nuxt-link
:to="{
name: 'profile-id-slug',
params: { id: scope.row.id, slug: scope.row.slug },
}"
>
<b>{{ scope.row.slug | truncate(20) }}</b>
</nuxt-link>
</template>
<template #unblockUser="scope">
<base-button circle size="small" @click="unblockUser(scope)" icon="user-plus" />
</template>
</ds-table>
</ds-card>
<ds-card v-else>
<ds-space>
<ds-placeholder>
{{ $t('settings.blocked-users.empty') }}
</ds-placeholder>
</ds-space>
<ds-space>
<ds-text align="center">
{{ $t('settings.blocked-users.how-to') }}
</ds-text>
</ds-space>
</ds-card>
</div>
</template>
<script>
import { blockedUsers, unblockUser } from '~/graphql/settings/BlockedUsers'
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
export default {
components: {
UserAvatar,
},
data() {
return {
blockedUsers: [],
}
},
computed: {
fields() {
return {
avatar: '',
name: this.$t('settings.blocked-users.columns.name'),
slug: this.$t('settings.blocked-users.columns.slug'),
unblockUser: this.$t('settings.blocked-users.columns.unblock'),
}
},
},
apollo: {
blockedUsers: { query: blockedUsers, fetchPolicy: 'cache-and-network' },
},
methods: {
async unblockUser(user) {
await this.$apollo.mutate({
mutation: unblockUser(),
variables: { id: user.row.id },
})
this.$apollo.queries.blockedUsers.refetch()
const { name } = user.row
this.$toast.success(this.$t('settings.blocked-user.unblocked', { name }))
},
},
}
</script>
<style lang="scss">
.ds-table-col {
vertical-align: middle;
}
</style>

View File

@ -46,7 +46,7 @@ describe('muted-users.vue', () => {
expect(wrapper.is('div')).toBe(true)
})
describe('given a list of blocked users', () => {
describe('given a list of muted users', () => {
beforeEach(() => {
const mutedUsers = [{ id: 'u1', name: 'John Doe', slug: 'john-doe', avatar: '' }]
wrapper.setData({ mutedUsers })
@ -54,7 +54,7 @@ describe('muted-users.vue', () => {
describe('click unmute', () => {
beforeEach(() => {
wrapper.find('button').trigger('click')
wrapper.find('.base-button').trigger('click')
})
it('calls unmute mutation with given user', () => {

View File

@ -17,7 +17,7 @@
</ds-space>
<ds-card v-if="mutedUsers && mutedUsers.length">
<ds-table :data="mutedUsers" :fields="fields" condensed>
<template slot="avatar" slot-scope="scope">
<template #avatar="scope">
<nuxt-link
:to="{
name: 'profile-id-slug',
@ -27,7 +27,7 @@
<user-avatar :user="scope.row" size="small" />
</nuxt-link>
</template>
<template slot="name" slot-scope="scope">
<template #name="scope">
<nuxt-link
:to="{
name: 'profile-id-slug',
@ -37,7 +37,7 @@
<b>{{ scope.row.name | truncate(20) }}</b>
</nuxt-link>
</template>
<template slot="slug" slot-scope="scope">
<template #slug="scope">
<nuxt-link
:to="{
name: 'profile-id-slug',
@ -48,7 +48,7 @@
</nuxt-link>
</template>
<template slot="unmuteUser" slot-scope="scope">
<template #unmuteUser="scope">
<base-button circle size="small" @click="unmuteUser(scope)" icon="user-plus" />
</template>
</ds-table>

File diff suppressed because it is too large Load Diff

170
yarn.lock
View File

@ -9,12 +9,12 @@
dependencies:
"@babel/highlight" "^7.8.3"
"@babel/compat-data@^7.8.0", "@babel/compat-data@^7.8.1":
version "7.8.1"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.8.1.tgz#fc0bbbb7991e4fb2b47e168e60f2cc2c41680be9"
integrity sha512-Z+6ZOXvyOWYxJ50BwxzdhRnRsGST8Y3jaZgxYig575lTjVSs3KtJnmESwZegg6e2Dn0td1eDhoWlp1wI4BTCPw==
"@babel/compat-data@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.8.4.tgz#bbe65d05a291667a8394fe8a0e0e277ef22b0d2a"
integrity sha512-lHLhlsvFjJAqNU71b7k6Vv9ewjmTXKvqaMv7n0G1etdCabWLw3nEYE8mmgoVOxMIFE07xOvo7H7XBASirX6Rrg==
dependencies:
browserslist "^4.8.2"
browserslist "^4.8.5"
invariant "^2.2.4"
semver "^5.5.0"
@ -38,17 +38,17 @@
semver "^5.4.1"
source-map "^0.5.0"
"@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941"
integrity sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==
"@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e"
integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.8.3"
"@babel/helpers" "^7.8.3"
"@babel/parser" "^7.8.3"
"@babel/generator" "^7.8.4"
"@babel/helpers" "^7.8.4"
"@babel/parser" "^7.8.4"
"@babel/template" "^7.8.3"
"@babel/traverse" "^7.8.3"
"@babel/traverse" "^7.8.4"
"@babel/types" "^7.8.3"
convert-source-map "^1.7.0"
debug "^4.1.0"
@ -59,10 +59,10 @@
semver "^5.4.1"
source-map "^0.5.0"
"@babel/generator@^7.4.4", "@babel/generator@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03"
integrity sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==
"@babel/generator@^7.4.4", "@babel/generator@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e"
integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==
dependencies:
"@babel/types" "^7.8.3"
jsesc "^2.5.1"
@ -101,15 +101,15 @@
"@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/helper-compilation-targets@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.3.tgz#2deedc816fd41dca7355ef39fd40c9ea69f0719a"
integrity sha512-JLylPCsFjhLN+6uBSSh3iYdxKdeO9MNmoY96PE/99d8kyBFaXLORtAVhqN6iHa+wtPeqxKLghDOZry0+Aiw9Tw==
"@babel/helper-compilation-targets@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.4.tgz#03d7ecd454b7ebe19a254f76617e61770aed2c88"
integrity sha512-3k3BsKMvPp5bjxgMdrFyq0UaEO48HciVrOVF0+lon8pp95cyJ2ujAh0TrBHNMnJGT2rr0iKOJPFFbSqjDyf/Pg==
dependencies:
"@babel/compat-data" "^7.8.1"
browserslist "^4.8.2"
"@babel/compat-data" "^7.8.4"
browserslist "^4.8.5"
invariant "^2.2.4"
levenary "^1.1.0"
levenary "^1.1.1"
semver "^5.5.0"
"@babel/helper-create-class-features-plugin@^7.3.0":
@ -310,13 +310,13 @@
"@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/helpers@^7.4.4", "@babel/helpers@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.3.tgz#382fbb0382ce7c4ce905945ab9641d688336ce85"
integrity sha512-LmU3q9Pah/XyZU89QvBgGt+BCsTPoQa+73RxAQh8fb8qkDyIfeQnmgs+hvzhTCKTzqOyk7JTkS3MS1S8Mq5yrQ==
"@babel/helpers@^7.4.4", "@babel/helpers@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73"
integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==
dependencies:
"@babel/template" "^7.8.3"
"@babel/traverse" "^7.8.3"
"@babel/traverse" "^7.8.4"
"@babel/types" "^7.8.3"
"@babel/highlight@^7.8.3":
@ -328,10 +328,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.4.5", "@babel/parser@^7.7.5", "@babel/parser@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081"
integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==
"@babel/parser@^7.4.5", "@babel/parser@^7.7.5", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8"
integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==
"@babel/plugin-proposal-async-generator-functions@^7.2.0", "@babel/plugin-proposal-async-generator-functions@^7.8.3":
version "7.8.3"
@ -559,10 +559,10 @@
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-transform-for-of@^7.4.4", "@babel/plugin-transform-for-of@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.3.tgz#15f17bce2fc95c7d59a24b299e83e81cedc22e18"
integrity sha512-ZjXznLNTxhpf4Q5q3x1NsngzGA38t9naWH8Gt+0qYZEJAcvPI9waSStSh56u19Ofjr7QmD0wUsQ8hw8s/p1VnA==
"@babel/plugin-transform-for-of@^7.4.4", "@babel/plugin-transform-for-of@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz#6fe8eae5d6875086ee185dd0b098a8513783b47d"
integrity sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
@ -647,10 +647,10 @@
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.3"
"@babel/plugin-transform-parameters@^7.4.4", "@babel/plugin-transform-parameters@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.3.tgz#7890576a13b17325d8b7d44cb37f21dc3bbdda59"
integrity sha512-/pqngtGb54JwMBZ6S/D3XYylQDFtGjWrnoCF4gXZOUpFV/ujbxnoNGNvDGu6doFWRPBveE72qTx/RRU44j5I/Q==
"@babel/plugin-transform-parameters@^7.4.4", "@babel/plugin-transform-parameters@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz#1d5155de0b65db0ccf9971165745d3bb990d77d3"
integrity sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA==
dependencies:
"@babel/helper-call-delegate" "^7.8.3"
"@babel/helper-get-function-arity" "^7.8.3"
@ -749,10 +749,10 @@
"@babel/helper-annotate-as-pure" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-transform-typeof-symbol@^7.2.0", "@babel/plugin-transform-typeof-symbol@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.3.tgz#5cffb216fb25c8c64ba6bf5f76ce49d3ab079f4d"
integrity sha512-3TrkKd4LPqm4jHs6nPtSDI/SV9Cm5PRJkHLUgTcqRQQTMAZ44ZaAdDZJtvWFSaRcvT0a1rTmJ5ZA5tDKjleF3g==
"@babel/plugin-transform-typeof-symbol@^7.2.0", "@babel/plugin-transform-typeof-symbol@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412"
integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
@ -818,13 +818,13 @@
js-levenshtein "^1.1.3"
semver "^5.5.0"
"@babel/preset-env@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.8.3.tgz#dc0fb2938f52bbddd79b3c861a4b3427dd3a6c54"
integrity sha512-Rs4RPL2KjSLSE2mWAx5/iCH+GC1ikKdxPrhnRS6PfFVaiZeom22VFKN4X8ZthyN61kAaR05tfXTbCvatl9WIQg==
"@babel/preset-env@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.8.4.tgz#9dac6df5f423015d3d49b6e9e5fa3413e4a72c4e"
integrity sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w==
dependencies:
"@babel/compat-data" "^7.8.0"
"@babel/helper-compilation-targets" "^7.8.3"
"@babel/compat-data" "^7.8.4"
"@babel/helper-compilation-targets" "^7.8.4"
"@babel/helper-module-imports" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-proposal-async-generator-functions" "^7.8.3"
@ -853,7 +853,7 @@
"@babel/plugin-transform-dotall-regex" "^7.8.3"
"@babel/plugin-transform-duplicate-keys" "^7.8.3"
"@babel/plugin-transform-exponentiation-operator" "^7.8.3"
"@babel/plugin-transform-for-of" "^7.8.3"
"@babel/plugin-transform-for-of" "^7.8.4"
"@babel/plugin-transform-function-name" "^7.8.3"
"@babel/plugin-transform-literals" "^7.8.3"
"@babel/plugin-transform-member-expression-literals" "^7.8.3"
@ -864,7 +864,7 @@
"@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3"
"@babel/plugin-transform-new-target" "^7.8.3"
"@babel/plugin-transform-object-super" "^7.8.3"
"@babel/plugin-transform-parameters" "^7.8.3"
"@babel/plugin-transform-parameters" "^7.8.4"
"@babel/plugin-transform-property-literals" "^7.8.3"
"@babel/plugin-transform-regenerator" "^7.8.3"
"@babel/plugin-transform-reserved-words" "^7.8.3"
@ -872,13 +872,13 @@
"@babel/plugin-transform-spread" "^7.8.3"
"@babel/plugin-transform-sticky-regex" "^7.8.3"
"@babel/plugin-transform-template-literals" "^7.8.3"
"@babel/plugin-transform-typeof-symbol" "^7.8.3"
"@babel/plugin-transform-typeof-symbol" "^7.8.4"
"@babel/plugin-transform-unicode-regex" "^7.8.3"
"@babel/types" "^7.8.3"
browserslist "^4.8.2"
browserslist "^4.8.5"
core-js-compat "^3.6.2"
invariant "^2.2.2"
levenary "^1.1.0"
levenary "^1.1.1"
semver "^5.5.0"
"@babel/preset-react@7.0.0":
@ -934,16 +934,16 @@
"@babel/parser" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/traverse@^7.4.5", "@babel/traverse@^7.5.5", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.3.tgz#a826215b011c9b4f73f3a893afbc05151358bf9a"
integrity sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==
"@babel/traverse@^7.4.5", "@babel/traverse@^7.5.5", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c"
integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.8.3"
"@babel/generator" "^7.8.4"
"@babel/helper-function-name" "^7.8.3"
"@babel/helper-split-export-declaration" "^7.8.3"
"@babel/parser" "^7.8.3"
"@babel/parser" "^7.8.4"
"@babel/types" "^7.8.3"
debug "^4.1.0"
globals "^11.1.0"
@ -1737,14 +1737,14 @@ browserify@16.2.3, browserify@^16.1.0:
vm-browserify "^1.0.0"
xtend "^4.0.0"
browserslist@^4.6.0, browserslist@^4.8.2, browserslist@^4.8.3:
version "4.8.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.3.tgz#65802fcd77177c878e015f0e3189f2c4f627ba44"
integrity sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg==
browserslist@^4.6.0, browserslist@^4.8.3, browserslist@^4.8.5:
version "4.8.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.6.tgz#96406f3f5f0755d272e27a66f4163ca821590a7e"
integrity sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==
dependencies:
caniuse-lite "^1.0.30001017"
electron-to-chromium "^1.3.322"
node-releases "^1.1.44"
caniuse-lite "^1.0.30001023"
electron-to-chromium "^1.3.341"
node-releases "^1.1.47"
bser@2.1.1:
version "2.1.1"
@ -1845,10 +1845,10 @@ camelcase@^5.0.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-lite@^1.0.30001017:
version "1.0.30001020"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001020.tgz#3f04c1737500ffda78be9beb0b5c1e2070e15926"
integrity sha512-yWIvwA68wRHKanAVS1GjN8vajAv7MBFshullKCeq/eKpK7pJBVDgFFEqvgWTkcP2+wIDeQGYFRXECjKZnLkUjA==
caniuse-lite@^1.0.30001023:
version "1.0.30001023"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz#b82155827f3f5009077bdd2df3d8968bcbcc6fc4"
integrity sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA==
capture-exit@^2.0.0:
version "2.0.0"
@ -2923,10 +2923,10 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
electron-to-chromium@^1.3.322:
version "1.3.322"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8"
integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==
electron-to-chromium@^1.3.341:
version "1.3.344"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz#f1397a633c35e726730c24be1084cd25c3ee8148"
integrity sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw==
elegant-spinner@^1.0.1:
version "1.0.1"
@ -4397,10 +4397,10 @@ leven@^3.1.0:
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
levenary@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.0.tgz#fc146fe75f32dc483a0a2c64aef720f602cd6210"
integrity sha512-VHcwhO0UTpUW7rLPN2/OiWJdgA1e9BqEDALhrgCe/F+uUJnep6CoUsTzMeP8Rh0NGr9uKquXxqe7lwLZo509nQ==
levenary@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77"
integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==
dependencies:
leven "^3.1.0"
@ -4978,10 +4978,10 @@ node-pre-gyp@^0.10.0:
semver "^5.3.0"
tar "^4"
node-releases@^1.1.44:
version "1.1.45"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.45.tgz#4cf7e9175d71b1317f15ffd68ce63bce1d53e9f2"
integrity sha512-cXvGSfhITKI8qsV116u2FTzH5EWZJfgG7d4cpqwF8I8+1tWpD6AsvvGRKq2onR0DNj1jfqsjkXZsm14JMS7Cyg==
node-releases@^1.1.47:
version "1.1.47"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.47.tgz#c59ef739a1fd7ecbd9f0b7cf5b7871e8a8b591e4"
integrity sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA==
dependencies:
semver "^6.3.0"