瀏覽代碼

:recycle: integrating chat view into pairs and reworking pubnub

tags/0.0.1^2
j 3 年之前
父節點
當前提交
81eced6e29

+ 6
- 6
backend/docker-compose.yml 查看文件

1
-version: "3"
1
+version: '3'
2
 
2
 
3
 services:
3
 services:
4
     siimee-db:
4
     siimee-db:
5
-        image: mariadb:latest
5
+        image: mariadb:10.7
6
         container_name: siimee-dev-mariadb
6
         container_name: siimee-dev-mariadb
7
         volumes:
7
         volumes:
8
-          - siimee_db:/var/lib/mysql
8
+            - siimee_db:/var/lib/mysql
9
         environment:
9
         environment:
10
-          MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
11
-          MYSQL_DATABASE: "${DB_NAME}"
10
+            MYSQL_ROOT_PASSWORD: '${DB_ROOT_PASSWORD}'
11
+            MYSQL_DATABASE: '${DB_NAME}'
12
         ports:
12
         ports:
13
-          - "${DB_PORT}:3306"
13
+            - '${DB_PORT}:3306'
14
 
14
 
15
 volumes:
15
 volumes:
16
     siimee_db:
16
     siimee_db:

+ 11
- 10
backend/knexfile.js 查看文件

1
-require('dotenv').config({path: './server/.env'})
2
-// require('dotenv').config()
1
+require('dotenv').config()
3
 
2
 
4
 const local = {
3
 const local = {
5
     host: process.env.DB_HOST,
4
     host: process.env.DB_HOST,
6
     user: process.env.DB_USER,
5
     user: process.env.DB_USER,
7
     password: process.env.DB_ROOT_PASSWORD,
6
     password: process.env.DB_ROOT_PASSWORD,
8
     database: process.env.DB_NAME,
7
     database: process.env.DB_NAME,
9
-    port: process.env.DB_PORT
8
+    port: process.env.DB_PORT,
10
 }
9
 }
11
 const pscale = {
10
 const pscale = {
12
     host: process.env.PSCALE_DB_HOST ? process.env.PSCALE_DB_HOST : '127.0.0.1',
11
     host: process.env.PSCALE_DB_HOST ? process.env.PSCALE_DB_HOST : '127.0.0.1',
13
     user: process.env.PSCALE_DB_USER ? process.env.PSCALE_DB_USER : 'root',
12
     user: process.env.PSCALE_DB_USER ? process.env.PSCALE_DB_USER : 'root',
14
-    password: process.env.PSCALE_DB_PASSWORD ? process.env.PSCALE_DB_PASSWORD : '', 
13
+    password: process.env.PSCALE_DB_PASSWORD
14
+        ? process.env.PSCALE_DB_PASSWORD
15
+        : '',
15
     database: process.env.PSCALE_DB_NAME,
16
     database: process.env.PSCALE_DB_NAME,
16
-    port: process.env.PSCALE_DB_PORT ? process.env.PSCALE_DB_PORT : 3306
17
+    port: process.env.PSCALE_DB_PORT ? process.env.PSCALE_DB_PORT : 3306,
17
 }
18
 }
18
 
19
 
19
-const connectionSettings = process.env.USE_LOCAL_DB == "true" ? local : pscale
20
+const connectionSettings = process.env.USE_LOCAL_DB == 'true' ? local : pscale
20
 
21
 
21
 module.exports = {
22
 module.exports = {
22
     development: {
23
     development: {
24
         connection: connectionSettings,
25
         connection: connectionSettings,
25
         pool: {
26
         pool: {
26
             min: 2,
27
             min: 2,
27
-            max: 10
28
+            max: 10,
28
         },
29
         },
29
         migrations: {
30
         migrations: {
30
-            directory: './db/migrations'
31
+            directory: './db/migrations',
31
         },
32
         },
32
         seeds: {
33
         seeds: {
33
-            directory: './db/seeds'
34
-        }
34
+            directory: './db/seeds',
35
+        },
35
     },
36
     },
36
 }
37
 }

+ 16
- 10
backend/package-lock.json 查看文件

2057
             }
2057
             }
2058
         },
2058
         },
2059
         "node_modules/caniuse-lite": {
2059
         "node_modules/caniuse-lite": {
2060
-            "version": "1.0.30001312",
2061
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
2062
-            "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==",
2060
+            "version": "1.0.30001423",
2061
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001423.tgz",
2062
+            "integrity": "sha512-09iwWGOlifvE1XuHokFMP7eR38a0JnajoyL3/i87c8ZjRWRrdKo1fqjNfugfBD0UDBIOz0U+jtNhJ0EPm1VleQ==",
2063
             "dev": true,
2063
             "dev": true,
2064
-            "funding": {
2065
-                "type": "opencollective",
2066
-                "url": "https://opencollective.com/browserslist"
2067
-            }
2064
+            "funding": [
2065
+                {
2066
+                    "type": "opencollective",
2067
+                    "url": "https://opencollective.com/browserslist"
2068
+                },
2069
+                {
2070
+                    "type": "tidelift",
2071
+                    "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
2072
+                }
2073
+            ]
2068
         },
2074
         },
2069
         "node_modules/chalk": {
2075
         "node_modules/chalk": {
2070
             "version": "3.0.0",
2076
             "version": "3.0.0",
9889
             "dev": true
9895
             "dev": true
9890
         },
9896
         },
9891
         "caniuse-lite": {
9897
         "caniuse-lite": {
9892
-            "version": "1.0.30001418",
9893
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
9894
-            "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==",
9898
+            "version": "1.0.30001423",
9899
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001423.tgz",
9900
+            "integrity": "sha512-09iwWGOlifvE1XuHokFMP7eR38a0JnajoyL3/i87c8ZjRWRrdKo1fqjNfugfBD0UDBIOz0U+jtNhJ0EPm1VleQ==",
9895
             "dev": true
9901
             "dev": true
9896
         },
9902
         },
9897
         "chalk": {
9903
         "chalk": {

+ 8
- 1
frontend/src/components/PairsList.vue 查看文件

2
 section.pairs-list
2
 section.pairs-list
3
     article(v-if='pairs.length')
3
     article(v-if='pairs.length')
4
         template(v-for='pair in pairs')
4
         template(v-for='pair in pairs')
5
-            router-link.pair.w-flex.align-center.justify-space-around(:to='`/profile/${pair.profile.pid}`')
5
+            router-link.pair.w-flex.align-center.justify-space-around(
6
+                :to='`/profile/${pair.profile.pid}`'
7
+                v-if='tabName == "pending"'
8
+            )
9
+            router-link.pair.w-flex.align-center.justify-space-around(
10
+                :to='`/chat/${pair.profile.pid}`'
11
+                v-else
12
+            )
6
                 .dot--icon
13
                 .dot--icon
7
                 .avatar
14
                 .avatar
8
                 .idCard
15
                 .idCard

+ 36
- 0
frontend/src/entities/chat-message/chat-message.js 查看文件

1
+/** @module entities/chat-message */
2
+
3
+import { chatMessageSchema } from './chat-message.schema.js'
4
+
5
+/** Class representing a grouping */
6
+class ChatMessage {
7
+    /**
8
+     * Create the chatMessage for pubnub
9
+     * @param {string} message you message
10
+     * @return {ChatMessage} the message instance object
11
+     */
12
+    constructor(message) {
13
+        this.description = message
14
+        this.type = this.constructor.name.toLowerCase()
15
+    }
16
+    /**
17
+     * validate this record
18
+     * @return {boolean} is it valid or not?
19
+     */
20
+    isValid() {
21
+        const validate = chatMessageSchema.validate(this)
22
+
23
+        /**
24
+         * Log out some useful error messages
25
+         * TODO: Send validate.error to logging error handler
26
+         */
27
+        if (validate.error) {
28
+            console.error(`error: ${validate.error} - ${this.type} validation`)
29
+        }
30
+
31
+        /** validate(this) always returns something so force it to a bool */
32
+        return !validate.error ? true : false
33
+    }
34
+}
35
+
36
+export { ChatMessage }

+ 21
- 0
frontend/src/entities/chat-message/chat-message.schema.js 查看文件

1
+/** @module entities/chatMessageSchema */
2
+import Joi from 'joi'
3
+
4
+/**
5
+ * chat message schema object
6
+ * for pubnub messages
7
+ * @constructor
8
+ */
9
+const chatMessageSchema = {
10
+    type: 'object',
11
+    properties: Joi.object().keys({
12
+        description: Joi.string(),
13
+    }),
14
+    /** fields required before saving */
15
+    required: ['description'],
16
+    validate(instance) {
17
+        return this.properties.validate(instance)
18
+    },
19
+}
20
+
21
+export { chatMessageSchema }

+ 1
- 0
frontend/src/entities/chat-message/index.js 查看文件

1
+export * from './chat-message.js'

+ 1
- 0
frontend/src/entities/index.js 查看文件

6
 export * from './survey/index.js'
6
 export * from './survey/index.js'
7
 export * from './grouping/index.js'
7
 export * from './grouping/index.js'
8
 export * from './card/index.js'
8
 export * from './card/index.js'
9
+export * from './chat-message/index.js'

+ 32
- 28
frontend/src/services/chat.service.js 查看文件

1
 import PubNub from 'pubnub'
1
 import PubNub from 'pubnub'
2
 
2
 
3
+import { ChatMessage } from '../entities'
4
+
5
+const MAIN_CHANNEL = 'Channel-Siimee'
6
+
3
 /**
7
 /**
4
  * Provider method holder
8
  * Provider method holder
5
  * We always reference this object so
9
  * We always reference this object so
8
  *
12
  *
9
  * This gets overloaded later in the program
13
  * This gets overloaded later in the program
10
  */
14
  */
11
-const providerMethods = {
15
+const _providerMethods = {
12
     publish: () => console.error('no provider publish method set'),
16
     publish: () => console.error('no provider publish method set'),
13
     subscribe: () => console.error('no provider subscribe method set'),
17
     subscribe: () => console.error('no provider subscribe method set'),
14
     listen: () => console.error('no provider listen method set'),
18
     listen: () => console.error('no provider listen method set'),
15
 }
19
 }
20
+
16
 /**
21
 /**
17
- *
18
- *
19
  * Breaking out as much pubnub specific flavor
22
  * Breaking out as much pubnub specific flavor
20
  */
23
  */
21
-const setupPubnub = async uuid => {
24
+const _setupPubnub = async uuid => {
22
     const publishKey = import.meta.env.VITE_PUBNUB_PUBLISH_KEY
25
     const publishKey = import.meta.env.VITE_PUBNUB_PUBLISH_KEY
23
     const subscribeKey = import.meta.env.VITE_PUBNUB_SUBSCRIBE_KEY
26
     const subscribeKey = import.meta.env.VITE_PUBNUB_SUBSCRIBE_KEY
24
 
27
 
31
         uuid,
34
         uuid,
32
     })
35
     })
33
     // Pass pubnub specific methods to our placeholder obj
36
     // Pass pubnub specific methods to our placeholder obj
34
-    providerMethods['publish'] = pubnubClient.publish
35
-    providerMethods['subscribe'] = pubnubClient.subscribe
36
-    providerMethods['listen'] = pubnubClient.addListener
37
+    _providerMethods['publish'] = pubnubClient.publish
38
+    _providerMethods['subscribe'] = pubnubClient.subscribe
39
+    _providerMethods['listen'] = pubnubClient.addListener
37
 
40
 
38
     return pubnubClient
41
     return pubnubClient
39
 }
42
 }
40
 
43
 
41
-class ChatMessage {
42
-    constructor(message) {
43
-        this.description = message
44
-    }
45
-}
46
-const MAIN_CHANNEL = 'Channel-Siimee'
47
-
48
 /** Singleton that holds all our chat information */
44
 /** Singleton that holds all our chat information */
49
 class Chatter {
45
 class Chatter {
50
     /**
46
     /**
54
     constructor() {
50
     constructor() {
55
         // Our pubnub instance
51
         // Our pubnub instance
56
         this.provider = null
52
         this.provider = null
57
-
58
-        // UUID used to identify unique users
59
         this.uuid = null
53
         this.uuid = null
60
-
61
-        // Setup the main channel
62
-        //  subscriptions array will be built dynamically from the "this.groupings" object
63
-        this.subscriptions = [MAIN_CHANNEL]
54
+        this._subscriptions = [MAIN_CHANNEL]
64
 
55
 
65
         this.listeners = {
56
         this.listeners = {
66
             status: async e => {
57
             status: async e => {
70
             presence: this._onPresence,
61
             presence: this._onPresence,
71
         }
62
         }
72
     }
63
     }
64
+    get subscriptions() {
65
+        let truncated = this._subscriptions
66
+
67
+        // Do NOT include the main channel as part of subscriptions
68
+        const mainChanIndex = truncated.indexOf(MAIN_CHANNEL)
69
+        if (mainChanIndex > -1) {
70
+            truncated.splice(mainChanIndex, 1)
71
+        }
72
+
73
+        return truncated
74
+    }
73
     async _onPresence(e) {
75
     async _onPresence(e) {
76
+        console.log('presence :', e)
74
         return
77
         return
75
     }
78
     }
76
     setOnMessage(cb) {
79
     setOnMessage(cb) {
79
 
82
 
80
     async setup(uuid, groupings) {
83
     async setup(uuid, groupings) {
81
         this.uuid = `${uuid}`
84
         this.uuid = `${uuid}`
82
-        this.provider = await setupPubnub(this.uuid)
85
+        this.provider = await _setupPubnub(this.uuid)
83
 
86
 
84
         //  step_1: build the this.groupings object from the backend
87
         //  step_1: build the this.groupings object from the backend
85
         //  await for the groupings to be fetched before subscribing to channels
88
         //  await for the groupings to be fetched before subscribing to channels
86
         await this._setupAllChannels(groupings)
89
         await this._setupAllChannels(groupings)
87
         this._listenFor({ listeners: this.listeners })
90
         this._listenFor({ listeners: this.listeners })
88
-        this.subscribe({ channels: this.subscriptions })
91
+        this.subscribe({ channels: this._subscriptions })
89
         return this.subscriptions
92
         return this.subscriptions
90
     }
93
     }
91
     /**
94
     /**
97
      * @return {object} timestamp
100
      * @return {object} timestamp
98
      */
101
      */
99
     async publish(channel, message) {
102
     async publish(channel, message) {
100
-        return providerMethods.publish({
103
+        return _providerMethods.publish({
101
             channel,
104
             channel,
102
             message: new ChatMessage(message),
105
             message: new ChatMessage(message),
103
         })
106
         })
108
      * @param {array} channels
111
      * @param {array} channels
109
      */
112
      */
110
     subscribe({ channels }) {
113
     subscribe({ channels }) {
111
-        providerMethods.subscribe({ channels })
114
+        console.log('subscribing to:', channels)
115
+        _providerMethods.subscribe({ channels })
112
     }
116
     }
113
     /**
117
     /**
114
      * Listen to events and set callbacks
118
      * Listen to events and set callbacks
115
      * Facade so we can hide provider specific methods
119
      * Facade so we can hide provider specific methods
116
      */
120
      */
117
     _listenFor({ listeners }) {
121
     _listenFor({ listeners }) {
118
-        providerMethods.listen(listeners)
122
+        _providerMethods.listen(listeners)
119
     }
123
     }
120
     //  step 2: build the this.subscriptions array from the this.groupings object
124
     //  step 2: build the this.subscriptions array from the this.groupings object
121
     // fetch all groupings for this profile and then store them in the chatter groupings object for reference
125
     // fetch all groupings for this profile and then store them in the chatter groupings object for reference
122
     async _setupAllChannels(groupings) {
126
     async _setupAllChannels(groupings) {
123
         groupings.forEach(grouping => {
127
         groupings.forEach(grouping => {
124
-            this.subscriptions.push(grouping.grouping_name)
128
+            this._subscriptions.push(grouping.grouping_name)
125
         })
129
         })
126
     }
130
     }
127
     stop() {
131
     stop() {
128
-        this.subscriptions = [MAIN_CHANNEL]
132
+        this._subscriptions = [MAIN_CHANNEL]
129
         console.warn('chatter stop not implemented')
133
         console.warn('chatter stop not implemented')
130
     }
134
     }
131
 }
135
 }
132
 
136
 
133
-export { Chatter }
137
+export { Chatter, MAIN_CHANNEL }

+ 2
- 2
frontend/src/services/login.service.js 查看文件

83
 
83
 
84
         this._loading.value = false
84
         this._loading.value = false
85
         console.warn('logged in:', this.isLoggedIn)
85
         console.warn('logged in:', this.isLoggedIn)
86
+        console.warn('subscribed to:', this.chatter.subscriptions)
86
         return this.id.value
87
         return this.id.value
87
     }
88
     }
88
     logout() {
89
     logout() {
136
     async setupChatter() {
137
     async setupChatter() {
137
         this.chatter = new Chatter()
138
         this.chatter = new Chatter()
138
         // Use the reactive id object
139
         // Use the reactive id object
139
-        const testAccountUUID = this.id.value
140
-        await this.chatter.setup(testAccountUUID, this.groupings)
140
+        await this.chatter.setup(this.id.value, this.groupings)
141
     }
141
     }
142
 }
142
 }
143
 
143
 

+ 27
- 22
frontend/src/views/ChatView.vue 查看文件

1
 <template lang="pug">
1
 <template lang="pug">
2
 main.view--chat
2
 main.view--chat
3
-    header 
4
-        h2 Chat Page
5
- 
6
-    article(v-if="profile.isLoggedIn && target")
7
-        h3 {{ profile.id }} 
8
-            span chatting with: {{ target.profile_id }}
3
+    header(v-if='profile')
4
+        p {{ profile.chatter }}
5
+        hr
6
+        p messages: {{ messages }}
7
+        hr
9
         p subscriptions: {{ profile.chatter.subscriptions }}
8
         p subscriptions: {{ profile.chatter.subscriptions }}
10
 
9
 
11
-        ul(id="messages").w-flex.column
12
-            li(v-for="message, i in messages" class="pa1")
13
-                w-card.w-flex.row
14
-                    article
15
-                        p {{ message.message }}
16
-                    footer
17
-                        p {{ message.publisher }} | {{ message.timetoken }}
18
-                        w-button(class="my12" @click="openDrawer = i" text) setting
19
-                    w-drawer(v-model="openDrawer" absolute width="160px")
20
-                        p some settings things
10
+    article(v-if='profile.isLoggedIn && target')
11
+        h3 {{ profile.id }}&nbsp;
12
+            span chatting with: {{ target.profile_id }}
13
+
14
+        ul#messages.w-flex.column
15
+            //- li.pa1(v-for='(message, i in messages)')
16
+            //-     w-card.w-flex.row
17
+            //-         article
18
+                //- p {{ message.message }}
19
+        //-             footer
20
+        //-                 p {{ message.publisher }} | {{ message.timetoken }}
21
+        //-                 w-button.my12(@click='openDrawer = i' text) setting
22
+        //-             w-drawer(absolute v-model='openDrawer' width='160px')
23
+        //-                 p some settings things
21
 
24
 
22
-        input(v-model="toSend" @keyup.enter="sendMessage")
23
-        button(@click="sendMessage") send
25
+        input(@keyup.enter='sendMessage' v-model='toSend')
26
+        button(@click='sendMessage') send
24
 
27
 
25
-    p(v-else-if="profile.isLoggedIn && !target") No match found between profile {{ $route.params.pid }} and {{profile.id}}... 
26
-    w-spinner(v-else bounce)
28
+    p(v-else-if='profile.isLoggedIn && !target') No match found between profile {{ $route.params.pid }} and {{ profile.id }}...
29
+    w-spinner(bounce v-else)
27
 
30
 
28
     MainNav
31
     MainNav
29
 </template>
32
 </template>
62
             if (!e.message) return
65
             if (!e.message) return
63
             this.messages.push(e)
66
             this.messages.push(e)
64
         },
67
         },
68
+        // TODO: test this
65
         getGrouping() {
69
         getGrouping() {
66
             return currentProfile.groupings.find(
70
             return currentProfile.groupings.find(
67
-                g => g.profile.profile_id == this.$route.params.tid,
71
+                g => g.profile.profile_id == this.$route.params.pid,
68
             )
72
             )
69
         },
73
         },
70
         sendMessage() {
74
         sendMessage() {
72
             currentProfile.chatter.publish(grouping.grouping_name, this.toSend)
76
             currentProfile.chatter.publish(grouping.grouping_name, this.toSend)
73
             this.toSend = ''
77
             this.toSend = ''
74
         },
78
         },
79
+        // TODO: test this
75
         loadTargetProfile() {
80
         loadTargetProfile() {
76
             try {
81
             try {
77
                 const grouping = this.getGrouping()
82
                 const grouping = this.getGrouping()
78
                 if (!grouping) {
83
                 if (!grouping) {
79
                     throw `no match found for ${currentProfile.id.value}`
84
                     throw `no match found for ${currentProfile.id.value}`
80
                 }
85
                 }
81
-
86
+                console.log('grouping :', grouping)
82
                 this.target = grouping.profile
87
                 this.target = grouping.profile
83
             } catch (err) {
88
             } catch (err) {
84
                 console.error(err)
89
                 console.error(err)

Loading…
取消
儲存