forked from AkkomaGang/akkoma-fe
Merge branch 'feature/chat' into 'develop'
Feature/chat See merge request pleroma/pleroma-fe!182
This commit is contained in:
commit
63d29eab3f
13 changed files with 175 additions and 6 deletions
|
@ -23,9 +23,15 @@ module.exports = {
|
||||||
assetsPublicPath: '/',
|
assetsPublicPath: '/',
|
||||||
proxyTable: {
|
proxyTable: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'https://social.heldscal.la/',
|
target: 'htts://localhost:4000/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
cookieDomainRewrite: 'localhost'
|
cookieDomainRewrite: 'localhost'
|
||||||
|
},
|
||||||
|
'/socket': {
|
||||||
|
target: 'htts://localhost:4000/',
|
||||||
|
changeOrigin: true,
|
||||||
|
cookieDomainRewrite: 'localhost',
|
||||||
|
ws: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"localforage": "^1.5.0",
|
"localforage": "^1.5.0",
|
||||||
"node-sass": "^3.10.1",
|
"node-sass": "^3.10.1",
|
||||||
"object-path": "^0.11.3",
|
"object-path": "^0.11.3",
|
||||||
|
"phoenix": "^1.3.0",
|
||||||
"sanitize-html": "^1.13.0",
|
"sanitize-html": "^1.13.0",
|
||||||
"sass-loader": "^4.0.2",
|
"sass-loader": "^4.0.2",
|
||||||
"vue": "^2.3.4",
|
"vue": "^2.3.4",
|
||||||
|
|
21
src/components/chat/chat.js
Normal file
21
src/components/chat/chat.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
const chat = {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
currentMessage: '',
|
||||||
|
channel: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
messages () {
|
||||||
|
return this.$store.state.chat.messages
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit (message) {
|
||||||
|
this.$store.state.chat.channel.push('new_msg', {text: message}, 10000)
|
||||||
|
this.currentMessage = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default chat
|
59
src/components/chat/chat.vue
Normal file
59
src/components/chat/chat.vue
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<div class="chat-panel panel panel-default">
|
||||||
|
<div class="panel-heading timeline-heading base02-background base04">
|
||||||
|
<div class="title">
|
||||||
|
{{$t('chat.title')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body base01-background">
|
||||||
|
<div class="chat-window">
|
||||||
|
<div class="chat-message" v-for="message in messages" :key="message.id">
|
||||||
|
<span class="chat-avatar">
|
||||||
|
<img :src="message.author.avatar" />
|
||||||
|
{{message.author.username}}:
|
||||||
|
</span>
|
||||||
|
<span class="chat-text">
|
||||||
|
{{message.text}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chat-input">
|
||||||
|
<form @submit.prevent="submit(currentMessage)">
|
||||||
|
<input v-model="currentMessage" type="text" >
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./chat.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.chat-window {
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.chat-message {
|
||||||
|
padding: 0.2em 0.5em
|
||||||
|
}
|
||||||
|
.chat-avatar {
|
||||||
|
img {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chat-input {
|
||||||
|
display: flex;
|
||||||
|
form {
|
||||||
|
flex: auto;
|
||||||
|
input {
|
||||||
|
margin: 0.5em;
|
||||||
|
width: fill-available;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,6 +2,9 @@ const NavPanel = {
|
||||||
computed: {
|
computed: {
|
||||||
currentUser () {
|
currentUser () {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
chat () {
|
||||||
|
return this.$store.state.chat.channel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
{{ $t("nav.timeline") }}
|
{{ $t("nav.timeline") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if='chat && currentUser'>
|
||||||
|
<router-link class="base00-background" to='/chat'>
|
||||||
|
{{ $t("nav.chat") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
<li v-if='currentUser'>
|
<li v-if='currentUser'>
|
||||||
<router-link class="base00-background" :to="{ name: 'mentions', params: { username: currentUser.screen_name } }">
|
<router-link class="base00-background" :to="{ name: 'mentions', params: { username: currentUser.screen_name } }">
|
||||||
{{ $t("nav.mentions") }}
|
{{ $t("nav.mentions") }}
|
||||||
|
|
|
@ -179,7 +179,11 @@ const fi = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const en = {
|
const en = {
|
||||||
|
chat: {
|
||||||
|
title: 'Chat'
|
||||||
|
},
|
||||||
nav: {
|
nav: {
|
||||||
|
chat: 'Local Chat',
|
||||||
timeline: 'Timeline',
|
timeline: 'Timeline',
|
||||||
mentions: 'Mentions',
|
mentions: 'Mentions',
|
||||||
public_tl: 'Public Timeline',
|
public_tl: 'Public Timeline',
|
||||||
|
|
14
src/main.js
14
src/main.js
|
@ -12,11 +12,13 @@ import UserProfile from './components/user_profile/user_profile.vue'
|
||||||
import Settings from './components/settings/settings.vue'
|
import Settings from './components/settings/settings.vue'
|
||||||
import Registration from './components/registration/registration.vue'
|
import Registration from './components/registration/registration.vue'
|
||||||
import UserSettings from './components/user_settings/user_settings.vue'
|
import UserSettings from './components/user_settings/user_settings.vue'
|
||||||
|
import Chat from './components/chat/chat.vue'
|
||||||
|
|
||||||
import statusesModule from './modules/statuses.js'
|
import statusesModule from './modules/statuses.js'
|
||||||
import usersModule from './modules/users.js'
|
import usersModule from './modules/users.js'
|
||||||
import apiModule from './modules/api.js'
|
import apiModule from './modules/api.js'
|
||||||
import configModule from './modules/config.js'
|
import configModule from './modules/config.js'
|
||||||
|
import chatModule from './modules/chat.js'
|
||||||
|
|
||||||
import VueTimeago from 'vue-timeago'
|
import VueTimeago from 'vue-timeago'
|
||||||
import VueI18n from 'vue-i18n'
|
import VueI18n from 'vue-i18n'
|
||||||
|
@ -57,10 +59,12 @@ const store = new Vuex.Store({
|
||||||
statuses: statusesModule,
|
statuses: statusesModule,
|
||||||
users: usersModule,
|
users: usersModule,
|
||||||
api: apiModule,
|
api: apiModule,
|
||||||
config: configModule
|
config: configModule,
|
||||||
|
chat: chatModule
|
||||||
},
|
},
|
||||||
plugins: [createPersistedState(persistedStateOptions)],
|
plugins: [createPersistedState(persistedStateOptions)],
|
||||||
strict: process.env.NODE_ENV !== 'production'
|
strict: false // Socket modifies itself, let's ignore this for now.
|
||||||
|
// strict: process.env.NODE_ENV !== 'production'
|
||||||
})
|
})
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
|
@ -78,6 +82,9 @@ window.fetch('/static/config.json')
|
||||||
store.dispatch('setOption', { name: 'background', value: background })
|
store.dispatch('setOption', { name: 'background', value: background })
|
||||||
store.dispatch('setOption', { name: 'logo', value: logo })
|
store.dispatch('setOption', { name: 'logo', value: logo })
|
||||||
store.dispatch('setOption', { name: 'registrationOpen', value: registrationOpen })
|
store.dispatch('setOption', { name: 'registrationOpen', value: registrationOpen })
|
||||||
|
if (data['chatDisabled']) {
|
||||||
|
store.dispatch('disableChat')
|
||||||
|
}
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ name: 'root', path: '/', redirect: data['defaultPath'] || '/main/all' },
|
{ name: 'root', path: '/', redirect: data['defaultPath'] || '/main/all' },
|
||||||
|
@ -90,7 +97,8 @@ window.fetch('/static/config.json')
|
||||||
{ name: 'mentions', path: '/:username/mentions', component: Mentions },
|
{ name: 'mentions', path: '/:username/mentions', component: Mentions },
|
||||||
{ name: 'settings', path: '/settings', component: Settings },
|
{ name: 'settings', path: '/settings', component: Settings },
|
||||||
{ name: 'registration', path: '/registration', component: Registration },
|
{ name: 'registration', path: '/registration', component: Registration },
|
||||||
{ name: 'user-settings', path: '/user-settings', component: UserSettings }
|
{ name: 'user-settings', path: '/user-settings', component: UserSettings },
|
||||||
|
{ name: 'chat', path: '/chat', component: Chat }
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
||||||
import {isArray} from 'lodash'
|
import {isArray} from 'lodash'
|
||||||
|
import { Socket } from 'phoenix'
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
state: {
|
state: {
|
||||||
backendInteractor: backendInteractorService(),
|
backendInteractor: backendInteractorService(),
|
||||||
fetchers: {}
|
fetchers: {},
|
||||||
|
socket: null,
|
||||||
|
chatDisabled: false
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setBackendInteractor (state, backendInteractor) {
|
setBackendInteractor (state, backendInteractor) {
|
||||||
|
@ -15,6 +18,12 @@ const api = {
|
||||||
},
|
},
|
||||||
removeFetcher (state, {timeline}) {
|
removeFetcher (state, {timeline}) {
|
||||||
delete state.fetchers[timeline]
|
delete state.fetchers[timeline]
|
||||||
|
},
|
||||||
|
setSocket (state, socket) {
|
||||||
|
state.socket = socket
|
||||||
|
},
|
||||||
|
setChatDisabled (state, value) {
|
||||||
|
state.chatDisabled = value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -37,6 +46,17 @@ const api = {
|
||||||
const fetcher = store.state.fetchers[timeline]
|
const fetcher = store.state.fetchers[timeline]
|
||||||
window.clearInterval(fetcher)
|
window.clearInterval(fetcher)
|
||||||
store.commit('removeFetcher', {timeline})
|
store.commit('removeFetcher', {timeline})
|
||||||
|
},
|
||||||
|
initializeSocket (store, token) {
|
||||||
|
// Set up websocket connection
|
||||||
|
if (!store.state.chatDisabled) {
|
||||||
|
let socket = new Socket('/socket', {params: {token: token}})
|
||||||
|
socket.connect()
|
||||||
|
store.dispatch('initializeChat', socket)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disableChat (store) {
|
||||||
|
store.commit('setChatDisabled', true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/modules/chat.js
Normal file
33
src/modules/chat.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
const chat = {
|
||||||
|
state: {
|
||||||
|
messages: [],
|
||||||
|
channel: null
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setChannel (state, channel) {
|
||||||
|
state.channel = channel
|
||||||
|
},
|
||||||
|
addMessage (state, message) {
|
||||||
|
state.messages.push(message)
|
||||||
|
state.messages = state.messages.slice(-19, 20)
|
||||||
|
},
|
||||||
|
setMessages (state, messages) {
|
||||||
|
state.messages = messages.slice(-19, 20)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
initializeChat (store, socket) {
|
||||||
|
const channel = socket.channel('chat:public')
|
||||||
|
channel.on('new_msg', (msg) => {
|
||||||
|
store.commit('addMessage', msg)
|
||||||
|
})
|
||||||
|
channel.on('messages', ({messages}) => {
|
||||||
|
store.commit('setMessages', messages)
|
||||||
|
})
|
||||||
|
channel.join()
|
||||||
|
store.commit('setChannel', channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default chat
|
|
@ -97,6 +97,10 @@ const users = {
|
||||||
// Set our new backend interactor
|
// Set our new backend interactor
|
||||||
commit('setBackendInteractor', backendInteractorService(userCredentials))
|
commit('setBackendInteractor', backendInteractorService(userCredentials))
|
||||||
|
|
||||||
|
if (user.token) {
|
||||||
|
store.dispatch('initializeSocket', user.token)
|
||||||
|
}
|
||||||
|
|
||||||
// Start getting fresh tweets.
|
// Start getting fresh tweets.
|
||||||
store.dispatch('startFetching', 'friends')
|
store.dispatch('startFetching', 'friends')
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
"background": "/static/bg.jpg",
|
"background": "/static/bg.jpg",
|
||||||
"logo": "/static/logo.png",
|
"logo": "/static/logo.png",
|
||||||
"registrationOpen": false,
|
"registrationOpen": false,
|
||||||
"defaultPath": "/main/all"
|
"defaultPath": "/main/all",
|
||||||
|
"chatDisabled": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -4226,6 +4226,10 @@ phantomjs-prebuilt@^2.1.3, phantomjs-prebuilt@^2.1.7:
|
||||||
request-progress "~2.0.1"
|
request-progress "~2.0.1"
|
||||||
which "~1.2.10"
|
which "~1.2.10"
|
||||||
|
|
||||||
|
phoenix@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.3.0.tgz#1df2c27f986ee295e37c9983ec28ebac1d7f4a3e"
|
||||||
|
|
||||||
pify@^2.0.0:
|
pify@^2.0.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||||
|
|
Loading…
Reference in a new issue