mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' into log-errors
This commit is contained in:
commit
941399ff2f
23
.github/workflows/test-e2e.yml
vendored
23
.github/workflows/test-e2e.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Copy backend env file
|
||||
run: |
|
||||
@ -46,7 +46,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Copy backend env file
|
||||
run: |
|
||||
@ -69,7 +69,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@08f58d1471bff7f3a07d167b4ad7df25d5fcfcb6 # v4.4.0
|
||||
with:
|
||||
node-version-file: 'backend/.tool-versions'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Copy env files
|
||||
run: |
|
||||
@ -107,6 +113,15 @@ jobs:
|
||||
# run copies of the current job in parallel
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@08f58d1471bff7f3a07d167b4ad7df25d5fcfcb6 # v4.4.0
|
||||
with:
|
||||
node-version-file: 'backend/.tool-versions'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Restore cypress cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
@ -172,7 +187,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Full stack tests | cleanup cache
|
||||
run: |
|
||||
|
||||
@ -83,7 +83,6 @@
|
||||
"nodemailer-html-to-text": "^3.2.0",
|
||||
"preview-email": "^3.1.0",
|
||||
"pug": "^3.0.3",
|
||||
"request": "~2.88.2",
|
||||
"sanitize-html": "~2.17.0",
|
||||
"slug": "~9.1.0",
|
||||
"trunc-html": "~1.1.2",
|
||||
|
||||
11
backend/src/db/types/Category.ts
Normal file
11
backend/src/db/types/Category.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface CategoryDbProperties {
|
||||
createdAt: string
|
||||
icon: string
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
}
|
||||
|
||||
export type Category = Node<Integer, CategoryDbProperties>
|
||||
13
backend/src/db/types/Comment.ts
Normal file
13
backend/src/db/types/Comment.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface CommentDbProperties {
|
||||
content: string
|
||||
contentExcerpt: string
|
||||
createdAt: string
|
||||
deleted: boolean
|
||||
disabled: boolean
|
||||
id: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type Comment = Node<Integer, CommentDbProperties>
|
||||
10
backend/src/db/types/EmailAddress.ts
Normal file
10
backend/src/db/types/EmailAddress.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface EmailAddressDbProperties {
|
||||
createdAt: string
|
||||
email: string
|
||||
nonce: string
|
||||
verifiedAt: string
|
||||
}
|
||||
|
||||
export type EmailAddress = Node<Integer, EmailAddressDbProperties>
|
||||
12
backend/src/db/types/Event.ts
Normal file
12
backend/src/db/types/Event.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
import { PostDbProperties } from './Post'
|
||||
|
||||
export interface EventDbProperties extends PostDbProperties {
|
||||
eventIsOnline: boolean
|
||||
eventLocationName: string
|
||||
eventStart: string
|
||||
eventVenue: string
|
||||
}
|
||||
|
||||
export type Event = Node<Integer, EventDbProperties>
|
||||
19
backend/src/db/types/Group.ts
Normal file
19
backend/src/db/types/Group.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface GroupDbProperties {
|
||||
about: string
|
||||
actionRadius: string
|
||||
createdAt: string
|
||||
deleted: boolean
|
||||
description: string
|
||||
descriptionExcerpt: string
|
||||
disabled: boolean
|
||||
groupType: string
|
||||
id: string
|
||||
locationName?: string
|
||||
name: string
|
||||
slug: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type Group = Node<Integer, GroupDbProperties>
|
||||
12
backend/src/db/types/Image.ts
Normal file
12
backend/src/db/types/Image.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface ImageDbProperties {
|
||||
alt: string
|
||||
aspectRatio: number
|
||||
createdAt: string
|
||||
sensitive: boolean
|
||||
type: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export type Image = Node<Integer, ImageDbProperties>
|
||||
8
backend/src/db/types/InviteCode.ts
Normal file
8
backend/src/db/types/InviteCode.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface InviteCodeDbProperties {
|
||||
code: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type InviteCode = Node<Integer, InviteCodeDbProperties>
|
||||
20
backend/src/db/types/Location.ts
Normal file
20
backend/src/db/types/Location.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface LocationDbProperties {
|
||||
id: string
|
||||
lat: number
|
||||
lng: number
|
||||
name: string
|
||||
nameDE: string
|
||||
nameEN: string
|
||||
nameES: string
|
||||
nameFR: string
|
||||
nameIT: string
|
||||
nameNL: string
|
||||
namePL: string
|
||||
namePT: string
|
||||
nameRU: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export type Location = Node<Integer, LocationDbProperties>
|
||||
13
backend/src/db/types/Message.ts
Normal file
13
backend/src/db/types/Message.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface MessageDbProperties {
|
||||
content: string
|
||||
createdAt: string
|
||||
distributed: boolean
|
||||
id: string
|
||||
indexId: number
|
||||
saved: boolean
|
||||
seen: boolean
|
||||
}
|
||||
|
||||
export type Message = Node<Integer, MessageDbProperties>
|
||||
21
backend/src/db/types/Post.ts
Normal file
21
backend/src/db/types/Post.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface PostDbProperties {
|
||||
clickedCount: number
|
||||
content: string
|
||||
contentExcerpt: string
|
||||
createdAt: string
|
||||
deleted: boolean
|
||||
disabled: boolean
|
||||
id: string
|
||||
language: string
|
||||
postType: string // this is a PostType[] in the graphql, mapped from the labels
|
||||
slug: string
|
||||
sortDate: string
|
||||
title: string
|
||||
updatedAt: string
|
||||
viewedTeaserCount: number
|
||||
}
|
||||
|
||||
export type Post = Node<Integer, PostDbProperties>
|
||||
export type Article = Node<Integer, PostDbProperties>
|
||||
11
backend/src/db/types/Report.ts
Normal file
11
backend/src/db/types/Report.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface ReportDbProperties {
|
||||
closed: boolean
|
||||
createdAt: string
|
||||
id: string
|
||||
rule: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type Report = Node<Integer, ReportDbProperties>
|
||||
10
backend/src/db/types/Tag.ts
Normal file
10
backend/src/db/types/Tag.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface TagDbProperties {
|
||||
deleted: boolean
|
||||
disabled: boolean
|
||||
id: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type Tag = Node<Integer, TagDbProperties>
|
||||
@ -6,27 +6,15 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable promise/avoid-new */
|
||||
/* eslint-disable promise/prefer-await-to-callbacks */
|
||||
/* eslint-disable n/no-unsupported-features/node-builtins */
|
||||
import { UserInputError } from 'apollo-server'
|
||||
import request from 'request'
|
||||
|
||||
import CONFIG from '@config/index'
|
||||
|
||||
const fetch = (url) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(url, function (error, response, body) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(JSON.parse(body))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl', 'ru']
|
||||
|
||||
const REQUEST_TIMEOUT = 3000
|
||||
|
||||
const createLocation = async (session, mapboxData) => {
|
||||
const data = {
|
||||
id: mapboxData.id + (mapboxData.address ? `-${mapboxData.address}` : ''),
|
||||
@ -78,74 +66,80 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
|
||||
|
||||
let locationId
|
||||
|
||||
if (locationName !== null) {
|
||||
const res: any = await fetch(
|
||||
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
|
||||
locationName,
|
||||
)}.json?access_token=${
|
||||
CONFIG.MAPBOX_TOKEN
|
||||
}&types=region,place,country,address&language=${locales.join(',')}`,
|
||||
)
|
||||
try {
|
||||
if (locationName !== null) {
|
||||
const response: any = await fetch(
|
||||
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
|
||||
locationName,
|
||||
)}.json?access_token=${
|
||||
CONFIG.MAPBOX_TOKEN
|
||||
}&types=region,place,country,address&language=${locales.join(',')}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
|
||||
},
|
||||
)
|
||||
|
||||
if (!res?.features?.[0]) {
|
||||
throw new UserInputError('locationName is invalid')
|
||||
}
|
||||
const res = await response.json()
|
||||
|
||||
let data
|
||||
|
||||
res.features.forEach((item) => {
|
||||
if (item.matching_place_name === locationName) {
|
||||
data = item
|
||||
if (!res?.features?.[0]) {
|
||||
throw new UserInputError('locationName is invalid')
|
||||
}
|
||||
})
|
||||
if (!data) {
|
||||
data = res.features[0]
|
||||
}
|
||||
|
||||
if (!data?.place_type?.length) {
|
||||
throw new UserInputError('locationName is invalid')
|
||||
}
|
||||
let data
|
||||
|
||||
if (data.place_type.length > 1) {
|
||||
data.id = 'region.' + data.id.split('.')[1]
|
||||
}
|
||||
await createLocation(session, data)
|
||||
res.features.forEach((item) => {
|
||||
if (item.matching_place_name === locationName) {
|
||||
data = item
|
||||
}
|
||||
})
|
||||
if (!data) {
|
||||
data = res.features[0]
|
||||
}
|
||||
|
||||
let parent = data
|
||||
if (!data?.place_type?.length) {
|
||||
throw new UserInputError('locationName is invalid')
|
||||
}
|
||||
|
||||
if (parent.address) {
|
||||
parent.id += `-${parent.address}`
|
||||
}
|
||||
if (data.place_type.length > 1) {
|
||||
data.id = 'region.' + data.id.split('.')[1]
|
||||
}
|
||||
await createLocation(session, data)
|
||||
|
||||
if (data.context) {
|
||||
for await (const ctx of data.context) {
|
||||
await createLocation(session, ctx)
|
||||
await session.writeTransaction((transaction) => {
|
||||
return transaction.run(
|
||||
`
|
||||
let parent = data
|
||||
|
||||
if (parent.address) {
|
||||
parent.id += `-${parent.address}`
|
||||
}
|
||||
|
||||
if (data.context) {
|
||||
for await (const ctx of data.context) {
|
||||
await createLocation(session, ctx)
|
||||
await session.writeTransaction((transaction) => {
|
||||
return transaction.run(
|
||||
`
|
||||
MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId})
|
||||
MERGE (child)<-[:IS_IN]-(parent)
|
||||
RETURN child.id, parent.id
|
||||
`,
|
||||
{
|
||||
parentId: parent.id,
|
||||
childId: ctx.id,
|
||||
},
|
||||
)
|
||||
})
|
||||
parent = ctx
|
||||
{
|
||||
parentId: parent.id,
|
||||
childId: ctx.id,
|
||||
},
|
||||
)
|
||||
})
|
||||
parent = ctx
|
||||
}
|
||||
}
|
||||
|
||||
locationId = data.id
|
||||
} else {
|
||||
locationId = 'non-existent-id'
|
||||
}
|
||||
|
||||
locationId = data.id
|
||||
} else {
|
||||
locationId = 'non-existent-id'
|
||||
}
|
||||
|
||||
// delete all current locations from node and add new location
|
||||
await session.writeTransaction((transaction) => {
|
||||
return transaction.run(
|
||||
`
|
||||
// delete all current locations from node and add new location
|
||||
await session.writeTransaction((transaction) => {
|
||||
return transaction.run(
|
||||
`
|
||||
MATCH (node:${nodeLabel} {id: $nodeId})
|
||||
OPTIONAL MATCH (node)-[relationship:IS_IN]->(:Location)
|
||||
DELETE relationship
|
||||
@ -154,18 +148,29 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
|
||||
MERGE (node)-[:IS_IN]->(location)
|
||||
RETURN location.id, node.id
|
||||
`,
|
||||
{ nodeId, locationId },
|
||||
)
|
||||
})
|
||||
{ nodeId, locationId },
|
||||
)
|
||||
})
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export const queryLocations = async ({ place, lang }) => {
|
||||
const res: any = await fetch(
|
||||
`https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${CONFIG.MAPBOX_TOKEN}&types=region,place,country&language=${lang}`,
|
||||
)
|
||||
// Return empty array if no location found or error occurred
|
||||
if (!res?.features) {
|
||||
return []
|
||||
try {
|
||||
const res: any = await fetch(
|
||||
`https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${CONFIG.MAPBOX_TOKEN}&types=region,place,country&language=${lang}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
|
||||
},
|
||||
)
|
||||
const response = await res.json()
|
||||
// Return empty array if no location found or error occurred
|
||||
if (!response?.features) {
|
||||
return []
|
||||
}
|
||||
return response.features
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
return res.features
|
||||
}
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||
|
||||
defineStep('I see all the reported posts including the one from above', () => {
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '/api',
|
||||
hostname: 'localhost',
|
||||
}).as('getReports')
|
||||
|
||||
cy.wait(['@getReports'],{ timeout: 30000 }).then((interception) => {
|
||||
console.log('Cypress interception:', interception)
|
||||
cy.wait('@reportsQuery', { timeout: 30000 }).then((interception) => {
|
||||
cy.wrap(interception.response.statusCode).should('eq', 200)
|
||||
cy.wrap(interception.request.body)
|
||||
.should('have.property', 'query', `query ($orderBy: ReportOrdering, $first: Int, $offset: Int, $reviewed: Boolean, $closed: Boolean) {
|
||||
@ -104,6 +97,6 @@ defineStep('I see all the reported posts including the one from above', () => {
|
||||
})
|
||||
|
||||
cy.get('table tbody').within(() => {
|
||||
cy.contains('tr', 'The Truth about the Holocaust')
|
||||
cy.contains('tr', 'The Truth about the Holocaust').should('be.visible')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -14,6 +14,18 @@ defineStep('I click on {string}', element => {
|
||||
'Moderation': 'a[href="/moderation"]',
|
||||
}
|
||||
|
||||
if (element === 'Moderation') {
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '/api',
|
||||
hostname: 'localhost',
|
||||
}, (req) => {
|
||||
if (req.body && req.body.query && req.body.query.includes('query ($orderBy: ReportOrdering')) {
|
||||
req.alias = 'reportsQuery'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cy.get(elementSelectors[element])
|
||||
.click()
|
||||
.wait(750)
|
||||
|
||||
@ -1,98 +1,91 @@
|
||||
<template>
|
||||
<div>
|
||||
<client-only>
|
||||
<vue-advanced-chat
|
||||
:theme="theme"
|
||||
:current-user-id="currentUser.id"
|
||||
:room-id="computedRoomId"
|
||||
:template-actions="JSON.stringify(templatesText)"
|
||||
:menu-actions="JSON.stringify(menuActions)"
|
||||
:text-messages="JSON.stringify(textMessages)"
|
||||
:message-actions="messageActions"
|
||||
:messages="JSON.stringify(messages)"
|
||||
:messages-loaded="messagesLoaded"
|
||||
:rooms="JSON.stringify(rooms)"
|
||||
:room-actions="JSON.stringify(roomActions)"
|
||||
:rooms-loaded="roomsLoaded"
|
||||
:loading-rooms="loadingRooms"
|
||||
show-files="true"
|
||||
show-audio="true"
|
||||
capture-files="true"
|
||||
:height="'calc(100dvh - 190px)'"
|
||||
:styles="JSON.stringify(computedChatStyle)"
|
||||
:show-footer="true"
|
||||
:responsive-breakpoint="responsiveBreakpoint"
|
||||
:single-room="singleRoom"
|
||||
show-reaction-emojis="false"
|
||||
@send-message="sendMessage($event.detail[0])"
|
||||
@fetch-messages="fetchMessages($event.detail[0])"
|
||||
@fetch-more-rooms="fetchRooms"
|
||||
@add-room="toggleUserSearch"
|
||||
@show-demo-options="showDemoOptions = $event"
|
||||
@open-user-tag="redirectToUserProfile($event.detail[0])"
|
||||
@open-file="openFile($event.detail[0].file.file)"
|
||||
>
|
||||
<div
|
||||
v-if="selectedRoom && selectedRoom.roomId"
|
||||
slot="room-options"
|
||||
class="chat-room-options"
|
||||
>
|
||||
<ds-flex v-if="singleRoom">
|
||||
<ds-flex-item centered class="single-chat-bubble">
|
||||
<nuxt-link :to="{ name: 'chat' }">
|
||||
<base-button icon="expand" size="small" circle />
|
||||
</nuxt-link>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item centered>
|
||||
<div class="vac-svg-button vac-room-options">
|
||||
<slot name="menu-icon">
|
||||
<base-button
|
||||
icon="close"
|
||||
size="small"
|
||||
circle
|
||||
@click="$emit('close-single-room', true)"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</div>
|
||||
|
||||
<div slot="room-header-avatar">
|
||||
<div
|
||||
v-if="selectedRoom && selectedRoom.avatar"
|
||||
class="vac-avatar"
|
||||
:style="{ 'background-image': `url('${selectedRoom.avatar}')` }"
|
||||
/>
|
||||
<div v-else-if="selectedRoom" class="vac-avatar">
|
||||
<span class="initials">{{ getInitialsName(selectedRoom.roomName) }}</span>
|
||||
<vue-advanced-chat
|
||||
:theme="theme"
|
||||
:current-user-id="currentUser.id"
|
||||
:room-id="computedRoomId"
|
||||
:template-actions="JSON.stringify(templatesText)"
|
||||
:menu-actions="JSON.stringify(menuActions)"
|
||||
:text-messages="JSON.stringify(textMessages)"
|
||||
:message-actions="messageActions"
|
||||
:messages="JSON.stringify(messages)"
|
||||
:messages-loaded="messagesLoaded"
|
||||
:rooms="JSON.stringify(rooms)"
|
||||
:room-actions="JSON.stringify(roomActions)"
|
||||
:rooms-loaded="roomsLoaded"
|
||||
:loading-rooms="loadingRooms"
|
||||
:media-preview-enabled="isSafari ? 'false' : 'true'"
|
||||
show-files="true"
|
||||
show-audio="true"
|
||||
capture-files="true"
|
||||
:height="'calc(100dvh - 190px)'"
|
||||
:styles="JSON.stringify(computedChatStyle)"
|
||||
:show-footer="true"
|
||||
:responsive-breakpoint="responsiveBreakpoint"
|
||||
:single-room="singleRoom"
|
||||
show-reaction-emojis="false"
|
||||
@send-message="sendMessage($event.detail[0])"
|
||||
@fetch-messages="fetchMessages($event.detail[0])"
|
||||
@fetch-more-rooms="fetchRooms"
|
||||
@add-room="toggleUserSearch"
|
||||
@show-demo-options="showDemoOptions = $event"
|
||||
@open-user-tag="redirectToUserProfile($event.detail[0])"
|
||||
@open-file="openFile($event.detail[0].file.file)"
|
||||
>
|
||||
<div v-if="selectedRoom && selectedRoom.roomId" slot="room-options" class="chat-room-options">
|
||||
<ds-flex v-if="singleRoom">
|
||||
<ds-flex-item centered class="single-chat-bubble">
|
||||
<nuxt-link :to="{ name: 'chat' }">
|
||||
<base-button icon="expand" size="small" circle />
|
||||
</nuxt-link>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item centered>
|
||||
<div class="vac-svg-button vac-room-options">
|
||||
<slot name="menu-icon">
|
||||
<base-button
|
||||
icon="close"
|
||||
size="small"
|
||||
circle
|
||||
@click="$emit('close-single-room', true)"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="message in messages.filter((m) => m.isUploading)"
|
||||
:slot="'message_' + message._id"
|
||||
v-bind:key="message._id"
|
||||
class="vac-format-message-wrapper"
|
||||
>
|
||||
<div class="markdown">
|
||||
<p>{{ $t('chat.transmitting') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="room-header-avatar">
|
||||
<div
|
||||
v-if="selectedRoom && selectedRoom.avatar"
|
||||
class="vac-avatar"
|
||||
:style="{ 'background-image': `url('${selectedRoom.avatar}')` }"
|
||||
/>
|
||||
<div v-else-if="selectedRoom" class="vac-avatar">
|
||||
<span class="initials">{{ getInitialsName(selectedRoom.roomName) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="room in rooms" :slot="'room-list-avatar_' + room.id" :key="room.id">
|
||||
<div
|
||||
v-if="room.avatar"
|
||||
class="vac-avatar"
|
||||
:style="{ 'background-image': `url('${room.avatar}')` }"
|
||||
/>
|
||||
<div v-else class="vac-avatar">
|
||||
<span class="initials">{{ getInitialsName(room.roomName) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</vue-advanced-chat>
|
||||
</client-only>
|
||||
</div>
|
||||
<div
|
||||
v-for="message in messages.filter((m) => m.isUploading)"
|
||||
:slot="'message_' + message._id"
|
||||
v-bind:key="message._id"
|
||||
class="vac-format-message-wrapper"
|
||||
>
|
||||
<div class="markdown">
|
||||
<p>{{ $t('chat.transmitting') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="room in rooms" :slot="'room-list-avatar_' + room.id" :key="room.id">
|
||||
<div
|
||||
v-if="room.avatar"
|
||||
class="vac-avatar"
|
||||
:style="{ 'background-image': `url('${room.avatar}')` }"
|
||||
/>
|
||||
<div v-else class="vac-avatar">
|
||||
<span class="initials">{{ getInitialsName(room.roomName) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</vue-advanced-chat>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -228,6 +221,9 @@ export default {
|
||||
|
||||
return roomId
|
||||
},
|
||||
isSafari() {
|
||||
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
},
|
||||
textMessages() {
|
||||
return {
|
||||
ROOMS_EMPTY: this.$t('chat.roomsEmpty'),
|
||||
@ -516,6 +512,11 @@ export default {
|
||||
|
||||
openFile: async function (file) {
|
||||
if (!file || !file.url) return
|
||||
|
||||
/* For videos, this function is called only on Safari.
|
||||
We don't want to download video files when clicking on them. */
|
||||
if (file.type.startsWith('video/')) return
|
||||
|
||||
/* To make the browser download the file instead of opening it, it needs to be
|
||||
from the same origin or from local blob storage. So we fetch it first
|
||||
and then create a download link from blob storage. */
|
||||
|
||||
@ -44,7 +44,10 @@
|
||||
</ds-chip>
|
||||
</div>
|
||||
<!-- group categories -->
|
||||
<div class="categories" v-if="categoriesActive && group.categories.length > 0">
|
||||
<div
|
||||
class="categories"
|
||||
v-if="categoriesActive && group.categories && group.categories.length > 0"
|
||||
>
|
||||
<category
|
||||
v-for="category in group.categories"
|
||||
:key="category.id"
|
||||
|
||||
@ -53,7 +53,10 @@
|
||||
class="footer"
|
||||
v-observe-visibility="(isVisible, entry) => visibilityChanged(isVisible, entry, post.id)"
|
||||
>
|
||||
<div class="categories" v-if="categoriesActive && post.categories.length > 0">
|
||||
<div
|
||||
class="categories"
|
||||
v-if="categoriesActive && post.categories && post.categories.length > 0"
|
||||
>
|
||||
<category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
|
||||
@ -107,19 +107,26 @@ export default {
|
||||
this.cities = []
|
||||
return
|
||||
}
|
||||
this.loadingGeo = true
|
||||
|
||||
const place = encodeURIComponent(value)
|
||||
const lang = this.$i18n.locale()
|
||||
try {
|
||||
this.loadingGeo = true
|
||||
|
||||
const {
|
||||
data: { queryLocations: result },
|
||||
} = await this.$apollo.query({ query: queryLocations(), variables: { place, lang } })
|
||||
const place = encodeURIComponent(value)
|
||||
const lang = this.$i18n.locale()
|
||||
|
||||
this.cities = this.processLocationsResult(result)
|
||||
this.loadingGeo = false
|
||||
const {
|
||||
data: { queryLocations: result },
|
||||
} = await this.$apollo.query({ query: queryLocations(), variables: { place, lang } })
|
||||
|
||||
return this.cities.find((city) => city.value === value)
|
||||
this.cities = this.processLocationsResult(result)
|
||||
this.loadingGeo = false
|
||||
|
||||
return this.cities.find((city) => city.value === value)
|
||||
} catch (error) {
|
||||
this.$toast.error(error.message)
|
||||
} finally {
|
||||
this.loadingGeo = false
|
||||
}
|
||||
},
|
||||
clearLocationName(event) {
|
||||
event.target.value = ''
|
||||
|
||||
@ -14,7 +14,9 @@
|
||||
<modal />
|
||||
</client-only>
|
||||
<div v-if="getShowChat.showChat" class="chat-modul">
|
||||
<chat singleRoom :roomId="getShowChat.roomID" @close-single-room="closeSingleRoom" />
|
||||
<client-only>
|
||||
<chat singleRoom :roomId="getShowChat.roomID" @close-single-room="closeSingleRoom" />
|
||||
</client-only>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -261,6 +261,10 @@ export default {
|
||||
},
|
||||
}
|
||||
|
||||
if (ctx.isClient) {
|
||||
config.devtool = 'source-map'
|
||||
}
|
||||
|
||||
config.resolve.alias['~@'] = path.resolve(__dirname, '/')
|
||||
config.resolve.alias['@@'] = path.resolve(__dirname, '/')
|
||||
|
||||
|
||||
@ -5,12 +5,14 @@
|
||||
@add-chat-room="addChatRoom"
|
||||
@close-user-search="showUserSearch = false"
|
||||
/>
|
||||
<chat
|
||||
:roomId="getShowChat.showChat ? getShowChat.roomID : null"
|
||||
ref="chat"
|
||||
@toggle-user-search="showUserSearch = !showUserSearch"
|
||||
:show-room="showRoom"
|
||||
/>
|
||||
<client-only>
|
||||
<chat
|
||||
:roomId="getShowChat.showChat ? getShowChat.roomID : null"
|
||||
ref="chat"
|
||||
@toggle-user-search="showUserSearch = !showUserSearch"
|
||||
:show-room="showRoom"
|
||||
/>
|
||||
</client-only>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user