diff --git a/config/index.js b/config/index.js
index 6652048c..c48d91b8 100644
--- a/config/index.js
+++ b/config/index.js
@@ -23,9 +23,15 @@ module.exports = {
assetsPublicPath: '/',
proxyTable: {
'/api': {
- target: 'https://social.heldscal.la/',
+ target: 'htts://localhost:4000/',
changeOrigin: true,
cookieDomainRewrite: 'localhost'
+ },
+ '/socket': {
+ target: 'htts://localhost:4000/',
+ changeOrigin: true,
+ cookieDomainRewrite: 'localhost',
+ ws: true
}
},
// CSS Sourcemaps off by default because relative paths are "buggy"
diff --git a/package.json b/package.json
index e8d84274..4e98647b 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"localforage": "^1.5.0",
"node-sass": "^3.10.1",
"object-path": "^0.11.3",
+ "phoenix": "^1.3.0",
"sanitize-html": "^1.13.0",
"sass-loader": "^4.0.2",
"vue": "^2.3.4",
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
new file mode 100644
index 00000000..ef326d4a
--- /dev/null
+++ b/src/components/chat/chat.js
@@ -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
diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue
new file mode 100644
index 00000000..6c1e2c38
--- /dev/null
+++ b/src/components/chat/chat.vue
@@ -0,0 +1,59 @@
+
+
+
+
+ {{$t('chat.title')}}
+
+
+
+
+
+
+
+ {{message.author.username}}:
+
+
+ {{message.text}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index baeaaede..ea5d7ea4 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -2,6 +2,9 @@ const NavPanel = {
computed: {
currentUser () {
return this.$store.state.users.currentUser
+ },
+ chat () {
+ return this.$store.state.chat.channel
}
}
}
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index aea841e9..ccc772a8 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -7,6 +7,11 @@
{{ $t("nav.timeline") }}
+
+
+ {{ $t("nav.chat") }}
+
+
{{ $t("nav.mentions") }}
diff --git a/src/i18n/messages.js b/src/i18n/messages.js
index 9aeffdfa..ecb3557c 100644
--- a/src/i18n/messages.js
+++ b/src/i18n/messages.js
@@ -179,7 +179,11 @@ const fi = {
}
const en = {
+ chat: {
+ title: 'Chat'
+ },
nav: {
+ chat: 'Local Chat',
timeline: 'Timeline',
mentions: 'Mentions',
public_tl: 'Public Timeline',
diff --git a/src/main.js b/src/main.js
index 6c5bf83e..a8ee5fda 100644
--- a/src/main.js
+++ b/src/main.js
@@ -12,11 +12,13 @@ import UserProfile from './components/user_profile/user_profile.vue'
import Settings from './components/settings/settings.vue'
import Registration from './components/registration/registration.vue'
import UserSettings from './components/user_settings/user_settings.vue'
+import Chat from './components/chat/chat.vue'
import statusesModule from './modules/statuses.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
+import chatModule from './modules/chat.js'
import VueTimeago from 'vue-timeago'
import VueI18n from 'vue-i18n'
@@ -57,10 +59,12 @@ const store = new Vuex.Store({
statuses: statusesModule,
users: usersModule,
api: apiModule,
- config: configModule
+ config: configModule,
+ chat: chatModule
},
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({
@@ -78,6 +82,9 @@ window.fetch('/static/config.json')
store.dispatch('setOption', { name: 'background', value: background })
store.dispatch('setOption', { name: 'logo', value: logo })
store.dispatch('setOption', { name: 'registrationOpen', value: registrationOpen })
+ if (data['chatDisabled']) {
+ store.dispatch('disableChat')
+ }
const routes = [
{ 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: 'settings', path: '/settings', component: Settings },
{ 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({
diff --git a/src/modules/api.js b/src/modules/api.js
index e61382eb..c91fb97b 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -1,10 +1,13 @@
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import {isArray} from 'lodash'
+import { Socket } from 'phoenix'
const api = {
state: {
backendInteractor: backendInteractorService(),
- fetchers: {}
+ fetchers: {},
+ socket: null,
+ chatDisabled: false
},
mutations: {
setBackendInteractor (state, backendInteractor) {
@@ -15,6 +18,12 @@ const api = {
},
removeFetcher (state, {timeline}) {
delete state.fetchers[timeline]
+ },
+ setSocket (state, socket) {
+ state.socket = socket
+ },
+ setChatDisabled (state, value) {
+ state.chatDisabled = value
}
},
actions: {
@@ -37,6 +46,17 @@ const api = {
const fetcher = store.state.fetchers[timeline]
window.clearInterval(fetcher)
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)
}
}
}
diff --git a/src/modules/chat.js b/src/modules/chat.js
new file mode 100644
index 00000000..b1244ebe
--- /dev/null
+++ b/src/modules/chat.js
@@ -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
diff --git a/src/modules/users.js b/src/modules/users.js
index 30f8dc27..8303ecc1 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -97,6 +97,10 @@ const users = {
// Set our new backend interactor
commit('setBackendInteractor', backendInteractorService(userCredentials))
+ if (user.token) {
+ store.dispatch('initializeSocket', user.token)
+ }
+
// Start getting fresh tweets.
store.dispatch('startFetching', 'friends')
diff --git a/static/config.json b/static/config.json
index b186246b..880efca8 100644
--- a/static/config.json
+++ b/static/config.json
@@ -4,5 +4,6 @@
"background": "/static/bg.jpg",
"logo": "/static/logo.png",
"registrationOpen": false,
- "defaultPath": "/main/all"
+ "defaultPath": "/main/all",
+ "chatDisabled": false
}
diff --git a/yarn.lock b/yarn.lock
index d0d2dde9..3fcd29ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4226,6 +4226,10 @@ phantomjs-prebuilt@^2.1.3, phantomjs-prebuilt@^2.1.7:
request-progress "~2.0.1"
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:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"