forked from sabrinahu618/chat-lab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chat.js
193 lines (168 loc) · 5.17 KB
/
chat.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import * as Vue from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
import { mixin } from "https://mavue.mavo.io/mavue.js";
import GraffitiPlugin from 'https://graffiti.garden/graffiti-js/plugins/vue/plugin.js'
import Resolver from './resolver.js'
const app = {
// Import MaVue
mixins: [mixin],
// Import resolver
created() {
this.resolver = new Resolver(this.$gf)
},
setup() {
// Initialize the name of the channel we're chatting in
const channel = Vue.ref('default')
// And a flag for whether or not we're private-messaging
const privateMessaging = Vue.ref(false)
// If we're private messaging use "me" as the channel,
// otherwise use the channel value
const $gf = Vue.inject('graffiti')
const context = Vue.computed(()=> privateMessaging.value? [$gf.me] : [channel.value])
// Initialize the collection of messages associated with the context
const { objects: messagesRaw } = $gf.useObjects(context)
return { channel, privateMessaging, messagesRaw }
},
data() {
// Initialize some more reactive variables
return {
messageText: '',
editID: '',
editText: '',
recipient: ''
}
},
computed: {
messages() {
let messages = this.messagesRaw
// Filter the "raw" messages for data
// that is appropriate for our application
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note
.filter(m=>
// Does the message have a type property?
m.type &&
// Is the value of that property 'Note'?
m.type=='Note' &&
// Does the message have a content property?
m.content &&
// Is that property a string?
typeof m.content=='string')
// Do some more filtering for private messaging
if (this.privateMessaging) {
messages = messages.filter(m=>
// Is the message private?
m.bto &&
// Is the message to exactly one person?
m.bto.length == 1 &&
(
// Is the message to the recipient?
m.bto[0] == this.recipient ||
// Or is the message from the recipient?
m.actor == this.recipient
))
}
return messages
// Sort the messages with the
// most recently created ones first
.sort((m1, m2)=> new Date(m2.published) - new Date(m1.published))
// Only show the 10 most recent ones
.slice(0,10)
},
},
methods: {
sendMessage() {
const message = {
type: 'Note',
content: this.messageText,
}
// The context field declares which
// channel(s) the object is posted in
// You can post in more than one if you want!
// The bto field makes messages private
if (this.privateMessaging) {
message.bto = [this.recipient]
message.context = [this.$gf.me, this.recipient]
} else {
message.context = [this.channel]
}
// Send!
this.$gf.post(message)
},
removeMessage(message) {
this.$gf.remove(message)
},
startEditMessage(message) {
// Mark which message we're editing
this.editID = message.id
// And copy over it's existing text
this.editText = message.content
},
saveEditMessage(message) {
// Save the text (which will automatically
// sync with the server)
message.content = this.editText
// And clear the edit mark
this.editID = ''
}
}
}
const Name = {
props: ['actor', 'editable'],
setup(props) {
// Get a collection of all objects associated with the actor
const { actor } = Vue.toRefs(props)
const $gf = Vue.inject('graffiti')
return $gf.useObjects([actor])
},
computed: {
profile() {
return this.objects
// Filter the raw objects for profile data
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-profile
.filter(m=>
// Does the message have a type property?
m.type &&
// Is the value of that property 'Profile'?
m.type=='Profile' &&
// Does the message have a name property?
m.name &&
// Is that property a string?
typeof m.name=='string')
// Choose the most recent one or null if none exists
.reduce((prev, curr)=> !prev || curr.published > prev.published? curr : prev, null)
}
},
data() {
return {
editing: false,
editText: ''
}
},
methods: {
editName() {
this.editing = true
// If we already have a profile,
// initialize the edit text to our existing name
this.editText = this.profile? this.profile.name : this.editText
},
saveName() {
if (this.profile) {
// If we already have a profile, just change the name
// (this will sync automatically)
this.profile.name = this.editText
} else {
// Otherwise create a profile
this.$gf.post({
type: 'Profile',
name: this.editText
})
}
// Exit the editing state
this.editing = false
}
},
template: '#name'
}
app.components = { Name }
Vue.createApp(app)
.use(GraffitiPlugin(Vue))
.mount('#app')