mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge branch 'master' of https://github.com/Human-Connection/Human-Connection into 1062-notification-about-comment-on-post
This commit is contained in:
commit
d98ebdd713
@ -27,6 +27,7 @@
|
||||
* [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md)
|
||||
* [HTTPS](deployment/digital-ocean/https/README.md)
|
||||
* [Human Connection](deployment/human-connection/README.md)
|
||||
* [Error Reporting](deployment/human-connection/error-reporting/README.md)
|
||||
* [Mailserver](deployment/human-connection/mailserver/README.md)
|
||||
* [Maintenance](deployment/human-connection/maintenance/README.md)
|
||||
* [Volumes](deployment/volumes/README.md)
|
||||
|
||||
@ -14,3 +14,6 @@ JWT_SECRET="b/&&7b78BF&fv/Vd"
|
||||
MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ"
|
||||
|
||||
PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
|
||||
|
||||
SENTRY_DSN_BACKEND=
|
||||
COMMIT=
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/joi": "^15.1.1",
|
||||
"@sentry/node": "^5.6.1",
|
||||
"activitystrea.ms": "~2.1.3",
|
||||
"apollo-cache-inmemory": "~1.6.3",
|
||||
"apollo-client": "~2.6.4",
|
||||
@ -55,15 +56,16 @@
|
||||
"cheerio": "~1.0.0-rc.3",
|
||||
"cors": "~2.8.5",
|
||||
"cross-env": "~5.2.0",
|
||||
"date-fns": "2.0.0-beta.4",
|
||||
"date-fns": "2.0.0-beta.5",
|
||||
"debug": "~4.1.1",
|
||||
"dotenv": "~8.0.0",
|
||||
"dotenv": "~8.1.0",
|
||||
"express": "^4.17.1",
|
||||
"faker": "Marak/faker.js#master",
|
||||
"graphql": "~14.4.2",
|
||||
"graphql-custom-directives": "~0.2.14",
|
||||
"graphql-iso-date": "~3.6.1",
|
||||
"graphql-middleware": "~3.0.5",
|
||||
"graphql-middleware-sentry": "^3.2.0",
|
||||
"graphql-shield": "~6.0.4",
|
||||
"graphql-tag": "~2.10.1",
|
||||
"helmet": "~3.20.0",
|
||||
@ -89,8 +91,8 @@
|
||||
"metascraper-youtube": "^5.6.3",
|
||||
"minimatch": "^3.0.4",
|
||||
"neo4j-driver": "~1.7.5",
|
||||
"neo4j-graphql-js": "^2.7.0",
|
||||
"neode": "^0.3.1",
|
||||
"neo4j-graphql-js": "^2.7.1",
|
||||
"neode": "^0.3.2",
|
||||
"node-fetch": "~2.6.0",
|
||||
"nodemailer": "^6.3.0",
|
||||
"npm-run-all": "~4.1.5",
|
||||
@ -98,7 +100,7 @@
|
||||
"sanitize-html": "~1.20.1",
|
||||
"slug": "~1.1.0",
|
||||
"trunc-html": "~1.1.2",
|
||||
"uuid": "~3.3.2",
|
||||
"uuid": "~3.3.3",
|
||||
"wait-on": "~3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -111,20 +113,20 @@
|
||||
"apollo-server-testing": "~2.8.1",
|
||||
"babel-core": "~7.0.0-0",
|
||||
"babel-eslint": "~10.0.2",
|
||||
"babel-jest": "~24.8.0",
|
||||
"babel-jest": "~24.9.0",
|
||||
"chai": "~4.2.0",
|
||||
"cucumber": "~5.1.0",
|
||||
"eslint": "~6.1.0",
|
||||
"eslint-config-prettier": "~6.0.0",
|
||||
"eslint-config-standard": "~13.0.1",
|
||||
"eslint": "~6.2.0",
|
||||
"eslint-config-prettier": "~6.1.0",
|
||||
"eslint-config-standard": "~14.0.0",
|
||||
"eslint-plugin-import": "~2.18.2",
|
||||
"eslint-plugin-jest": "~22.15.1",
|
||||
"eslint-plugin-node": "~9.1.0",
|
||||
"eslint-plugin-prettier": "~3.1.0",
|
||||
"eslint-plugin-promise": "~4.2.1",
|
||||
"eslint-plugin-standard": "~4.0.0",
|
||||
"eslint-plugin-standard": "~4.0.1",
|
||||
"graphql-request": "~1.8.2",
|
||||
"jest": "~24.8.0",
|
||||
"jest": "~24.9.0",
|
||||
"nodemon": "~1.19.1",
|
||||
"prettier": "~1.18.2",
|
||||
"supertest": "~4.0.2"
|
||||
|
||||
@ -102,27 +102,27 @@ export default class ActivityPub {
|
||||
handleUndoActivity(activity) {
|
||||
debug('inside UNDO')
|
||||
switch (activity.object.type) {
|
||||
case 'Follow':
|
||||
case 'Follow': {
|
||||
const followActivity = activity.object
|
||||
return this.dataSource.undoFollowActivity(followActivity.actor, followActivity.object)
|
||||
case 'Like':
|
||||
}
|
||||
case 'Like': {
|
||||
return this.dataSource.deleteShouted(activity)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCreateActivity(activity) {
|
||||
debug('inside create')
|
||||
switch (activity.object.type) {
|
||||
case 'Article':
|
||||
case 'Note':
|
||||
case 'Note': {
|
||||
const articleObject = activity.object
|
||||
if (articleObject.inReplyTo) {
|
||||
return this.dataSource.createComment(activity)
|
||||
} else {
|
||||
return this.dataSource.createPost(activity)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,13 +159,14 @@ export default class ActivityPub {
|
||||
async handleAcceptActivity(activity) {
|
||||
debug('inside accept')
|
||||
switch (activity.object.type) {
|
||||
case 'Follow':
|
||||
case 'Follow': {
|
||||
const followObject = activity.object
|
||||
const followingCollectionPage = await this.collections.getFollowingCollectionPage(
|
||||
followObject.actor,
|
||||
)
|
||||
followingCollectionPage.orderedItems.push(followObject.object)
|
||||
await this.dataSource.saveFollowingCollectionPage(followingCollectionPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ export function createSignature(options) {
|
||||
// verifying
|
||||
export function verifySignature(url, headers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const signatureHeader = headers['signature'] ? headers['signature'] : headers['Signature']
|
||||
const signatureHeader = headers.signature ? headers.signature : headers.Signature
|
||||
if (!signatureHeader) {
|
||||
debug('No Signature header present!')
|
||||
resolve(false)
|
||||
|
||||
@ -11,6 +11,8 @@ const {
|
||||
SMTP_PORT,
|
||||
SMTP_USERNAME,
|
||||
SMTP_PASSWORD,
|
||||
SENTRY_DSN_BACKEND,
|
||||
COMMIT,
|
||||
NEO4J_URI = 'bolt://localhost:7687',
|
||||
NEO4J_USERNAME = 'neo4j',
|
||||
NEO4J_PASSWORD = 'neo4j',
|
||||
@ -31,15 +33,18 @@ export const neo4jConfigs = { NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD }
|
||||
export const serverConfigs = { GRAPHQL_PORT, CLIENT_URI, GRAPHQL_URI }
|
||||
|
||||
export const developmentConfigs = {
|
||||
DEBUG: process.env.NODE_ENV !== 'production' && process.env.DEBUG === 'true',
|
||||
DEBUG: process.env.NODE_ENV !== 'production' && process.env.DEBUG,
|
||||
DISABLED_MIDDLEWARES:
|
||||
(process.env.NODE_ENV !== 'production' && process.env.DISABLED_MIDDLEWARES) || '',
|
||||
}
|
||||
|
||||
export const sentryConfigs = { SENTRY_DSN_BACKEND, COMMIT }
|
||||
|
||||
export default {
|
||||
...requiredConfigs,
|
||||
...smtpConfigs,
|
||||
...neo4jConfigs,
|
||||
...serverConfigs,
|
||||
...developmentConfigs,
|
||||
...sentryConfigs,
|
||||
}
|
||||
|
||||
@ -14,10 +14,12 @@ import orderBy from './orderByMiddleware'
|
||||
import validation from './validation/validationMiddleware'
|
||||
import handleNotifications from './handleNotifications/handleNotificationsMiddleware'
|
||||
import email from './email/emailMiddleware'
|
||||
import sentry from './sentryMiddleware'
|
||||
|
||||
export default schema => {
|
||||
const middlewares = {
|
||||
permissions: permissions,
|
||||
sentry: sentry,
|
||||
activityPub: activityPub,
|
||||
dateTime: dateTime,
|
||||
validation: validation,
|
||||
@ -35,6 +37,7 @@ export default schema => {
|
||||
}
|
||||
|
||||
let order = [
|
||||
'sentry',
|
||||
'permissions',
|
||||
// 'activityPub', disabled temporarily
|
||||
'dateTime',
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { rule, shield, deny, allow, and, or, not } from 'graphql-shield'
|
||||
import { neode } from '../bootstrap/neo4j'
|
||||
import CONFIG from '../config'
|
||||
|
||||
const debug = !!CONFIG.DEBUG
|
||||
const allowExternalErrors = true
|
||||
|
||||
const instance = neode()
|
||||
|
||||
/*
|
||||
* TODO: implement
|
||||
* See: https://github.com/Human-Connection/Nitro-Backend/pull/40#pullrequestreview-180898363
|
||||
*/
|
||||
const isAuthenticated = rule({
|
||||
cache: 'contextual',
|
||||
})(async (_parent, _args, ctx, _info) => {
|
||||
@ -204,6 +204,8 @@ const permissions = shield(
|
||||
},
|
||||
},
|
||||
{
|
||||
debug,
|
||||
allowExternalErrors,
|
||||
fallbackRule: allow,
|
||||
},
|
||||
)
|
||||
|
||||
26
backend/src/middleware/sentryMiddleware.js
Normal file
26
backend/src/middleware/sentryMiddleware.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { sentry } from 'graphql-middleware-sentry'
|
||||
import { sentryConfigs } from '../config'
|
||||
|
||||
let sentryMiddleware = (resolve, root, args, context, resolveInfo) =>
|
||||
resolve(root, args, context, resolveInfo)
|
||||
|
||||
if (sentryConfigs.SENTRY_DSN_BACKEND) {
|
||||
sentryMiddleware = sentry({
|
||||
forwardErrors: true,
|
||||
config: {
|
||||
dsn: sentryConfigs.SENTRY_DSN_BACKEND,
|
||||
release: sentryConfigs.COMMIT,
|
||||
environment: process.env.NODE_ENV,
|
||||
},
|
||||
withScope: (scope, error, context) => {
|
||||
scope.setUser({
|
||||
id: context.user.id,
|
||||
})
|
||||
scope.setExtra('body', context.req.body)
|
||||
scope.setExtra('origin', context.req.headers.origin)
|
||||
scope.setExtra('user-agent', context.req.headers['user-agent'])
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default sentryMiddleware
|
||||
@ -35,7 +35,7 @@ export default applyScalars(
|
||||
],
|
||||
// add 'User' here as soon as possible
|
||||
},
|
||||
debug: CONFIG.DEBUG,
|
||||
debug: !!CONFIG.DEBUG,
|
||||
},
|
||||
}),
|
||||
),
|
||||
|
||||
@ -151,7 +151,6 @@ export default {
|
||||
friendsCount: '<-[:FRIENDS]->(related:User)',
|
||||
followingCount: '-[:FOLLOWS]->(related:User)',
|
||||
followedByCount: '<-[:FOLLOWS]-(related:User)',
|
||||
commentsCount: '-[:WROTE]->(r:Comment)',
|
||||
commentedCount: '-[:WROTE]->(:Comment)-[:COMMENTS]->(related:Post)',
|
||||
shoutedCount: '-[:SHOUTED]->(related:Post)',
|
||||
badgesCount: '<-[:REWARDED]-(related:Badge)',
|
||||
|
||||
@ -29,11 +29,6 @@ type Post {
|
||||
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
|
||||
|
||||
comments: [Comment]! @relation(name: "COMMENTS", direction: "IN")
|
||||
commentsCount: Int!
|
||||
@cypher(
|
||||
statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)"
|
||||
)
|
||||
|
||||
shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN")
|
||||
shoutedCount: Int!
|
||||
@cypher(
|
||||
|
||||
@ -64,7 +64,6 @@ type User {
|
||||
)
|
||||
|
||||
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
|
||||
commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
|
||||
commentedCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment)-[:COMMENTS]->(p:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true AND NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))")
|
||||
|
||||
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
|
||||
@ -143,7 +142,6 @@ type Query {
|
||||
followedByCount: Int
|
||||
followedByCurrentUser: Boolean
|
||||
contributionsCount: Int
|
||||
commentsCount: Int
|
||||
commentedCount: Int
|
||||
shoutedCount: Int
|
||||
badgesCount: Int
|
||||
|
||||
@ -33,8 +33,8 @@ const createServer = options => {
|
||||
}
|
||||
},
|
||||
schema: middleware(schema),
|
||||
debug: CONFIG.DEBUG,
|
||||
tracing: CONFIG.DEBUG,
|
||||
debug: !!CONFIG.DEBUG,
|
||||
tracing: !!CONFIG.DEBUG,
|
||||
}
|
||||
const server = new ApolloServer(Object.assign({}, defaults, options))
|
||||
|
||||
|
||||
1096
backend/yarn.lock
1096
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -260,7 +260,7 @@ Then("the first post on the landing page has the title:", title => {
|
||||
Then(
|
||||
"the page {string} returns a 404 error with a message:",
|
||||
(route, message) => {
|
||||
// TODO: how can we check HTTP codes with cypress?
|
||||
cy.request({ url: route, failOnStatusCode: false }).its('status').should('eq', 404)
|
||||
cy.visit(route, { failOnStatusCode: false });
|
||||
cy.get(".error").should("contain", message);
|
||||
}
|
||||
|
||||
@ -32,6 +32,8 @@
|
||||
value: 1G
|
||||
- name: NEO4J_dbms_memory_heap_max__size
|
||||
value: 1G
|
||||
- name: NEO4J_dbms_security_procedures_unrestricted
|
||||
value: "algo.*,apoc.*"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: configmap
|
||||
|
||||
16
deployment/human-connection/error-reporting/README.md
Normal file
16
deployment/human-connection/error-reporting/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Error reporting
|
||||
|
||||
We use [Sentry](https://github.com/getsentry/sentry) for error reporting in both
|
||||
our backend and web frontend. You can either use a hosted or a self-hosted
|
||||
instance. Just set the two `DSN` in your
|
||||
[configmap](../templates/configmap.template.yaml) and update the `COMMIT`
|
||||
during a deployment with your commit or the version of your release.
|
||||
|
||||
## Self-hosted Sentry
|
||||
|
||||
For data privacy it is recommended to set up your own instance of sentry.
|
||||
If you are lucky enough to have a kubernetes cluster with the required hardware
|
||||
support, try this [helm chart](https://github.com/helm/charts/tree/master/stable/sentry).
|
||||
|
||||
On our kubernetes cluster we get "mult-attach" errors for persistent volumes.
|
||||
Apparently Digital Ocean's kubernetes clusters do not fulfill the requirements.
|
||||
@ -9,6 +9,9 @@
|
||||
NEO4J_URI: "bolt://nitro-neo4j.human-connection:7687"
|
||||
NEO4J_AUTH: "none"
|
||||
CLIENT_URI: "https://nitro-staging.human-connection.org"
|
||||
SENTRY_DSN_WEBAPP: ""
|
||||
SENTRY_DSN_BACKEND: ""
|
||||
COMMIT: ""
|
||||
metadata:
|
||||
name: configmap
|
||||
namespace: human-connection
|
||||
|
||||
@ -26,11 +26,11 @@
|
||||
"cypress-cucumber-preprocessor": "^1.13.1",
|
||||
"cypress-file-upload": "^3.3.3",
|
||||
"cypress-plugin-retries": "^1.2.2",
|
||||
"dotenv": "^8.0.0",
|
||||
"dotenv": "^8.1.0",
|
||||
"faker": "Marak/faker.js#master",
|
||||
"graphql-request": "^1.8.2",
|
||||
"neo4j-driver": "^1.7.5",
|
||||
"neode": "^0.3.1",
|
||||
"neode": "^0.3.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"slug": "^1.1.0"
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
sed -i "s/<COMMIT>/${TRAVIS_COMMIT}/g" $TRAVIS_BUILD_DIR/scripts/patch-deployment.yaml
|
||||
kubectl --namespace=human-connection patch deployment nitro-backend -p "$(cat $TRAVIS_BUILD_DIR/scripts/patch-deployment.yaml)"
|
||||
kubectl --namespace=human-connection patch deployment nitro-web -p "$(cat $TRAVIS_BUILD_DIR/scripts/patch-deployment.yaml)"
|
||||
sed -i "s/<COMMIT>/${TRAVIS_COMMIT}/g" $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml
|
||||
sed -i "s/<COMMIT>/${TRAVIS_COMMIT}/g" $TRAVIS_BUILD_DIR/scripts/patches/patch-configmap.yaml
|
||||
kubectl --namespace=human-connection patch configmap configmap -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-configmap.yaml)"
|
||||
kubectl --namespace=human-connection patch deployment nitro-backend -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml)"
|
||||
kubectl --namespace=human-connection patch deployment nitro-web -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml)"
|
||||
|
||||
7
scripts/patches/patch-configmap.yaml
Normal file
7
scripts/patches/patch-configmap.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
COMMIT: <COMMIT>
|
||||
metadata:
|
||||
name: configmap
|
||||
namespace: human-connection
|
||||
@ -1 +1,3 @@
|
||||
MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ"
|
||||
SENTRY_DSN_WEBAPP=
|
||||
COMMIT=
|
||||
|
||||
@ -67,7 +67,7 @@ import { mapGetters, mapMutations } from 'vuex'
|
||||
import HcUser from '~/components/User'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
import ContentViewer from '~/components/Editor/ContentViewer'
|
||||
import HcEditCommentForm from '~/components/comments/EditCommentForm/EditCommentForm'
|
||||
import HcEditCommentForm from '~/components/EditCommentForm/EditCommentForm'
|
||||
|
||||
export default {
|
||||
data: function() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { mount, createLocalVue } from '@vue/test-utils'
|
||||
import CommentForm from './CommentForm.vue'
|
||||
import CommentForm from './CommentForm'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
import Vuex from 'vuex'
|
||||
import MutationObserver from 'mutation-observer'
|
||||
@ -30,10 +30,10 @@
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import HcEditor from '~/components/Editor/Editor'
|
||||
import PostCommentsQuery from '~/graphql/PostCommentsQuery.js'
|
||||
import CommentMutations from '~/graphql/CommentMutations.js'
|
||||
import { mapGetters } from 'vuex'
|
||||
import HcEditor from '~/components/Editor/Editor'
|
||||
import PostQuery from '~/graphql/PostQuery'
|
||||
import CommentMutations from '~/graphql/CommentMutations'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -41,7 +41,6 @@ export default {
|
||||
},
|
||||
props: {
|
||||
post: { type: Object, default: () => {} },
|
||||
comments: { type: Array, default: () => [] },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -76,18 +75,18 @@ export default {
|
||||
this.disabled = true
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: CommentMutations().CreateComment,
|
||||
mutation: CommentMutations(this.$i18n).CreateComment,
|
||||
variables: {
|
||||
postId: this.post.id,
|
||||
content: this.form.content,
|
||||
},
|
||||
update: (store, { data: { CreateComment } }) => {
|
||||
const data = store.readQuery({
|
||||
query: PostCommentsQuery(this.$i18n),
|
||||
query: PostQuery(this.$i18n),
|
||||
variables: { slug: this.post.slug },
|
||||
})
|
||||
data.Post[0].comments.push(CreateComment)
|
||||
store.writeQuery({ query: PostCommentsQuery(this.$i18n), data })
|
||||
store.writeQuery({ query: PostQuery(this.$i18n), data })
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
@ -1,5 +1,5 @@
|
||||
import { config, mount, createLocalVue } from '@vue/test-utils'
|
||||
import CommentList from '.'
|
||||
import CommentList from './CommentList'
|
||||
import Empty from '~/components/Empty'
|
||||
import Vuex from 'vuex'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
@ -21,63 +21,52 @@ describe('CommentList.vue', () => {
|
||||
let store
|
||||
let wrapper
|
||||
let propsData
|
||||
let data
|
||||
|
||||
propsData = {
|
||||
post: {
|
||||
id: 1,
|
||||
},
|
||||
}
|
||||
store = new Vuex.Store({
|
||||
getters: {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
})
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$filters: {
|
||||
truncate: a => a,
|
||||
},
|
||||
$apollo: {
|
||||
queries: {
|
||||
Post: {
|
||||
refetch: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
data = () => {
|
||||
return {
|
||||
comments: [],
|
||||
}
|
||||
}
|
||||
|
||||
describe('shallowMount', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
post: {
|
||||
id: 1,
|
||||
comments: [{ id: 'comment134', contentExcerpt: 'this is a comment' }],
|
||||
},
|
||||
}
|
||||
store = new Vuex.Store({
|
||||
getters: {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
})
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$filters: {
|
||||
truncate: a => a,
|
||||
},
|
||||
$apollo: {
|
||||
queries: {
|
||||
Post: {
|
||||
refetch: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(CommentList, {
|
||||
store,
|
||||
mocks,
|
||||
localVue,
|
||||
propsData,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.setData({
|
||||
comments: [
|
||||
{
|
||||
id: 'c1',
|
||||
contentExcerpt: 'this is a comment',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it('displays a message icon when there are no comments to display', () => {
|
||||
propsData.post.comments = []
|
||||
expect(Wrapper().findAll(Empty)).toHaveLength(1)
|
||||
})
|
||||
|
||||
@ -4,25 +4,25 @@
|
||||
<span>
|
||||
<ds-icon name="comments" />
|
||||
<ds-tag
|
||||
v-if="comments"
|
||||
v-if="post.comments.length"
|
||||
style="margin-top: -4px; margin-left: -12px; position: absolute;"
|
||||
color="primary"
|
||||
size="small"
|
||||
round
|
||||
>
|
||||
{{ comments.length }}
|
||||
{{ post.comments.length }}
|
||||
</ds-tag>
|
||||
Comments
|
||||
</span>
|
||||
</h3>
|
||||
<ds-space margin-bottom="large" />
|
||||
<div v-if="comments && comments.length" id="comments" class="comments">
|
||||
<div v-if="post.comments && post.comments.length" id="comments" class="comments">
|
||||
<comment
|
||||
v-for="(comment, index) in comments"
|
||||
v-for="(comment, index) in post.comments"
|
||||
:key="comment.id"
|
||||
:comment="comment"
|
||||
:post="post"
|
||||
@deleteComment="comments.splice(index, 1)"
|
||||
@deleteComment="post.comments.splice(index, 1)"
|
||||
/>
|
||||
</div>
|
||||
<hc-empty v-else name="empty" icon="messages" />
|
||||
@ -31,7 +31,6 @@
|
||||
<script>
|
||||
import Comment from '~/components/Comment.vue'
|
||||
import HcEmpty from '~/components/Empty.vue'
|
||||
import PostCommentsQuery from '~/graphql/PostCommentsQuery.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -41,29 +40,5 @@ export default {
|
||||
props: {
|
||||
post: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
comments: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
Post(post) {
|
||||
const [first] = post
|
||||
this.comments = (first && first.comments) || []
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
Post: {
|
||||
query() {
|
||||
return PostCommentsQuery(this.$i18n)
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
slug: this.post.slug,
|
||||
}
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -20,7 +20,7 @@ export default {
|
||||
},
|
||||
props: {
|
||||
startVal: { type: Number, default: 0 },
|
||||
endVal: { type: Number, required: true },
|
||||
endVal: { type: Number, default: 0 },
|
||||
duration: { type: Number, default: 3000 },
|
||||
autoplay: { type: Boolean, default: true },
|
||||
separator: { type: String, default: '.' },
|
||||
|
||||
@ -48,7 +48,7 @@ describe('DeleteData.vue', () => {
|
||||
}
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
return { id: 'u343', name: deleteAccountName, contributionsCount: 2, commentsCount: 3 }
|
||||
return { id: 'u343', name: deleteAccountName, contributionsCount: 2, commentedCount: 3 }
|
||||
},
|
||||
}
|
||||
actions = { 'auth/logout': jest.fn() }
|
||||
|
||||
@ -30,12 +30,12 @@
|
||||
}}
|
||||
</label>
|
||||
<ds-space margin-bottom="small" />
|
||||
<label v-if="currentUser.commentsCount" class="checkbox-container">
|
||||
<label v-if="currentUser.commentedCount" class="checkbox-container">
|
||||
<input type="checkbox" v-model="deleteComments" />
|
||||
<span class="checkmark"></span>
|
||||
{{
|
||||
$t('settings.deleteUserAccount.commentsCount', {
|
||||
count: currentUser.commentsCount,
|
||||
$t('settings.deleteUserAccount.commentedCount', {
|
||||
count: currentUser.commentedCount,
|
||||
})
|
||||
}}
|
||||
</label>
|
||||
|
||||
@ -1,27 +1,24 @@
|
||||
<template>
|
||||
<ds-card v-show="hashtag" class="filter-menu-card">
|
||||
<div>
|
||||
<ds-space margin-bottom="x-small" />
|
||||
<ds-flex>
|
||||
<ds-flex-item>
|
||||
<ds-heading size="h3">{{ $t('filter-menu.hashtag-search', { hashtag }) }}</ds-heading>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<div class="filter-menu-buttons">
|
||||
<ds-button
|
||||
v-tooltip="{
|
||||
content: this.$t('filter-menu.clearSearch'),
|
||||
placement: 'left',
|
||||
delay: { show: 500 },
|
||||
}"
|
||||
name="clear-search-button"
|
||||
icon="close"
|
||||
@click="clearSearch"
|
||||
/>
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</div>
|
||||
<ds-card class="filter-menu-card">
|
||||
<ds-flex class="filter-menu-content">
|
||||
<ds-flex-item>
|
||||
<ds-heading size="h3">{{ $t('filter-menu.hashtag-search', { hashtag }) }}</ds-heading>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<div class="filter-menu-buttons">
|
||||
<ds-button
|
||||
v-tooltip="{
|
||||
content: this.$t('filter-menu.clearSearch'),
|
||||
placement: 'left',
|
||||
delay: { show: 500 },
|
||||
}"
|
||||
name="clear-search-button"
|
||||
icon="close"
|
||||
@click="clearSearch"
|
||||
/>
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
@ -43,6 +40,11 @@ export default {
|
||||
background-color: $background-color-soft;
|
||||
}
|
||||
|
||||
.filter-menu-content {
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-menu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
23
webapp/components/MasonryGrid/MasonryGrid.spec.js
Normal file
23
webapp/components/MasonryGrid/MasonryGrid.spec.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import MasonryGrid from './MasonryGrid'
|
||||
|
||||
describe('MasonryGrid', () => {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(MasonryGrid)
|
||||
})
|
||||
|
||||
it('adds the "reset-grid-height" class when one or more children are updating', () => {
|
||||
wrapper.trigger('calculating-item-height')
|
||||
|
||||
expect(wrapper.classes()).toContain('reset-grid-height')
|
||||
})
|
||||
|
||||
it('removes the "reset-grid-height" class when all children have completed updating', () => {
|
||||
wrapper.setData({ itemsCalculating: 1 })
|
||||
wrapper.trigger('finished-calculating-item-height')
|
||||
|
||||
expect(wrapper.classes()).not.toContain('reset-grid-height')
|
||||
})
|
||||
})
|
||||
35
webapp/components/MasonryGrid/MasonryGrid.vue
Normal file
35
webapp/components/MasonryGrid/MasonryGrid.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<ds-grid
|
||||
:min-column-width="300"
|
||||
v-on:calculating-item-height="startCalculation"
|
||||
v-on:finished-calculating-item-height="endCalculation"
|
||||
:class="[itemsCalculating ? 'reset-grid-height' : '']"
|
||||
>
|
||||
<slot></slot>
|
||||
</ds-grid>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
itemsCalculating: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startCalculation() {
|
||||
this.itemsCalculating += 1
|
||||
},
|
||||
endCalculation() {
|
||||
this.itemsCalculating -= 1
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.reset-grid-height {
|
||||
grid-auto-rows: auto !important;
|
||||
align-items: self-start;
|
||||
}
|
||||
</style>
|
||||
27
webapp/components/MasonryGrid/MasonryGridItem.spec.js
Normal file
27
webapp/components/MasonryGrid/MasonryGridItem.spec.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import MasonryGridItem from './MasonryGridItem'
|
||||
|
||||
describe('MasonryGridItem', () => {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(MasonryGridItem)
|
||||
wrapper.vm.$parent.$emit = jest.fn()
|
||||
})
|
||||
|
||||
it('emits "calculating-item-height" when starting calculation', async () => {
|
||||
wrapper.vm.calculateItemHeight()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
const firstCallArgument = wrapper.vm.$parent.$emit.mock.calls[0][0]
|
||||
expect(firstCallArgument).toBe('calculating-item-height')
|
||||
})
|
||||
|
||||
it('emits "finished-calculating-item-height" after the calculation', async () => {
|
||||
wrapper.vm.calculateItemHeight()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
const secondCallArgument = wrapper.vm.$parent.$emit.mock.calls[1][0]
|
||||
expect(secondCallArgument).toBe('finished-calculating-item-height')
|
||||
})
|
||||
})
|
||||
39
webapp/components/MasonryGrid/MasonryGridItem.vue
Normal file
39
webapp/components/MasonryGrid/MasonryGridItem.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<ds-grid-item :rowSpan="rowSpan">
|
||||
<slot></slot>
|
||||
</ds-grid-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
rowSpan: 10,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
calculateItemHeight() {
|
||||
this.$parent.$emit('calculating-item-height')
|
||||
this.$nextTick(() => {
|
||||
const gridStyle = this.$parent.$el.style
|
||||
const rowHeight = parseInt(gridStyle.gridAutoRows)
|
||||
const rowGapValue = gridStyle.rowGap || gridStyle.gridRowGap
|
||||
const rowGap = parseInt(rowGapValue)
|
||||
const itemHeight = this.$el.clientHeight
|
||||
|
||||
this.rowSpan = Math.ceil((itemHeight + rowGap) / (rowHeight + rowGap))
|
||||
this.$parent.$emit('finished-calculating-item-height')
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const image = this.$el.querySelector('img')
|
||||
if (image) {
|
||||
image.onload = () => this.calculateItemHeight()
|
||||
} else {
|
||||
// use timeout to make sure layout is set up before calculation
|
||||
setTimeout(() => this.calculateItemHeight(), 0)
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -24,7 +24,7 @@ const post = {
|
||||
deleted: false,
|
||||
contributionsCount: 25,
|
||||
shoutedCount: 5,
|
||||
commentsCount: 39,
|
||||
commentedCount: 39,
|
||||
followedByCount: 2,
|
||||
followedByCurrentUser: true,
|
||||
location: null,
|
||||
@ -38,7 +38,7 @@ const post = {
|
||||
],
|
||||
__typename: 'User',
|
||||
},
|
||||
commentsCount: 12,
|
||||
commentedCount: 12,
|
||||
categories: [],
|
||||
shoutedCount: 421,
|
||||
__typename: 'Post',
|
||||
|
||||
@ -1,69 +1,71 @@
|
||||
<template>
|
||||
<ds-flex-item :width="width">
|
||||
<ds-card
|
||||
:image="post.image | proxyApiUrl"
|
||||
:class="{ 'post-card': true, 'disabled-content': post.disabled }"
|
||||
<ds-card
|
||||
:image="post.image | proxyApiUrl"
|
||||
:class="{ 'post-card': true, 'disabled-content': post.disabled }"
|
||||
>
|
||||
<!-- Post Link Target -->
|
||||
<nuxt-link
|
||||
class="post-link"
|
||||
:to="{ name: 'post-id-slug', params: { id: post.id, slug: post.slug } }"
|
||||
>
|
||||
<!-- Post Link Target -->
|
||||
<nuxt-link
|
||||
class="post-link"
|
||||
:to="{ name: 'post-id-slug', params: { id: post.id, slug: post.slug } }"
|
||||
>
|
||||
{{ post.title }}
|
||||
</nuxt-link>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Username, Image & Date of Post -->
|
||||
<div>
|
||||
<no-ssr>
|
||||
<hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" />
|
||||
</no-ssr>
|
||||
<hc-ribbon :text="$t('post.name')" />
|
||||
{{ post.title }}
|
||||
</nuxt-link>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Username, Image & Date of Post -->
|
||||
<div>
|
||||
<no-ssr>
|
||||
<hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" />
|
||||
</no-ssr>
|
||||
<hc-ribbon :text="$t('post.name')" />
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Title -->
|
||||
<ds-heading tag="h3" no-margin>{{ post.title }}</ds-heading>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Content Excerpt -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<div class="hc-editor-content" v-html="excerpt" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<!-- Footer o the Post -->
|
||||
<template slot="footer">
|
||||
<div style="display: inline-block; opacity: .5;">
|
||||
<!-- Categories -->
|
||||
<hc-category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{
|
||||
content: category.name,
|
||||
placement: 'bottom-start',
|
||||
delay: { show: 500 },
|
||||
}"
|
||||
:icon="category.icon"
|
||||
/>
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Title -->
|
||||
<ds-heading tag="h3" no-margin>{{ post.title }}</ds-heading>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Content Excerpt -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<div class="hc-editor-content" v-html="excerpt" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<!-- Footer o the Post -->
|
||||
<template slot="footer">
|
||||
<div style="display: inline-block; opacity: .5;">
|
||||
<!-- Categories -->
|
||||
<hc-category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{ content: category.name, placement: 'bottom-start', delay: { show: 500 } }"
|
||||
:icon="category.icon"
|
||||
<no-ssr>
|
||||
<div style="display: inline-block; float: right">
|
||||
<!-- Shouts Count -->
|
||||
<span :style="{ opacity: post.shoutedCount ? 1 : 0.5 }">
|
||||
<ds-icon name="bullhorn" />
|
||||
<small>{{ post.shoutedCount }}</small>
|
||||
</span>
|
||||
|
||||
<!-- Comments Count -->
|
||||
<span :style="{ opacity: post.commentedCount ? 1 : 0.5 }">
|
||||
<ds-icon name="comments" />
|
||||
<small>{{ post.commentedCount }}</small>
|
||||
</span>
|
||||
<!-- Menu -->
|
||||
<content-menu
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:modalsData="menuModalsData"
|
||||
:is-owner="isAuthor"
|
||||
/>
|
||||
</div>
|
||||
<no-ssr>
|
||||
<div style="display: inline-block; float: right">
|
||||
<!-- Shouts Count -->
|
||||
<span :style="{ opacity: post.shoutedCount ? 1 : 0.5 }">
|
||||
<ds-icon name="bullhorn" />
|
||||
<small>{{ post.shoutedCount }}</small>
|
||||
</span>
|
||||
|
||||
<!-- Comments Count -->
|
||||
<span :style="{ opacity: post.commentsCount ? 1 : 0.5 }">
|
||||
<ds-icon name="comments" />
|
||||
<small>{{ post.commentsCount }}</small>
|
||||
</span>
|
||||
<!-- Menu -->
|
||||
<content-menu
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:modalsData="menuModalsData"
|
||||
:is-owner="isAuthor"
|
||||
/>
|
||||
</div>
|
||||
</no-ssr>
|
||||
</template>
|
||||
</ds-card>
|
||||
</ds-flex-item>
|
||||
</no-ssr>
|
||||
</template>
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -122,7 +122,7 @@ describe('SearchInput.vue', () => {
|
||||
name: 'Trick',
|
||||
slug: 'trick',
|
||||
},
|
||||
commentsCount: 0,
|
||||
commentedCount: 0,
|
||||
createdAt: '2019-03-13T11:00:20.835Z',
|
||||
id: 'p10',
|
||||
label: 'Eos aut illo omnis quis eaque et iure aut.',
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<ds-flex-item>
|
||||
<ds-text size="small" color="softer" class="search-meta">
|
||||
<span style="text-align: right;">
|
||||
<b>{{ option.commentsCount }}</b>
|
||||
<b>{{ option.commentedCount }}</b>
|
||||
<ds-icon name="comments" />
|
||||
</span>
|
||||
<span style="width: 36px; display: inline-block; text-align: right;">
|
||||
|
||||
@ -50,7 +50,11 @@
|
||||
<ds-flex style="margin-top: -10px">
|
||||
<ds-flex-item class="ds-tab-nav-item">
|
||||
<ds-space margin="small">
|
||||
<ds-number :count="fanCount" :label="$t('profile.followers')" size="x-large" />
|
||||
<ds-number
|
||||
:count="user.followedByCount"
|
||||
:label="$t('profile.followers')"
|
||||
size="x-large"
|
||||
/>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item class="ds-tab-nav-item ds-tab-nav-item-active">
|
||||
@ -64,8 +68,8 @@
|
||||
<ds-flex-item class="ds-tab-nav-item">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="user.commentsCount"
|
||||
:label="$t('common.comment', null, user.commentsCount)"
|
||||
:count="user.commentedCount"
|
||||
:label="$t('common.comment', null, user.commentedCount)"
|
||||
/>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
@ -121,10 +125,6 @@ export default {
|
||||
itsMe() {
|
||||
return this.user.slug === this.$store.getters['auth/user'].slug
|
||||
},
|
||||
fanCount() {
|
||||
let count = Number(this.user.followedByCount) || 0
|
||||
return count
|
||||
},
|
||||
userLink() {
|
||||
const { id, slug } = this.user
|
||||
if (!(id && slug)) return ''
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ds-space :class="{ notification: true, read: notification.read }" margin-bottom="x-small">
|
||||
<ds-space :class="[{ read: notification.read }, notification]" margin-bottom="x-small">
|
||||
<no-ssr>
|
||||
<ds-space margin-bottom="x-small">
|
||||
<hc-user
|
||||
@ -28,10 +28,7 @@
|
||||
<ds-space margin-bottom="x-small" />
|
||||
<div v-if="resourceType == 'Post'">{{ post.contentExcerpt | removeHtml }}</div>
|
||||
<div v-else>
|
||||
<b>
|
||||
Comment:
|
||||
<nbsp />
|
||||
</b>
|
||||
<span class="comment-notification-header">Comment:</span>
|
||||
{{ comment.contentExcerpt | removeHtml }}
|
||||
</div>
|
||||
</ds-card>
|
||||
@ -94,4 +91,8 @@ export default {
|
||||
.notifications-card {
|
||||
min-width: 500px;
|
||||
}
|
||||
.comment-notification-header {
|
||||
font-weight: 700;
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default () => {
|
||||
export default i18n => {
|
||||
return {
|
||||
CreateComment: gql`
|
||||
mutation($postId: ID!, $content: String!) {
|
||||
@ -8,15 +8,26 @@ export default () => {
|
||||
id
|
||||
contentExcerpt
|
||||
content
|
||||
createdAt
|
||||
disabled
|
||||
deleted
|
||||
author {
|
||||
id
|
||||
slug
|
||||
name
|
||||
avatar
|
||||
disabled
|
||||
deleted
|
||||
shoutedCount
|
||||
contributionsCount
|
||||
commentedCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
badges {
|
||||
id
|
||||
icon
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
deleted
|
||||
disabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -24,8 +35,19 @@ export default () => {
|
||||
mutation($content: String!, $id: ID!) {
|
||||
UpdateComment(content: $content, id: $id) {
|
||||
id
|
||||
content
|
||||
contentExcerpt
|
||||
content
|
||||
createdAt
|
||||
disabled
|
||||
deleted
|
||||
author {
|
||||
id
|
||||
slug
|
||||
name
|
||||
avatar
|
||||
disabled
|
||||
deleted
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
@ -17,7 +17,7 @@ export default app => {
|
||||
deleted
|
||||
shoutedCount
|
||||
contributionsCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
location {
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default i18n => {
|
||||
const lang = i18n.locale().toUpperCase()
|
||||
return gql(`
|
||||
query Post($slug: String!) {
|
||||
Post(slug: $slug) {
|
||||
comments(orderBy: createdAt_asc) {
|
||||
id
|
||||
contentExcerpt
|
||||
content
|
||||
createdAt
|
||||
disabled
|
||||
deleted
|
||||
author {
|
||||
id
|
||||
slug
|
||||
name
|
||||
avatar
|
||||
disabled
|
||||
deleted
|
||||
shoutedCount
|
||||
contributionsCount
|
||||
commentsCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
location {
|
||||
name: name${lang}
|
||||
}
|
||||
badges {
|
||||
id
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
@ -22,7 +22,7 @@ export default i18n => {
|
||||
deleted
|
||||
shoutedCount
|
||||
contributionsCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
location {
|
||||
@ -36,10 +36,10 @@ export default i18n => {
|
||||
tags {
|
||||
id
|
||||
}
|
||||
commentsCount
|
||||
comments(orderBy: createdAt_desc) {
|
||||
comments(orderBy: createdAt_asc) {
|
||||
id
|
||||
contentExcerpt
|
||||
content
|
||||
createdAt
|
||||
disabled
|
||||
deleted
|
||||
@ -52,7 +52,7 @@ export default i18n => {
|
||||
deleted
|
||||
shoutedCount
|
||||
contributionsCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
location {
|
||||
@ -99,7 +99,7 @@ export const filterPosts = i18n => {
|
||||
deleted
|
||||
contributionsCount
|
||||
shoutedCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
location {
|
||||
@ -110,7 +110,6 @@ export const filterPosts = i18n => {
|
||||
icon
|
||||
}
|
||||
}
|
||||
commentsCount
|
||||
categories {
|
||||
id
|
||||
name
|
||||
|
||||
@ -35,7 +35,7 @@ export default i18n => {
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
contributionsCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
badges {
|
||||
id
|
||||
icon
|
||||
@ -57,7 +57,7 @@ export default i18n => {
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
contributionsCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
badges {
|
||||
id
|
||||
icon
|
||||
@ -66,7 +66,6 @@ export default i18n => {
|
||||
name: name${lang}
|
||||
}
|
||||
}
|
||||
contributionsCount
|
||||
socialMedia {
|
||||
id
|
||||
url
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default i18n => {
|
||||
const lang = i18n.locale().toUpperCase()
|
||||
return gql(`
|
||||
query Post($filter: _PostFilter, $first: Int, $offset: Int) {
|
||||
Post(filter: $filter, first: $first, offset: $offset, orderBy: createdAt_desc) {
|
||||
id
|
||||
slug
|
||||
title
|
||||
contentExcerpt
|
||||
shoutedCount
|
||||
commentsCount
|
||||
deleted
|
||||
image
|
||||
createdAt
|
||||
disabled
|
||||
deleted
|
||||
categories {
|
||||
id
|
||||
name
|
||||
icon
|
||||
}
|
||||
author {
|
||||
id
|
||||
slug
|
||||
avatar
|
||||
name
|
||||
disabled
|
||||
deleted
|
||||
location {
|
||||
name: name${lang}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const BlockedUsers = () => {
|
||||
return gql(`
|
||||
return gql`
|
||||
{
|
||||
blockedUsers {
|
||||
id
|
||||
@ -13,27 +13,31 @@ export const BlockedUsers = () => {
|
||||
deleted
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
}
|
||||
|
||||
export const Block = () => {
|
||||
return gql(`mutation($id:ID!) {
|
||||
block(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
followedByCurrentUser
|
||||
return gql`
|
||||
mutation($id: ID!) {
|
||||
block(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
followedByCurrentUser
|
||||
}
|
||||
}
|
||||
}`)
|
||||
`
|
||||
}
|
||||
|
||||
export const Unblock = () => {
|
||||
return gql(`mutation($id:ID!) {
|
||||
unblock(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
followedByCurrentUser
|
||||
return gql`
|
||||
mutation($id: ID!) {
|
||||
unblock(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
followedByCurrentUser
|
||||
}
|
||||
}
|
||||
}`)
|
||||
`
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@
|
||||
"deleteUserAccount": {
|
||||
"name": "Daten löschen",
|
||||
"contributionsCount": "Meine {count} Beiträge löschen",
|
||||
"commentsCount": "Meine {count} Kommentare löschen",
|
||||
"commentedCount": "Meine {count} Kommentare löschen",
|
||||
"accountDescription": "Sei dir bewusst, dass deine Beiträge und Kommentare für unsere Community wichtig sind. Wenn du sie trotzdem löschen möchtest, musst du sie unten markieren.",
|
||||
"accountWarning": "Dein Konto, deine Beiträge oder Kommentare kannst du nach dem Löschen <b>WEDER VERWALTEN NOCH WIEDERHERSTELLEN!</b>",
|
||||
"success": "Konto erfolgreich gelöscht!",
|
||||
|
||||
@ -173,7 +173,7 @@
|
||||
"deleteUserAccount": {
|
||||
"name": "Delete data",
|
||||
"contributionsCount": "Delete my {count} posts",
|
||||
"commentsCount": "Delete my {count} comments",
|
||||
"commentedCount": "Delete my {count} comments",
|
||||
"accountDescription": "Be aware that your Post and Comments are important to our community. If you still choose to delete them, you have to mark them below.",
|
||||
"accountWarning": "You <b>CAN'T MANAGE</b> and <b>CAN'T RECOVER</b> your Account, Posts, or Comments after deleting your account!",
|
||||
"success": "Account successfully deleted!",
|
||||
|
||||
@ -121,7 +121,7 @@
|
||||
"deleteUserAccount": {
|
||||
"name": "Usuń dane",
|
||||
"contributionsCount": "Usuń {count} moich postów",
|
||||
"commentsCount": "Usuń {count} moich komentarzy",
|
||||
"commentedCount": "Usuń {count} moich komentarzy",
|
||||
"accountDescription": "Be aware that your Post and Comments are important to our community. If you still choose to delete them, you have to mark them below.",
|
||||
"accountWarning": "Po usunięcie Twojego konta, nie możesz <b>ZARZĄDZAĆ</b> ani <b>ODZYSKAĆ</b> danych, wpisów oraz komentarzy!",
|
||||
"success": "Konto zostało usunięte",
|
||||
|
||||
@ -12,6 +12,9 @@ const styleguideStyles = process.env.STYLEGUIDE_DEV
|
||||
|
||||
const buildDir = process.env.NUXT_BUILD || '.nuxt'
|
||||
|
||||
const additionalSentryConfig = {}
|
||||
if (process.env.COMMIT) additionalSentryConfig.release = process.env.COMMIT
|
||||
|
||||
module.exports = {
|
||||
buildDir,
|
||||
mode: 'universal',
|
||||
@ -218,6 +221,7 @@ module.exports = {
|
||||
'@nuxtjs/apollo',
|
||||
'@nuxtjs/axios',
|
||||
'@nuxtjs/style-resources',
|
||||
'@nuxtjs/sentry',
|
||||
],
|
||||
|
||||
/*
|
||||
@ -284,6 +288,12 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
|
||||
sentry: {
|
||||
dsn: process.env.SENTRY_DSN_WEBAPP,
|
||||
publishRelease: !!process.env.COMMIT,
|
||||
config: additionalSentryConfig,
|
||||
},
|
||||
|
||||
manifest: {
|
||||
name: 'Human-Connection.org',
|
||||
description: 'Human-Connection.org',
|
||||
|
||||
@ -51,16 +51,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@human-connection/styleguide": "0.5.19",
|
||||
"@nuxtjs/apollo": "^4.0.0-rc10",
|
||||
"@nuxtjs/apollo": "^4.0.0-rc9",
|
||||
"@nuxtjs/axios": "~5.5.4",
|
||||
"@nuxtjs/dotenv": "~1.4.0",
|
||||
"@nuxtjs/sentry": "^3.0.0",
|
||||
"@nuxtjs/style-resources": "~1.0.0",
|
||||
"accounting": "~0.4.1",
|
||||
"apollo-cache-inmemory": "~1.6.3",
|
||||
"apollo-client": "~2.6.4",
|
||||
"cookie-universal-nuxt": "~2.0.17",
|
||||
"cross-env": "~5.2.0",
|
||||
"date-fns": "2.0.0-beta.4",
|
||||
"date-fns": "2.0.0-beta.5",
|
||||
"express": "~4.17.1",
|
||||
"graphql": "~14.4.2",
|
||||
"isemail": "^3.2.0",
|
||||
@ -78,8 +79,8 @@
|
||||
"vue-count-to": "~1.0.13",
|
||||
"vue-infinite-scroll": "^2.0.2",
|
||||
"vue-izitoast": "roschaefer/vue-izitoast#patch-1",
|
||||
"vuex-i18n": "~1.13.1",
|
||||
"vue-sweetalert-icons": "~4.2.0",
|
||||
"vuex-i18n": "~1.13.1",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -95,13 +96,13 @@
|
||||
"@vue/test-utils": "~1.0.0-beta.29",
|
||||
"babel-core": "~7.0.0-bridge.0",
|
||||
"babel-eslint": "~10.0.2",
|
||||
"babel-jest": "~24.8.0",
|
||||
"babel-jest": "~24.9.0",
|
||||
"babel-loader": "~8.0.6",
|
||||
"babel-preset-vue": "~2.0.2",
|
||||
"core-js": "~2.6.9",
|
||||
"css-loader": "~2.1.1",
|
||||
"eslint": "~5.16.0",
|
||||
"eslint-config-prettier": "~6.0.0",
|
||||
"eslint-config-prettier": "~6.1.0",
|
||||
"eslint-config-standard": "~12.0.0",
|
||||
"eslint-loader": "~2.2.1",
|
||||
"eslint-plugin-import": "~2.18.2",
|
||||
@ -109,11 +110,11 @@
|
||||
"eslint-plugin-node": "~9.1.0",
|
||||
"eslint-plugin-prettier": "~3.1.0",
|
||||
"eslint-plugin-promise": "~4.2.1",
|
||||
"eslint-plugin-standard": "~4.0.0",
|
||||
"eslint-plugin-standard": "~4.0.1",
|
||||
"eslint-plugin-vue": "~5.2.3",
|
||||
"flush-promises": "^1.0.2",
|
||||
"fuse.js": "^3.4.5",
|
||||
"jest": "~24.8.0",
|
||||
"jest": "~24.9.0",
|
||||
"mutation-observer": "^1.0.3",
|
||||
"node-sass": "~4.12.0",
|
||||
"nodemon": "~1.19.1",
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<ds-space margin="small">
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.users')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countUsers || 0" />
|
||||
<hc-count-to :end-val="statistics.countUsers" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -16,7 +16,7 @@
|
||||
<ds-space margin="small">
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.posts')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countPosts || 0" />
|
||||
<hc-count-to :end-val="statistics.countPosts" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -30,7 +30,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countComments || 0" />
|
||||
<hc-count-to :end-val="statistics.countComments" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -44,7 +44,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countNotifications || 0" />
|
||||
<hc-count-to :end-val="statistics.countNotifications" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -58,7 +58,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countOrganizations || 0" />
|
||||
<hc-count-to :end-val="statistics.countOrganizations" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -72,7 +72,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countProjects || 0" />
|
||||
<hc-count-to :end-val="statistics.countProjects" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -81,7 +81,7 @@
|
||||
<ds-space margin="small">
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.invites')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countInvites || 0" />
|
||||
<hc-count-to :end-val="statistics.countInvites" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -90,7 +90,7 @@
|
||||
<ds-space margin="small">
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.follows')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countFollows || 0" />
|
||||
<hc-count-to :end-val="statistics.countFollows" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -99,7 +99,7 @@
|
||||
<ds-space margin="small">
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.shouts')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to :end-val="statistics.countShouts || 0" />
|
||||
<hc-count-to :end-val="statistics.countShouts" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-flex :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item>
|
||||
<masonry-grid>
|
||||
<ds-grid-item v-show="hashtag" :row-span="2" column-span="fullWidth">
|
||||
<filter-menu :hashtag="hashtag" @clearSearch="clearSearch" />
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
</ds-grid-item>
|
||||
<ds-grid-item :row-span="2" column-span="fullWidth">
|
||||
<div class="sorting-dropdown">
|
||||
<ds-select
|
||||
v-model="selected"
|
||||
@ -14,15 +14,15 @@
|
||||
@input="toggleOnlySorting"
|
||||
></ds-select>
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
<hc-post-card
|
||||
v-for="post in posts"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
|
||||
@removePostFromList="deletePost(index, post.id)"
|
||||
/>
|
||||
</ds-flex>
|
||||
</ds-grid-item>
|
||||
<masonry-grid-item v-for="post in posts" :key="post.id">
|
||||
<hc-post-card
|
||||
:post="post"
|
||||
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
|
||||
@removePostFromList="deletePost(index, post.id)"
|
||||
/>
|
||||
</masonry-grid-item>
|
||||
</masonry-grid>
|
||||
<no-ssr>
|
||||
<ds-button
|
||||
v-tooltip="{ content: 'Create a new Post', placement: 'left', delay: { show: 500 } }"
|
||||
@ -51,6 +51,8 @@ import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
|
||||
import uniqBy from 'lodash/uniqBy'
|
||||
import HcPostCard from '~/components/PostCard'
|
||||
import HcLoadMore from '~/components/LoadMore.vue'
|
||||
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
|
||||
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { filterPosts } from '~/graphql/PostQuery.js'
|
||||
|
||||
@ -59,6 +61,8 @@ export default {
|
||||
FilterMenu,
|
||||
HcPostCard,
|
||||
HcLoadMore,
|
||||
MasonryGrid,
|
||||
MasonryGridItem,
|
||||
},
|
||||
data() {
|
||||
const { hashtag = null } = this.$route.query
|
||||
@ -96,7 +100,7 @@ export default {
|
||||
label: this.$t('sorting.commented'),
|
||||
value: 'Commented',
|
||||
icons: 'comment',
|
||||
order: 'commentsCount_desc',
|
||||
order: 'commentedCount_desc',
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -177,6 +181,21 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.masonry-grid {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
grid-auto-rows: 20px;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
grid-row-end: span 2;
|
||||
|
||||
&--full-width {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
.post-add-button {
|
||||
z-index: 100;
|
||||
position: fixed;
|
||||
@ -191,5 +210,6 @@ export default {
|
||||
position: relative;
|
||||
float: right;
|
||||
padding: 0 18px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -81,10 +81,10 @@ import HcTag from '~/components/Tag'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
import HcUser from '~/components/User'
|
||||
import HcShoutButton from '~/components/ShoutButton.vue'
|
||||
import HcCommentForm from '~/components/comments/CommentForm/CommentForm'
|
||||
import HcCommentList from '~/components/comments/CommentList'
|
||||
import HcCommentForm from '~/components/CommentForm/CommentForm'
|
||||
import HcCommentList from '~/components/CommentList/CommentList'
|
||||
import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers'
|
||||
import PostQuery from '~/graphql/PostQuery.js'
|
||||
import PostQuery from '~/graphql/PostQuery'
|
||||
import HcEmotions from '~/components/Emotions/Emotions'
|
||||
|
||||
export default {
|
||||
@ -122,29 +122,6 @@ export default {
|
||||
this.title = this.post.title
|
||||
},
|
||||
},
|
||||
async asyncData(context) {
|
||||
const {
|
||||
params,
|
||||
error,
|
||||
app: { apolloProvider, $i18n },
|
||||
} = context
|
||||
const client = apolloProvider.defaultClient
|
||||
const query = PostQuery($i18n)
|
||||
const variables = { slug: params.slug }
|
||||
const {
|
||||
data: { Post },
|
||||
} = await client.query({ query, variables })
|
||||
if (Post.length <= 0) {
|
||||
// TODO: custom 404 error page with translations
|
||||
const message = 'This post could not be found'
|
||||
return error({ statusCode: 404, message })
|
||||
}
|
||||
const [post] = Post
|
||||
return {
|
||||
post,
|
||||
title: post.title,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
// NOTE: quick fix for jumping flexbox implementation
|
||||
@ -175,6 +152,19 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
Post: {
|
||||
query() {
|
||||
return PostQuery(this.$i18n)
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
slug: this.$route.params.slug,
|
||||
}
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -187,7 +177,6 @@ export default {
|
||||
}
|
||||
|
||||
.post-card {
|
||||
// max-width: 800px;
|
||||
margin: auto;
|
||||
|
||||
.comments {
|
||||
|
||||
@ -91,7 +91,7 @@ export default {
|
||||
slug
|
||||
contentExcerpt
|
||||
shoutedCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
categories {
|
||||
id
|
||||
name
|
||||
@ -105,7 +105,7 @@ export default {
|
||||
contributionsCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
commentsCount
|
||||
commentedCount
|
||||
location {
|
||||
name: name${this.$i18n.locale().toUpperCase()}
|
||||
}
|
||||
|
||||
@ -43,14 +43,14 @@
|
||||
<ds-flex-item>
|
||||
<no-ssr>
|
||||
<ds-number :label="$t('profile.followers')">
|
||||
<hc-count-to slot="count" :end-val="followedByCount" />
|
||||
<hc-count-to slot="count" :end-val="user.followedByCount" />
|
||||
</ds-number>
|
||||
</no-ssr>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<no-ssr>
|
||||
<ds-number :label="$t('profile.following')">
|
||||
<hc-count-to slot="count" :end-val="Number(user.followingCount) || 0" />
|
||||
<hc-count-to slot="count" :end-val="user.followingCount" />
|
||||
</ds-number>
|
||||
</no-ssr>
|
||||
</ds-flex-item>
|
||||
@ -153,8 +153,8 @@
|
||||
</ds-flex-item>
|
||||
|
||||
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
|
||||
<ds-flex class="user-profile-posts-list" :width="{ base: '100%' }" gutter="small">
|
||||
<ds-flex-item class="profile-top-navigation">
|
||||
<masonry-grid class="user-profile-posts-list">
|
||||
<ds-grid-item class="profile-top-navigation" :row-span="3" column-span="fullWidth">
|
||||
<ds-card class="ds-tab-nav">
|
||||
<ul class="Tabs">
|
||||
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'post' }">
|
||||
@ -193,9 +193,9 @@
|
||||
<li class="Tabs__presentation-slider" role="presentation"></li>
|
||||
</ul>
|
||||
</ds-card>
|
||||
</ds-flex-item>
|
||||
</ds-grid-item>
|
||||
|
||||
<ds-flex-item style="text-align: center">
|
||||
<ds-grid-item :row-span="2" column-span="fullWidth" class="create-button">
|
||||
<ds-button
|
||||
v-if="myProfile"
|
||||
v-tooltip="{ content: 'Create a new Post', placement: 'left', delay: { show: 500 } }"
|
||||
@ -205,30 +205,30 @@
|
||||
size="large"
|
||||
primary
|
||||
/>
|
||||
</ds-flex-item>
|
||||
</ds-grid-item>
|
||||
|
||||
<template v-if="activePosts.length">
|
||||
<hc-post-card
|
||||
v-for="(post, index) in activePosts"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||
@removePostFromList="removePostFromList(index)"
|
||||
/>
|
||||
<masonry-grid-item v-for="(post, index) in activePosts" :key="post.id">
|
||||
<hc-post-card
|
||||
:post="post"
|
||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||
@removePostFromList="removePostFromList(index)"
|
||||
/>
|
||||
</masonry-grid-item>
|
||||
</template>
|
||||
<template v-else-if="$apollo.loading">
|
||||
<ds-flex-item>
|
||||
<ds-grid-item>
|
||||
<ds-section centered>
|
||||
<ds-spinner size="base"></ds-spinner>
|
||||
</ds-section>
|
||||
</ds-flex-item>
|
||||
</ds-grid-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ds-flex-item :width="{ base: '100%' }">
|
||||
<ds-grid-item column-span="fullWidth">
|
||||
<hc-empty margin="xx-large" icon="file" />
|
||||
</ds-flex-item>
|
||||
</ds-grid-item>
|
||||
</template>
|
||||
</ds-flex>
|
||||
</masonry-grid>
|
||||
<div
|
||||
v-if="hasMore"
|
||||
v-infinite-scroll="showMoreContributions"
|
||||
@ -254,9 +254,11 @@ import HcEmpty from '~/components/Empty.vue'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
import HcUpload from '~/components/Upload'
|
||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
||||
import PostQuery from '~/graphql/UserProfile/Post.js'
|
||||
import UserQuery from '~/graphql/User.js'
|
||||
import { Block, Unblock } from '~/graphql/settings/BlockedUsers.js'
|
||||
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
|
||||
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
|
||||
import { filterPosts } from '~/graphql/PostQuery'
|
||||
import UserQuery from '~/graphql/User'
|
||||
import { Block, Unblock } from '~/graphql/settings/BlockedUsers'
|
||||
|
||||
const tabToFilterMapping = ({ tab, id }) => {
|
||||
return {
|
||||
@ -279,6 +281,8 @@ export default {
|
||||
HcAvatar,
|
||||
ContentMenu,
|
||||
HcUpload,
|
||||
MasonryGrid,
|
||||
MasonryGridItem,
|
||||
},
|
||||
transition: {
|
||||
name: 'slide-up',
|
||||
@ -309,10 +313,6 @@ export default {
|
||||
myProfile() {
|
||||
return this.$route.params.id === this.$store.getters['auth/user'].id
|
||||
},
|
||||
followedByCount() {
|
||||
let count = Number(this.user.followedByCount) || 0
|
||||
return count
|
||||
},
|
||||
user() {
|
||||
return this.User ? this.User[0] : {}
|
||||
},
|
||||
@ -401,7 +401,7 @@ export default {
|
||||
apollo: {
|
||||
Post: {
|
||||
query() {
|
||||
return PostQuery(this.$i18n)
|
||||
return filterPosts(this.$i18n)
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
@ -429,6 +429,10 @@ export default {
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.create-button {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
.Tab {
|
||||
border-collapse: collapse;
|
||||
padding-bottom: 5px;
|
||||
@ -439,6 +443,8 @@ export default {
|
||||
.Tabs {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: table;
|
||||
@ -447,10 +453,13 @@ export default {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
&__tab {
|
||||
float: left;
|
||||
width: 33.333%;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
|
||||
&:first-child.active ~ .Tabs__presentation-slider {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ export const actions = {
|
||||
data: { currentUser },
|
||||
} = await client.query({
|
||||
query: gql`
|
||||
{
|
||||
query {
|
||||
currentUser {
|
||||
id
|
||||
name
|
||||
@ -81,7 +81,7 @@ export const actions = {
|
||||
about
|
||||
locationName
|
||||
contributionsCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
socialMedia {
|
||||
id
|
||||
url
|
||||
|
||||
@ -88,7 +88,7 @@ export const actions = {
|
||||
deleted
|
||||
contributionsCount
|
||||
shoutedCount
|
||||
commentsCount
|
||||
commentedCount
|
||||
followedByCount
|
||||
followedByCurrentUser
|
||||
location {
|
||||
@ -99,7 +99,6 @@ export const actions = {
|
||||
icon
|
||||
}
|
||||
}
|
||||
commentsCount
|
||||
categories {
|
||||
id
|
||||
name
|
||||
|
||||
@ -45,15 +45,14 @@ export const actions = {
|
||||
commit('SET_QUICK_PENDING', true)
|
||||
await this.app.apolloProvider.defaultClient
|
||||
.query({
|
||||
query: gql(`
|
||||
query: gql`
|
||||
query findPosts($query: String!) {
|
||||
findPosts(query: $query, limit: 10) {
|
||||
id
|
||||
slug
|
||||
label: title
|
||||
value: title,
|
||||
value: title
|
||||
shoutedCount
|
||||
commentsCount
|
||||
createdAt
|
||||
author {
|
||||
id
|
||||
@ -62,7 +61,7 @@ export const actions = {
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
`,
|
||||
variables: {
|
||||
query: value.replace(/\s/g, '~ ') + '~',
|
||||
},
|
||||
|
||||
1026
webapp/yarn.lock
1026
webapp/yarn.lock
File diff suppressed because it is too large
Load Diff
20
yarn.lock
20
yarn.lock
@ -2084,10 +2084,10 @@ dotenv@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
|
||||
integrity sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=
|
||||
|
||||
dotenv@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.0.0.tgz#ed310c165b4e8a97bb745b0a9d99c31bda566440"
|
||||
integrity sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==
|
||||
dotenv@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2"
|
||||
integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==
|
||||
|
||||
duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
|
||||
version "0.1.4"
|
||||
@ -3570,7 +3570,7 @@ needle@^2.2.1:
|
||||
iconv-lite "^0.4.4"
|
||||
sax "^1.2.4"
|
||||
|
||||
neo4j-driver@^1.6.3, neo4j-driver@^1.7.5:
|
||||
neo4j-driver@^1.7.5:
|
||||
version "1.7.5"
|
||||
resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-1.7.5.tgz#c3fe3677f69c12f26944563d45e7e7d818a685e4"
|
||||
integrity sha512-xCD2F5+tp/SD9r5avX5bSoY8u8RH2o793xJ9Ikjz1s5qQy7cFxFbbj2c52uz3BVGhRAx/NmB57VjOquYmmxGtw==
|
||||
@ -3579,14 +3579,14 @@ neo4j-driver@^1.6.3, neo4j-driver@^1.7.5:
|
||||
text-encoding-utf-8 "^1.0.2"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
neode@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/neode/-/neode-0.3.1.tgz#d40147bf20d6951b69c9d392fbdd322aeca07816"
|
||||
integrity sha512-SdaJmdjQ3PWOH6W1H8Xgd2CLyJs+BPPXPt0jOVNs7naeQH8nWPP6ixDqI6NWDCxwecTdNl//fpAicB9I6hCwEw==
|
||||
neode@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/neode/-/neode-0.3.2.tgz#ced277e1daba26a77c48f5857c30af054f11c7df"
|
||||
integrity sha512-Bm4GBXdXunv8cqUUkJtksIGHDnYdBJf4UHwzFgXbJiDKBAdqfjhzwAPAhf1PrvlFmR4vJva2Bh/XvIghYOiKrA==
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.1.0"
|
||||
dotenv "^4.0.0"
|
||||
neo4j-driver "^1.6.3"
|
||||
neo4j-driver "^1.7.5"
|
||||
uuid "^3.3.2"
|
||||
|
||||
next-tick@^1.0.0:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user