mirror of
https://github.com/styppo/hamstr.git
synced 2024-10-18 13:33:22 +00:00
tweaked mentions, feed, following
This commit is contained in:
parent
59a77368c6
commit
ec3d3396d4
@ -35,6 +35,7 @@
|
|||||||
"tributejs": "^5.1.3",
|
"tributejs": "^5.1.3",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
"vue-router": "^4.0.0",
|
"vue-router": "^4.0.0",
|
||||||
|
"vuedraggable": "^4.1.0",
|
||||||
"vuex": "^4.0.1"
|
"vuex": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -15,13 +15,6 @@
|
|||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
{{isFollowing ? "unfollow" : "follow" }}
|
{{isFollowing ? "unfollow" : "follow" }}
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
<q-icon
|
|
||||||
:name='isFollowing ? "person_remove" : "person_add"'
|
|
||||||
:class='isFollowing ? "flip-horizontal" : ""'
|
|
||||||
/>
|
|
||||||
<q-tooltip>
|
|
||||||
{{isFollowing ? "unfollow" : "follow" }}
|
|
||||||
</q-tooltip>
|
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -127,6 +127,9 @@ div#emoji-mart-list section:first-of-type h3:first-of-type {
|
|||||||
.emoji-mart-emoji {
|
.emoji-mart-emoji {
|
||||||
padding: .3rem;
|
padding: .3rem;
|
||||||
}
|
}
|
||||||
|
.emoji-mart-emoji:hover:before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
button.emoji-mart-emoji {
|
button.emoji-mart-emoji {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
@ -205,12 +205,18 @@ ul {
|
|||||||
padding-inline-start: 1rem;
|
padding-inline-start: 1rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
.post-highlighted ul {
|
||||||
|
padding-inline-start: 1.5rem;
|
||||||
|
}
|
||||||
ol {
|
ol {
|
||||||
list-style-type: decimal;
|
list-style-type: decimal;
|
||||||
list-style-position: outside;
|
list-style-position: outside;
|
||||||
padding-inline-start: 1rem;
|
padding-inline-start: 1rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
.post-highlighted ol {
|
||||||
|
padding-inline-start: 1.5rem;
|
||||||
|
}
|
||||||
ul ul,
|
ul ul,
|
||||||
ol ul {
|
ol ul {
|
||||||
list-style-type: circle;
|
list-style-type: circle;
|
||||||
@ -226,7 +232,6 @@ ul ol {
|
|||||||
p {
|
p {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.break-word-wrap {
|
.break-word-wrap {
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
@ -30,14 +30,15 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- <q-list id='tribute-wrapper' class='overflow-auto' style='position: aboslute; bottom: 100%; max-height: 70vh;'> -->
|
<!-- <q-list id='tribute-wrapper' class='overflow-auto' style='position: aboslute; bottom: 100%; max-height: 70vh;'> -->
|
||||||
<q-list id='tribute-wrapper' class='overflow-auto flex row z-top' style='max-height: 70vh' @click.stop>
|
<q-list id='tribute-wrapper' class='overflow-auto flex row z-top' style='max-height: 70vh' @click.stop='focusInput'>
|
||||||
</q-list>
|
</q-list>
|
||||||
<q-list v-if='Object.keys(mentions).length && !sending && focus'>
|
<q-list v-if='tags.length && !sending' class='q-px-sm tagged-wrapper'>
|
||||||
<div class='text-bold text-caption'>tags<span v-if='$route.name === "messages"'>{{' **NOTE TAGS ARE NOT PRIVATE**'}}</span></div>
|
<div class='text-bold text-subtitle2 text-primary'>tagged<span v-if='$route.name === "messages"'>{{' **NOTE TAGS ARE NOT PRIVATE**'}}</span></div>
|
||||||
<div v-for='(mention, index) in mentions' :key='index' class='flex row no-wrap'>
|
<div v-for='(tag, index) in tags' :key='index' class='flex row no-wrap q-gutter-xs'>
|
||||||
{{ "#[" + mention.index + "] "+ (mention.tag[0] === "e" ? " event: " : "") + (mention.tag[0] === "p" ? " profile: " : "")}}
|
<div class='text-bold'>{{ "#[" + index + "] " }}</div>
|
||||||
<BaseUserName v-if='mention.tag[0] === "p"' :pubkey='mention.tag[1]' :fallback='true'/>
|
<div>{{ (tag[0] === "e" ? " event: " : "") + (tag[0] === "p" ? " user: " : "")}}</div>
|
||||||
<BaseMarkdown v-if='mention.tag[0] === "e"'> {{ `[&${shorten(mention.tag[1])}](/event/${mention.tag[1]})` }} </BaseMarkdown>
|
<BaseUserName v-if='tag[0] === "p"' :pubkey='tag[1]' :fallback='true'/>
|
||||||
|
<BaseMarkdown v-if='tag[0] === "e"'> {{ `[&${shorten(tag[1])}](/event/${tag[1]})` }} </BaseMarkdown>
|
||||||
</div>
|
</div>
|
||||||
</q-list>
|
</q-list>
|
||||||
<q-form
|
<q-form
|
||||||
@ -67,19 +68,14 @@
|
|||||||
autogrow
|
autogrow
|
||||||
autofocus
|
autofocus
|
||||||
:label="label"
|
:label="label"
|
||||||
:disable='sending'
|
:disable='sending || mentionsUpdating'
|
||||||
:loading='mentionsUpdating'
|
:loading='mentionsUpdating'
|
||||||
@update='updateCursorPosition'
|
|
||||||
@keypress.ctrl.enter="send"
|
@keypress.ctrl.enter="send"
|
||||||
@keyup='updateCursorPosition'
|
@click='trigger++'
|
||||||
@keydown='updateCursorPosition'
|
@keyup='trigger++'
|
||||||
@delete='updateCursorPosition'
|
|
||||||
@click='updateCursorPosition'
|
|
||||||
@focus='focus = true'
|
|
||||||
@blur='focus = false'
|
|
||||||
>
|
>
|
||||||
<template #loading>
|
<template #loading>
|
||||||
<div class='row justify-center q-my-md'>
|
<div class='full-width row justify-center q-my-md'>
|
||||||
<q-spinner-orbit color="accent" size='md'/>
|
<q-spinner-orbit color="accent" size='md'/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -167,6 +163,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import helpersMixin from '../utils/mixin'
|
import helpersMixin from '../utils/mixin'
|
||||||
// import {getPubKeyTagWithRelay, getEventTagWithRelay, processMentions} from '../utils/helpers'
|
// import {getPubKeyTagWithRelay, getEventTagWithRelay, processMentions} from '../utils/helpers'
|
||||||
|
// import {nextTick} from 'vue'
|
||||||
import {getPubKeyTagWithRelay, getEventTagWithRelay, extractMentions} from '../utils/helpers'
|
import {getPubKeyTagWithRelay, getEventTagWithRelay, extractMentions} from '../utils/helpers'
|
||||||
import BaseButtonCopy from 'components/BaseButtonCopy.vue'
|
import BaseButtonCopy from 'components/BaseButtonCopy.vue'
|
||||||
import BaseButtonClear from 'components/BaseButtonClear.vue'
|
import BaseButtonClear from 'components/BaseButtonClear.vue'
|
||||||
@ -212,7 +209,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
text: '',
|
text: '',
|
||||||
cursorPosition: 0,
|
// cursorPosition: 0,
|
||||||
sending: false,
|
sending: false,
|
||||||
// emojiSelecting: false,
|
// emojiSelecting: false,
|
||||||
toolSelected: '',
|
toolSelected: '',
|
||||||
@ -220,32 +217,40 @@ export default {
|
|||||||
sendIconTranslation: 0,
|
sendIconTranslation: 0,
|
||||||
// tributeList: [],
|
// tributeList: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
focus,
|
// focus,
|
||||||
mentionsUpdating: false,
|
mentionsUpdating: false,
|
||||||
focusInput() {
|
focusInput() {
|
||||||
this.$refs.input.focus()
|
setTimeout(async () => {
|
||||||
|
await this.$nextTick()
|
||||||
|
this.$refs.input.focus()
|
||||||
|
}, 1)
|
||||||
},
|
},
|
||||||
|
trigger: 1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
mentions(curr, prev) {
|
||||||
|
if (Object.keys(curr).length < Object.keys(prev).length) {
|
||||||
|
// await this.$nextTick()
|
||||||
|
// this.trigger++
|
||||||
|
this.recalibrateMentionTags()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'text'(curr, prev) {
|
||||||
|
if (curr.length > prev.length) {
|
||||||
|
this.updateMentionsTags()
|
||||||
|
}
|
||||||
|
},
|
||||||
'replyMode'(curr, prev) {
|
'replyMode'(curr, prev) {
|
||||||
if (this.replyMode && curr !== prev) {
|
if (curr !== prev) {
|
||||||
this.$emit('resized')
|
this.$emit('resized')
|
||||||
setTimeout(async () => {
|
this.focusInput()
|
||||||
await this.$nextTick()
|
|
||||||
this.focusInput()
|
|
||||||
}, 20)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'messageMode'(curr, prev) {
|
'messageMode'(curr, prev) {
|
||||||
if (this.messageMode && curr !== prev) {
|
if (curr !== prev) {
|
||||||
// this.focusInput()
|
this.focusInput()
|
||||||
// await this.$nextTick()
|
|
||||||
setTimeout(async () => {
|
|
||||||
await this.$nextTick()
|
|
||||||
this.focusInput()
|
|
||||||
}, 20)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -258,7 +263,6 @@ export default {
|
|||||||
return this.createMentionsProvider()
|
return this.createMentionsProvider()
|
||||||
},
|
},
|
||||||
overCharLimit() {
|
overCharLimit() {
|
||||||
// if (this.messageMode) false
|
|
||||||
return 280 - this.text.length < 0
|
return 280 - this.text.length < 0
|
||||||
},
|
},
|
||||||
textValid() {
|
textValid() {
|
||||||
@ -326,6 +330,16 @@ export default {
|
|||||||
}
|
}
|
||||||
return hashtags
|
return hashtags
|
||||||
},
|
},
|
||||||
|
cursorPosition() {
|
||||||
|
// only checking this.text.length to trigger recompute
|
||||||
|
if (this.text.length && this.trigger) return this.textarea.selectionStart
|
||||||
|
else return this.textarea.selectionStart
|
||||||
|
},
|
||||||
|
cursorPositionEnd() {
|
||||||
|
// only checking this.text.length to trigger recompute
|
||||||
|
if (this.text.length && this.trigger) return this.textarea.selectionEnd
|
||||||
|
else return this.textarea.selectionEnd
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -334,6 +348,7 @@ export default {
|
|||||||
|
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.profileMentionsProvider.detach(this.textarea)
|
this.profileMentionsProvider.detach(this.textarea)
|
||||||
|
this.reset()
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -346,40 +361,17 @@ export default {
|
|||||||
console.log('send already in progress')
|
console.log('send already in progress')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.cursorPosition = 0
|
|
||||||
this.toolSelected = ''
|
this.toolSelected = ''
|
||||||
this.sending = true
|
this.sending = true
|
||||||
this.animateSendIcon()
|
this.animateSendIcon()
|
||||||
this.text = await extractMentions(this.text, this.tags)
|
this.text = await extractMentions(this.text, this.tags)
|
||||||
// make sure mentions are sequential
|
this.recalibrateMentionTags()
|
||||||
if (Object.keys(this.mentions).length &&
|
|
||||||
Math.max(...Object.keys(this.mentions).map(key => key.split('_')[0])) >
|
|
||||||
this.tags.filter(tag => tag.length >= 2).filter((v, i, a) => a.indexOf(v) === i).length) {
|
|
||||||
// save copy of mentions and remove for now
|
|
||||||
let mentions = Object.assign({}, this.mentions)
|
|
||||||
this.tags = []
|
|
||||||
let offset = 0
|
|
||||||
console.log('fyi having to clean up too many mentions')
|
|
||||||
// now add back mentions
|
|
||||||
for (let index in mentions) {
|
|
||||||
let mention = mentions[index]
|
|
||||||
let idx = this.tags.findIndex(([t, v]) => t === mention.tag[0] && v === mention.tag[1])
|
|
||||||
// console.log('idx', idx)
|
|
||||||
if (idx === -1) {
|
|
||||||
this.tags.push(mention.tag)
|
|
||||||
idx = this.tags.length - 1
|
|
||||||
}
|
|
||||||
this.text = this.text.slice(0, mention.position + offset) + idx + this.text.slice(mention.position + offset + mention.length)
|
|
||||||
if (mention.length !== String(idx).length) offset = String(idx).length - mention.length
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
let event
|
let event
|
||||||
if (this.replyMode) event = await this.sendReply()
|
if (this.replyMode) event = await this.sendReply()
|
||||||
else if (this.messageMode) event = await this.sendMessage()
|
else if (this.messageMode) event = await this.sendMessage()
|
||||||
else event = await this.sendPost()
|
else event = await this.sendPost()
|
||||||
if (event) {
|
if (event) {
|
||||||
this.interpolateEventMentions(event)
|
if (!this.messageMode) this.interpolateEventMentions(event)
|
||||||
this.reset()
|
this.reset()
|
||||||
this.$emit('sent', event)
|
this.$emit('sent', event)
|
||||||
if (this.messageMode) this.$emit('clear-event')
|
if (this.messageMode) this.$emit('clear-event')
|
||||||
@ -391,7 +383,7 @@ export default {
|
|||||||
async sendPost() {
|
async sendPost() {
|
||||||
this.appendHashtags()
|
this.appendHashtags()
|
||||||
let tags = this.tags.map(([...v]) => [...v])
|
let tags = this.tags.map(([...v]) => [...v])
|
||||||
// console.log('tags sendPost:', tags)
|
// console.log('tags sendPost:', tags, this.tags)
|
||||||
let event = await this.$store.dispatch('sendPost', {message: this.text, tags: tags})
|
let event = await this.$store.dispatch('sendPost', {message: this.text, tags: tags})
|
||||||
if (event) {
|
if (event) {
|
||||||
return event
|
return event
|
||||||
@ -411,7 +403,6 @@ export default {
|
|||||||
let usableTags = this.event.tags.filter(
|
let usableTags = this.event.tags.filter(
|
||||||
([t, v]) => (t === 'p' || t === 'e') && v
|
([t, v]) => (t === 'p' || t === 'e') && v
|
||||||
).map(([t, v]) => { return [t, v] })
|
).map(([t, v]) => { return [t, v] })
|
||||||
// console.log('usableTags: ', usableTags)
|
|
||||||
|
|
||||||
// add last 4 pubkeys mentioned
|
// add last 4 pubkeys mentioned
|
||||||
let pubkeys = usableTags.filter(([t, v]) => t === 'p').map(([_, v]) => v)
|
let pubkeys = usableTags.filter(([t, v]) => t === 'p').map(([_, v]) => v)
|
||||||
@ -419,7 +410,6 @@ export default {
|
|||||||
for (let i = 0; i < Math.min(4, pubkeys.length); i++) {
|
for (let i = 0; i < Math.min(4, pubkeys.length); i++) {
|
||||||
this.tags.push(await getPubKeyTagWithRelay(pubkeys[pubkeys.length - 1 - i]))
|
this.tags.push(await getPubKeyTagWithRelay(pubkeys[pubkeys.length - 1 - i]))
|
||||||
}
|
}
|
||||||
// console.log('tags: ', tags)
|
|
||||||
// plus the author of the note being replied to, if not present already
|
// plus the author of the note being replied to, if not present already
|
||||||
if (!this.tags.find(([_, v]) => v === this.event.pubkey)) {
|
if (!this.tags.find(([_, v]) => v === this.event.pubkey)) {
|
||||||
this.tags.push(await getPubKeyTagWithRelay(this.event.pubkey))
|
this.tags.push(await getPubKeyTagWithRelay(this.event.pubkey))
|
||||||
@ -437,7 +427,6 @@ export default {
|
|||||||
let last = getEventTagWithRelay(this.event)
|
let last = getEventTagWithRelay(this.event)
|
||||||
this.tags.push(last)
|
this.tags.push(last)
|
||||||
}
|
}
|
||||||
// console.log('tags: ', tags)
|
|
||||||
|
|
||||||
// remove ourselves
|
// remove ourselves
|
||||||
this.tags = this.tags.filter(([_, v]) => v !== this.$store.state.keys.pub)
|
this.tags = this.tags.filter(([_, v]) => v !== this.$store.state.keys.pub)
|
||||||
@ -459,19 +448,10 @@ export default {
|
|||||||
}
|
}
|
||||||
this.appendHashtags()
|
this.appendHashtags()
|
||||||
let tags = this.tags.map(([...v]) => [...v])
|
let tags = this.tags.map(([...v]) => [...v])
|
||||||
// let tags = this.tags.map((tag) => {
|
|
||||||
// if (tag.length >= 3) return [tag[0], tag[1], tag[2]]
|
|
||||||
// else return [tag[0], tag[1]]
|
|
||||||
// })
|
|
||||||
// console.log('text: ', this.text)
|
|
||||||
// tags.push(...this.hashtags)
|
|
||||||
// console.log('tags: ', tags)
|
|
||||||
// console.log('event', event)
|
|
||||||
return await this.$store.dispatch('sendPost', {
|
return await this.$store.dispatch('sendPost', {
|
||||||
message: this.text,
|
message: this.text,
|
||||||
tags: tags
|
tags: tags
|
||||||
})
|
})
|
||||||
// 07e4f77f3f1c8342f4627381832c4b796d7795e2355e5bba5eef672ee65e1d20
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
@ -492,35 +472,30 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateCursorPosition() {
|
async updateMentionsTags() {
|
||||||
// console.log('mentions', this.mentions)
|
// let endOffset = this.text.length - this.cursorPosition
|
||||||
// console.log('tags', this.tags)
|
let curPos = this.cursorPosition
|
||||||
|
let prevTextLength = this.text.length
|
||||||
|
// console.log('updateMentionsTags cursor pos start, end', curPos, this.tags)
|
||||||
|
// if (curPos !== curPosEnd) return
|
||||||
const mentionRegex = /(?<t>[@&]{1})(?<p>[a-f0-9]{64})/g
|
const mentionRegex = /(?<t>[@&]{1})(?<p>[a-f0-9]{64})/g
|
||||||
if (this.text.match(mentionRegex)) {
|
if (this.text.toLowerCase().match(mentionRegex)) {
|
||||||
|
// console.log('mention found', this.text.length, this.cursorPosition)
|
||||||
this.mentionsUpdating = true
|
this.mentionsUpdating = true
|
||||||
this.text = await extractMentions(this.text, this.tags)
|
this.text = await extractMentions(this.text, this.tags)
|
||||||
this.mentionsUpdating = false
|
this.mentionsUpdating = false
|
||||||
|
this.focusInput()
|
||||||
|
this.setCursorPosition(curPos + (this.text.length - prevTextLength))
|
||||||
}
|
}
|
||||||
// let interpolated = this.interpolateMentions(this.text, this.tags)
|
|
||||||
// this.tags = interpolated.
|
|
||||||
let cursorPos = this.$refs.input.$el.querySelector('textarea').selectionStart
|
|
||||||
if (cursorPos) {
|
|
||||||
this.cursorPosition = cursorPos
|
|
||||||
}
|
|
||||||
// const mentionAnchorRegex = /#\[/g
|
|
||||||
// const mentionAnchorRegex = /#\[(\d+)\]/g
|
|
||||||
// let matches = this.text.matchAll(mentionAnchorRegex)
|
|
||||||
// let mentions = {}
|
|
||||||
// for (let match in this.text.matchAll(mentionAnchorRegex)) {
|
|
||||||
// console.log('match', match)
|
|
||||||
// mentions[match.group.i] = this.tags[match.group.i]
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
|
|
||||||
insertEmoji(emoji) {
|
insertEmoji(emoji) {
|
||||||
this.text = this.text.slice(0, this.cursorPosition) + emoji.native + this.text.slice(this.cursorPosition)
|
let curPos = this.cursorPosition
|
||||||
this.cursorPosition += emoji.native.length
|
let text = this.text
|
||||||
// this.cursorPosition++
|
text = text.slice(0, curPos) + emoji.native + text.slice(curPos)
|
||||||
|
this.text = text
|
||||||
|
this.setCursorPosition(curPos + emoji.native.length)
|
||||||
|
this.focusInput()
|
||||||
},
|
},
|
||||||
|
|
||||||
animateSendIcon() {
|
animateSendIcon() {
|
||||||
@ -552,10 +527,6 @@ export default {
|
|||||||
this.tags = []
|
this.tags = []
|
||||||
},
|
},
|
||||||
|
|
||||||
upshiftTags(tags) {
|
|
||||||
if (this.tags.length === 0) this.tags.concat(tags)
|
|
||||||
},
|
|
||||||
|
|
||||||
appendHashtags() {
|
appendHashtags() {
|
||||||
for (let hashtag of this.hashtags) {
|
for (let hashtag of this.hashtags) {
|
||||||
if (!this.tags.find(([_, v]) => v === hashtag)) {
|
if (!this.tags.find(([_, v]) => v === hashtag)) {
|
||||||
@ -563,6 +534,49 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
recalibrateMentionTags() {
|
||||||
|
let curPos = this.cursorPosition
|
||||||
|
// console.log('recalibrateMentionTags cursor pos start, end', curPos, this.text.length)
|
||||||
|
let mentions = Object.assign({}, this.mentions)
|
||||||
|
// if (Object.keys(mentions).length === 0 && this.tags.length === 0) return
|
||||||
|
this.tags = []
|
||||||
|
let offset = 0
|
||||||
|
let text = this.text
|
||||||
|
// now add back mentions
|
||||||
|
for (let index in mentions) {
|
||||||
|
let mention = mentions[index]
|
||||||
|
let idx = this.tags.findIndex(([t, v]) => t === mention.tag[0] && v === mention.tag[1])
|
||||||
|
if (idx === -1) {
|
||||||
|
this.tags.push(mention.tag)
|
||||||
|
idx = this.tags.length - 1
|
||||||
|
}
|
||||||
|
if (String(idx) === mention.tag[1]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
text = text.slice(0, mention.position + offset) + idx + text.slice(mention.position + offset + mention.length)
|
||||||
|
if (mention.length !== String(idx).length) {
|
||||||
|
offset += String(idx).length - mention.length
|
||||||
|
if (mention.position + mention.length < curPos) {
|
||||||
|
// await this.setCursorPosition(curPos + String(idx).length - mention.length)
|
||||||
|
curPos += String(idx).length - mention.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.text !== text) {
|
||||||
|
this.text = text
|
||||||
|
this.setCursorPosition(curPos)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setCursorPosition(pos) {
|
||||||
|
// console.log('setting cursor position to ', pos)
|
||||||
|
setTimeout(async () => {
|
||||||
|
this.textarea.setSelectionRange(pos, pos)
|
||||||
|
this.trigger++
|
||||||
|
// console.log('checking cursor position', this.cursorPosition)
|
||||||
|
}, 1)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -597,7 +611,16 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang='scss'>
|
||||||
ul {
|
#tribute-wrapper ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
#tribute-wrapper .tribute-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#tribute-wrapper .tribute-container .highlight {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,15 +52,46 @@
|
|||||||
<q-separator color='accent' />
|
<q-separator color='accent' />
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<h2 class='text-h5 text-bold q-my-none'> following </h2>
|
<div class='flex row justify-between no-wrap'>
|
||||||
|
<h2 class='text-h5 text-bold q-my-none'> following </h2>
|
||||||
|
<div>
|
||||||
|
<q-btn v-if='!reordering' flat icon='reorder' @click.stop='reorderFollowing'>
|
||||||
|
<q-tooltip>reorder following list</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<q-btn v-if='reordering' flat icon='close' @click.stop='cancelReorder'>
|
||||||
|
<q-tooltip>cancel</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<q-card-section class='no-padding' style='overflow-y: auto;'>
|
<q-card-section class='no-padding' style='overflow-y: auto;'>
|
||||||
<q-list v-if="$store.state.following.length" class='q-mt-xs q-pl-sm'>
|
<div v-if='$store.state.following.length' class='q-mt-xs q-pl-sm'>
|
||||||
<BaseUserCard
|
<q-list v-if="!reordering">
|
||||||
v-for="pubkey in $store.state.following"
|
<BaseUserCard
|
||||||
:pubkey="pubkey"
|
v-for="pubkey in $store.state.following"
|
||||||
:key="pubkey"
|
:pubkey="pubkey"
|
||||||
/>
|
:key="pubkey"
|
||||||
</q-list>
|
/>
|
||||||
|
</q-list>
|
||||||
|
<Draggable
|
||||||
|
v-else-if='reorderedFollowing.length'
|
||||||
|
v-model='reorderedFollowing'
|
||||||
|
@start="dragging=true"
|
||||||
|
@end="dragging=false"
|
||||||
|
item-key="pubkey"
|
||||||
|
>
|
||||||
|
<!-- <div>{{element.name}}</div> -->
|
||||||
|
<template #header>
|
||||||
|
<div class='flex row justify-between items-start'>
|
||||||
|
<span>drag and drop to reorder</span>
|
||||||
|
<q-btn outline size='sm' icon='save' label='save' color='secondary' @click.stop='saveReorder'/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #item="{element}">
|
||||||
|
<BaseUserCard :pubkey='element.pubkey' :action-buttons='false'/>
|
||||||
|
</template>
|
||||||
|
<!-- <BaseUserCard :clickable='false' :pubkey="element.pubkey" /> -->
|
||||||
|
</Draggable>
|
||||||
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
When you follow someone they will show up here.
|
When you follow someone they will show up here.
|
||||||
</div>
|
</div>
|
||||||
@ -71,6 +102,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import {Notify} from 'quasar'
|
import {Notify} from 'quasar'
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
import {searchDomain, queryName} from 'nostr-tools/nip05'
|
import {searchDomain, queryName} from 'nostr-tools/nip05'
|
||||||
import helpersMixin from '../utils/mixin'
|
import helpersMixin from '../utils/mixin'
|
||||||
import BaseButtonClear from 'components/BaseButtonClear.vue'
|
import BaseButtonClear from 'components/BaseButtonClear.vue'
|
||||||
@ -85,11 +117,15 @@ export default defineComponent({
|
|||||||
searching: false,
|
searching: false,
|
||||||
domainMode: false,
|
domainMode: false,
|
||||||
domainNames: {},
|
domainNames: {},
|
||||||
|
reordering: false,
|
||||||
|
reorderedFollowing: [],
|
||||||
|
dragging: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
BaseButtonClear,
|
BaseButtonClear,
|
||||||
|
Draggable,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
@ -176,6 +212,22 @@ export default defineComponent({
|
|||||||
message: 'No user found! Please enter full public key or NIP05 identifier and double check search string',
|
message: 'No user found! Please enter full public key or NIP05 identifier and double check search string',
|
||||||
color: 'negative'
|
color: 'negative'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
reorderFollowing() {
|
||||||
|
this.reorderedFollowing = this.$store.state.following.map((pubkey) => { return {pubkey} })
|
||||||
|
this.reordering = true
|
||||||
|
},
|
||||||
|
|
||||||
|
saveReorder() {
|
||||||
|
this.$store.commit('reorderFollows', this.reorderedFollowing.map(follow => follow.pubkey))
|
||||||
|
this.reordering = false
|
||||||
|
this.reorderedFollowing = []
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelReorder() {
|
||||||
|
this.reordering = false
|
||||||
|
this.reorderedFollowing = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -100,9 +100,9 @@
|
|||||||
transition-show='slide-up'
|
transition-show='slide-up'
|
||||||
transition-hide='slide-down'
|
transition-hide='slide-down'
|
||||||
>
|
>
|
||||||
<q-card unelevated class='column postEntry'
|
<q-card unelevated class='flex column no-wrap post-entry'
|
||||||
>
|
>
|
||||||
<div class='flex row justify-end q-pa-sm'>
|
<div class='flex row justify-end'>
|
||||||
<q-btn icon="close" flat dense v-close-popup/>
|
<q-btn icon="close" flat dense v-close-popup/>
|
||||||
</div>
|
</div>
|
||||||
<BasePostEntry class='q-pa-md' @sent='post = false'/>
|
<BasePostEntry class='q-pa-md' @sent='post = false'/>
|
||||||
@ -215,9 +215,9 @@ const userMenuItems = [
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.q-dialog .postEntry {
|
.q-dialog .post-entry {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
overflow: visible;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.compact-user-menu-space {
|
.compact-user-menu-space {
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
>
|
>
|
||||||
<q-tab name="follows" label='follows' />
|
<q-tab name="follows" label='follows' />
|
||||||
<q-tab name="global" label='global' />
|
<q-tab name="global" label='global' />
|
||||||
|
<q-tab v-if='botsFeed.length' name="bots" label='bots' />
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
<q-tab-panels v-model="tab" animated>
|
<q-tab-panels v-model="tab" animated>
|
||||||
<q-tab-panel name="follows" class='no-padding'>
|
<q-tab-panel name="follows" class='no-padding'>
|
||||||
@ -93,6 +94,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel v-if='botsFeed.length' name="bots" class='no-padding'>
|
||||||
|
<div>
|
||||||
|
<q-virtual-scroll :items='botsFeed' virtual-scroll-item-size="110" ref='botsFeedScroll'>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<BasePostThread :key="item[0].id" :events="item" @add-event='addEventGlobal'/>
|
||||||
|
</template>
|
||||||
|
</q-virtual-scroll>
|
||||||
|
<div v-if='botsFeed.length'>
|
||||||
|
<q-separator color='accent'/>
|
||||||
|
<q-btn-group
|
||||||
|
flat
|
||||||
|
spread
|
||||||
|
dense
|
||||||
|
text-color="accent"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
:loading='loadingMore'
|
||||||
|
flat
|
||||||
|
color="accent"
|
||||||
|
class='text-weight-light'
|
||||||
|
style='letter-spacing: .1rem;'
|
||||||
|
label='load another day'
|
||||||
|
@click="loadMoreGlobalFeed"
|
||||||
|
>
|
||||||
|
<template #loading>
|
||||||
|
<div class='row justify-center q-my-md'>
|
||||||
|
<q-spinner-orbit color="accent" size='md' />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-btn>
|
||||||
|
</q-btn-group>
|
||||||
|
<q-separator color='accent'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
@ -115,6 +153,9 @@ export default {
|
|||||||
followsFeedSet: new Set(),
|
followsFeedSet: new Set(),
|
||||||
globalFeed: [],
|
globalFeed: [],
|
||||||
globalFeedSet: new Set(),
|
globalFeedSet: new Set(),
|
||||||
|
botsFeed: [],
|
||||||
|
botsFeedSet: new Set(),
|
||||||
|
bots: [],
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
tab: 'follows',
|
tab: 'follows',
|
||||||
sub: null,
|
sub: null,
|
||||||
@ -125,15 +166,6 @@ export default {
|
|||||||
async mounted() {
|
async mounted() {
|
||||||
this.loadMoreFollowsFeed()
|
this.loadMoreFollowsFeed()
|
||||||
this.loadMoreGlobalFeed()
|
this.loadMoreGlobalFeed()
|
||||||
// let notes = await dbGetHomeFeedNotes(200)
|
|
||||||
// this.interpolateEventMentions(notes)
|
|
||||||
// if (notes.length === 0) this.tab = 'global'
|
|
||||||
// if (notes.length > 0) this.reachedEnd = false
|
|
||||||
|
|
||||||
// for (let i = notes.length - 1; i >= 0; i--) {
|
|
||||||
// addToThread(this.followsFeed, notes[i])
|
|
||||||
// this.followsFeedSet.add(notes[i].id)
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.listener = onNewHomeFeedNote(event => {
|
this.listener = onNewHomeFeedNote(event => {
|
||||||
if (this.followsFeedSet.has(event.id)) return
|
if (this.followsFeedSet.has(event.id)) return
|
||||||
@ -183,40 +215,29 @@ export default {
|
|||||||
this.loadingMore = false
|
this.loadingMore = false
|
||||||
},
|
},
|
||||||
|
|
||||||
// listenGlobalFeed() {
|
async loadMoreGlobalFeed() {
|
||||||
// this.globalFeed = []
|
|
||||||
// this.globalFeedSet = new Set()
|
|
||||||
|
|
||||||
// this.sub = pool.sub(
|
|
||||||
// {
|
|
||||||
// filter: [
|
|
||||||
// {
|
|
||||||
// kinds: [1, 2],
|
|
||||||
// since: Math.floor(Date.now() / 1000) - 86400,
|
|
||||||
// until: Math.floor(Date.now() / 1000)
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// cb: async (event, relay) => {
|
|
||||||
// if (this.globalFeedSet.has(event.id)) return
|
|
||||||
|
|
||||||
// // this.$store.dispatch('useProfile', {
|
|
||||||
// // pubkey: event.pubkey,
|
|
||||||
// // request: true
|
|
||||||
// // })
|
|
||||||
// this.interpolateEventMentions(event)
|
|
||||||
// this.globalFeedSet.add(event.id)
|
|
||||||
// addToThread(this.globalFeed, event, 'feed')
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// 'global-feed'
|
|
||||||
// )
|
|
||||||
// },
|
|
||||||
|
|
||||||
loadMoreGlobalFeed() {
|
|
||||||
this.loadingMore = true
|
this.loadingMore = true
|
||||||
if (this.sub) this.sub.unsub()
|
if (this.sub) this.sub.unsub()
|
||||||
|
|
||||||
|
if (this.bots.length === 0) {
|
||||||
|
await new Promise(resolve => {
|
||||||
|
let sub = pool.sub({
|
||||||
|
filter: [{authors: ['29f63b70d8961835b14062b195fc7d84fa810560b36dde0749e4bc084f0f8952'], kinds: [3]}],
|
||||||
|
cb: async event => {
|
||||||
|
this.bots = event.tags.filter(([t, v]) => t === 'p' && v).map(([_, v]) => v)
|
||||||
|
clearTimeout(timeout)
|
||||||
|
if (sub) sub.unsub()
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let timeout = setTimeout(() => {
|
||||||
|
sub.unsub()
|
||||||
|
sub = null
|
||||||
|
resolve()
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.since) this.since = Math.floor(Date.now() / 1000) - 86400
|
if (!this.since) this.since = Math.floor(Date.now() / 1000) - 86400
|
||||||
else this.since -= 86400
|
else this.since -= 86400
|
||||||
|
|
||||||
@ -238,7 +259,8 @@ export default {
|
|||||||
// })
|
// })
|
||||||
this.interpolateEventMentions(event)
|
this.interpolateEventMentions(event)
|
||||||
this.globalFeedSet.add(event.id)
|
this.globalFeedSet.add(event.id)
|
||||||
addToThread(this.globalFeed, event, 'feed')
|
if (this.bots.includes(event.pubkey)) addToThread(this.botsFeed, event)
|
||||||
|
else addToThread(this.globalFeed, event, 'feed')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -259,7 +281,9 @@ export default {
|
|||||||
if (this.globalFeedSet.has(event.id)) return
|
if (this.globalFeedSet.has(event.id)) return
|
||||||
this.interpolateEventMentions(event)
|
this.interpolateEventMentions(event)
|
||||||
this.globalFeedSet.add(event.id)
|
this.globalFeedSet.add(event.id)
|
||||||
addToThread(this.globalFeed, event, 'feed')
|
// addToThread(this.globalFeed, event, 'feed')
|
||||||
|
if (this.bots.includes(event.pubkey)) addToThread(this.botsFeed, event)
|
||||||
|
else addToThread(this.globalFeed, event, 'feed')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,12 @@
|
|||||||
@click.stop='scrollToBottom()'
|
@click.stop='scrollToBottom()'
|
||||||
/>
|
/>
|
||||||
<!-- <q-separator v-if='Object.keys(replyEvent).length' color='primary' size='1px'/> -->
|
<!-- <q-separator v-if='Object.keys(replyEvent).length' color='primary' size='1px'/> -->
|
||||||
<BasePostEntry :message-mode='replyEvent? "reply" : "message"' :event='replyEvent' @clear-event='replyEvent=null'/>
|
<BasePostEntry
|
||||||
|
:message-mode='replyEvent? "reply" : "message"'
|
||||||
|
:event='replyEvent'
|
||||||
|
@sent='addMessage'
|
||||||
|
@clear-event='replyEvent=null'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
@ -148,41 +153,7 @@ export default {
|
|||||||
this.$store.dispatch('useProfile', {pubkey: this.$route.params.pubkey})
|
this.$store.dispatch('useProfile', {pubkey: this.$route.params.pubkey})
|
||||||
|
|
||||||
this.listener = onNewMessage(this.$route.params.pubkey, async event => {
|
this.listener = onNewMessage(this.$route.params.pubkey, async event => {
|
||||||
if (this.messagesSet.has(event.id)) return
|
this.addMessage(event)
|
||||||
this.messagesSet.add(event.id)
|
|
||||||
|
|
||||||
await this.lock()
|
|
||||||
event.text = await this.getPlaintext(event)
|
|
||||||
this.unlock()
|
|
||||||
this.interpolateMessageMentions(event)
|
|
||||||
if (event.tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(event)
|
|
||||||
|
|
||||||
let messageScroll = this.$refs.messageScroll
|
|
||||||
let scrollToBottom = 100 > Math.abs((messageScroll.scrollHeight - messageScroll.clientHeight) - messageScroll.scrollTop) ||
|
|
||||||
messageScroll.scrollHeight === messageScroll.clientHeight
|
|
||||||
|
|
||||||
if (this.messages.length === 0) {
|
|
||||||
this.messages.push(event)
|
|
||||||
} else {
|
|
||||||
let last = this.messages[this.messages.length - 1]
|
|
||||||
if (
|
|
||||||
event.pubkey === this.$store.state.keys.pub &&
|
|
||||||
last.pubkey === event.pubkey &&
|
|
||||||
last.created_at + 120 >= event.created_at
|
|
||||||
) {
|
|
||||||
last.appended = last.appended || []
|
|
||||||
last.appended.push(event)
|
|
||||||
} else {
|
|
||||||
this.messages.push(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollToBottom) {
|
|
||||||
this.$store.commit('haveReadMessage', this.$route.params.pubkey)
|
|
||||||
this.scrollToBottom()
|
|
||||||
} else if (event.pubkey === this.$route.params.pubkey) {
|
|
||||||
this.unreadMessagesSet.add(event.id)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -208,28 +179,44 @@ export default {
|
|||||||
this.canLoadMore = false
|
this.canLoadMore = false
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessages = newMessages.filter(event => !this.messagesSet.has(event.id))
|
// newMessages = newMessages.filter(event => !this.messagesSet.has(event.id))
|
||||||
|
let newMessagesFiltered = []
|
||||||
|
|
||||||
for (let i = 0; i < newMessages.length; i++) {
|
for (let i = 0; i < newMessages.length; i++) {
|
||||||
this.messagesSet.add(newMessages[i].id)
|
// await newMessages.forEach(async (event) => {
|
||||||
newMessages[i].text = await this.getPlaintext(newMessages[i])
|
let event = newMessages[i]
|
||||||
this.interpolateMessageMentions(newMessages[i])
|
if (this.messagesSet.has(event.id)) return
|
||||||
if (newMessages[i].tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(newMessages[i])
|
|
||||||
if (newMessages[i].appended) {
|
this.messagesSet.add(event.id)
|
||||||
for (let j = 0; j < newMessages[i].appended.length; j++) {
|
event.text = await this.getPlaintext(event)
|
||||||
this.messagesSet.add(newMessages[i].appended[j].id)
|
this.interpolateMessageMentions(event)
|
||||||
newMessages[i].appended[j].text = await this.getPlaintext(newMessages[i].appended[j])
|
if (event.tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(event)
|
||||||
this.interpolateMessageMentions(newMessages[i].appended[j])
|
if (event.appended) {
|
||||||
if (newMessages[i].appended[j].tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(newMessages[i].appended[j])
|
for (let j = 0; j < event.appended.length; j++) {
|
||||||
|
this.messagesSet.add(event.appended[j].id)
|
||||||
|
event.appended[j].text = await this.getPlaintext(event.appended[j])
|
||||||
|
this.interpolateMessageMentions(event.appended[j])
|
||||||
|
if (event.appended[j].tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(event.appended[j])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newMessagesFiltered.push(event)
|
||||||
}
|
}
|
||||||
|
// this.messagesSet.add(newMessages[i].id)
|
||||||
|
// newMessages[i].text = await this.getPlaintext(newMessages[i])
|
||||||
|
// this.interpolateMessageMentions(newMessages[i])
|
||||||
|
// if (newMessages[i].tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(newMessages[i])
|
||||||
|
// if (newMessages[i].appended) {
|
||||||
|
// for (let j = 0; j < newMessages[i].appended.length; j++) {
|
||||||
|
// this.messagesSet.add(newMessages[i].appended[j].id)
|
||||||
|
// newMessages[i].appended[j].text = await this.getPlaintext(newMessages[i].appended[j])
|
||||||
|
// this.interpolateMessageMentions(newMessages[i].appended[j])
|
||||||
|
// if (newMessages[i].appended[j].tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(newMessages[i].appended[j])
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (newMessages.length === 0) {
|
// this.messages = newMessages.concat(this.messages)
|
||||||
this.canLoadMore = false
|
this.messages = newMessagesFiltered.concat(this.messages)
|
||||||
}
|
|
||||||
|
|
||||||
this.messages = newMessages.concat(this.messages)
|
|
||||||
done(!this.canLoadMore)
|
done(!this.canLoadMore)
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -354,7 +341,45 @@ export default {
|
|||||||
}, datestamps[0].innerText)
|
}, datestamps[0].innerText)
|
||||||
// console.log(messageScroll.scrollHeight, messageScroll.clientHeight, messageScroll.scrollTop)
|
// console.log(messageScroll.scrollHeight, messageScroll.clientHeight, messageScroll.scrollTop)
|
||||||
// console.log('currentDatestamp', this.currentDatestamp)
|
// console.log('currentDatestamp', this.currentDatestamp)
|
||||||
}
|
},
|
||||||
|
|
||||||
|
async addMessage(event) {
|
||||||
|
if (this.messagesSet.has(event.id)) return
|
||||||
|
this.messagesSet.add(event.id)
|
||||||
|
|
||||||
|
await this.lock()
|
||||||
|
event.text = await this.getPlaintext(event)
|
||||||
|
this.unlock()
|
||||||
|
this.interpolateMessageMentions(event)
|
||||||
|
if (event.tags.filter(([t, v]) => t === 'e' && v).length) this.processTaggedEvents(event)
|
||||||
|
|
||||||
|
let messageScroll = this.$refs.messageScroll
|
||||||
|
let scrollToBottom = 100 > Math.abs((messageScroll.scrollHeight - messageScroll.clientHeight) - messageScroll.scrollTop) ||
|
||||||
|
messageScroll.scrollHeight === messageScroll.clientHeight
|
||||||
|
|
||||||
|
if (this.messages.length === 0) {
|
||||||
|
this.messages.push(event)
|
||||||
|
} else {
|
||||||
|
let last = this.messages[this.messages.length - 1]
|
||||||
|
if (
|
||||||
|
event.pubkey === this.$store.state.keys.pub &&
|
||||||
|
last.pubkey === event.pubkey &&
|
||||||
|
last.created_at + 120 >= event.created_at
|
||||||
|
) {
|
||||||
|
last.appended = last.appended || []
|
||||||
|
last.appended.push(event)
|
||||||
|
} else {
|
||||||
|
this.messages.push(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrollToBottom) {
|
||||||
|
this.$store.commit('haveReadMessage', this.$route.params.pubkey)
|
||||||
|
this.scrollToBottom()
|
||||||
|
} else if (event.pubkey === this.$route.params.pubkey) {
|
||||||
|
this.unreadMessagesSet.add(event.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -90,9 +90,11 @@ export default {
|
|||||||
if (loadedNotifications.length < 40) {
|
if (loadedNotifications.length < 40) {
|
||||||
this.reachedEnd = true
|
this.reachedEnd = true
|
||||||
}
|
}
|
||||||
loadedNotifications = loadedNotifications.filter(event => !this.notificationsSet.has(event.id))
|
// loadedNotifications = loadedNotifications.filter(event => !this.notificationsSet.has(event.id))
|
||||||
this.interpolateEventMentions(loadedNotifications)
|
this.interpolateEventMentions(loadedNotifications)
|
||||||
loadedNotifications.forEach(event => {
|
loadedNotifications.forEach(event => {
|
||||||
|
if (this.notificationsSet.has(event.id)) return
|
||||||
|
|
||||||
this.notificationsSet.add(event.id)
|
this.notificationsSet.add(event.id)
|
||||||
this.addNotificationEvent(event)
|
this.addNotificationEvent(event)
|
||||||
this.$store.dispatch('useProfile', {pubkey: event.pubkey, request: true})
|
this.$store.dispatch('useProfile', {pubkey: event.pubkey, request: true})
|
||||||
|
@ -376,27 +376,50 @@ export async function publishContactList(store) {
|
|||||||
var tags = event?.tags || []
|
var tags = event?.tags || []
|
||||||
|
|
||||||
// remove contacts that we're not following anymore
|
// remove contacts that we're not following anymore
|
||||||
tags = tags.filter(
|
// tags = tags.filter(
|
||||||
([t, v]) => t === 'p' && store.state.following.find(f => f === v)
|
// ([t, v]) => t === 'p' && store.state.following.find(f => f === v)
|
||||||
)
|
// )
|
||||||
|
|
||||||
// now we merely add to the existing event because it might contain more data in the
|
// check existing event because it might contain more data in the
|
||||||
// tags that we don't want to replace
|
// tags that we don't want to replace, if so push existing event tag,
|
||||||
|
// else push state.following tag
|
||||||
|
let newTags = []
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
store.state.following.map(async pubkey => {
|
store.state.following.map(async pubkey => {
|
||||||
if (!tags.find(([t, v]) => t === 'p' && v === pubkey)) {
|
let index = tags.findIndex(([t, v]) => t === 'p' && v === pubkey)
|
||||||
tags.push(await getPubKeyTagWithRelay(pubkey))
|
if (index >= 0) {
|
||||||
|
newTags.push(tags[index])
|
||||||
|
} else {
|
||||||
|
newTags.push(await getPubKeyTagWithRelay(pubkey))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
// now we merely add to the existing event because it might contain more data in the
|
||||||
|
// tags that we don't want to replace
|
||||||
|
// await Promise.all(
|
||||||
|
// store.state.following.map(async pubkey => {
|
||||||
|
// if (!tags.find(([t, v]) => t === 'p' && v === pubkey)) {
|
||||||
|
// tags.push(await getPubKeyTagWithRelay(pubkey))
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
|
||||||
|
// event = {
|
||||||
|
// pubkey: store.state.keys.pub,
|
||||||
|
// created_at: Math.floor(Date.now() / 1000),
|
||||||
|
// kind: 3,
|
||||||
|
// tags,
|
||||||
|
// newTags,
|
||||||
|
// content: JSON.stringify(store.state.relays)
|
||||||
|
// }
|
||||||
event = await pool.publish({
|
event = await pool.publish({
|
||||||
pubkey: store.state.keys.pub,
|
pubkey: store.state.keys.pub,
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
kind: 3,
|
kind: 3,
|
||||||
tags,
|
tags: newTags,
|
||||||
content: JSON.stringify(store.state.relays)
|
content: JSON.stringify(store.state.relays)
|
||||||
})
|
})
|
||||||
|
console.log(event)
|
||||||
|
|
||||||
await store.dispatch('addEvent', {event})
|
await store.dispatch('addEvent', {event})
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ export default function (store) {
|
|||||||
case 'setRelayOpt':
|
case 'setRelayOpt':
|
||||||
case 'follow':
|
case 'follow':
|
||||||
case 'unfollow':
|
case 'unfollow':
|
||||||
|
case 'reorderFollows':
|
||||||
// make an event kind3 and publish it
|
// make an event kind3 and publish it
|
||||||
store.dispatch('publishContactList')
|
store.dispatch('publishContactList')
|
||||||
break
|
break
|
||||||
|
@ -66,6 +66,10 @@ export function unfollow(state, key) {
|
|||||||
if (idx >= 0) state.following.splice(idx, 1)
|
if (idx >= 0) state.following.splice(idx, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function reorderFollows(state, following) {
|
||||||
|
state.following = following
|
||||||
|
}
|
||||||
|
|
||||||
export function addProfileToCache(
|
export function addProfileToCache(
|
||||||
state,
|
state,
|
||||||
{pubkey, name, about, picture, nip05}
|
{pubkey, name, about, picture, nip05}
|
||||||
|
@ -66,7 +66,7 @@ export async function processMentions(event) {
|
|||||||
export async function extractMentions(text, tags) {
|
export async function extractMentions(text, tags) {
|
||||||
// const mentionRegex = /\B@(?<p>[a-f0-9]{64})\b/g
|
// const mentionRegex = /\B@(?<p>[a-f0-9]{64})\b/g
|
||||||
// const mentionRegex = /@((?<t>[a-z]{1}):{1})?(?<p>[a-f0-9]{64})\b/g
|
// const mentionRegex = /@((?<t>[a-z]{1}):{1})?(?<p>[a-f0-9]{64})\b/g
|
||||||
const mentionRegex = /(?<t>[@&]{1})(?<p>[a-f0-9]{64})/g
|
const mentionRegex = /(?<t>[@&]{1})(?<p>[a-f0-9]{64})\b/g
|
||||||
|
|
||||||
let tagIndexMap = {}
|
let tagIndexMap = {}
|
||||||
// event.tags.filter(([t, v]) => (t === 'p' || t === 'e') && v).forEach(([t, v], index) => tagIndexMap[v] = index)
|
// event.tags.filter(([t, v]) => (t === 'p' || t === 'e') && v).forEach(([t, v], index) => tagIndexMap[v] = index)
|
||||||
|
@ -144,7 +144,7 @@ export default {
|
|||||||
|
|
||||||
menuItemTemplate: item => {
|
menuItemTemplate: item => {
|
||||||
return `
|
return `
|
||||||
<div class="flex row no-wrap items-center" style="gap: .2rem;">
|
<div class="flex row no-wrap items-center" style="gap: .2rem; width: 100%;">
|
||||||
<div style="border-radius: 10px">
|
<div style="border-radius: 10px">
|
||||||
<img src=${this.$store.getters.avatar(item.original.value.pubkey)} style="object-fit: cover; height: 1.5rem; width: 1.5rem;"/>
|
<img src=${this.$store.getters.avatar(item.original.value.pubkey)} style="object-fit: cover; height: 1.5rem; width: 1.5rem;"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -350,7 +350,7 @@ const methods = {
|
|||||||
startkey: [ourPubKey, {}],
|
startkey: [ourPubKey, {}],
|
||||||
endkey: [ourPubKey, since]
|
endkey: [ourPubKey, since]
|
||||||
})
|
})
|
||||||
return result.rows.length
|
return result.rows.filter((v, i, a) => a.indexOf(v) === i).length
|
||||||
},
|
},
|
||||||
|
|
||||||
async dbGetUnreadMessages(pubkey, since) {
|
async dbGetUnreadMessages(pubkey, since) {
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -6141,6 +6141,11 @@ sockjs@^0.3.21:
|
|||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
websocket-driver "^0.7.4"
|
websocket-driver "^0.7.4"
|
||||||
|
|
||||||
|
sortablejs@1.14.0:
|
||||||
|
version "1.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
|
||||||
|
integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==
|
||||||
|
|
||||||
source-list-map@^2.0.0:
|
source-list-map@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||||
@ -6785,6 +6790,13 @@ vue@^3.0.0:
|
|||||||
"@vue/server-renderer" "3.2.36"
|
"@vue/server-renderer" "3.2.36"
|
||||||
"@vue/shared" "3.2.36"
|
"@vue/shared" "3.2.36"
|
||||||
|
|
||||||
|
vuedraggable@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270"
|
||||||
|
integrity sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==
|
||||||
|
dependencies:
|
||||||
|
sortablejs "1.14.0"
|
||||||
|
|
||||||
vuex@^4.0.1:
|
vuex@^4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.2.tgz#f896dbd5bf2a0e963f00c67e9b610de749ccacc9"
|
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.2.tgz#f896dbd5bf2a0e963f00c67e9b610de749ccacc9"
|
||||||
|
Loading…
Reference in New Issue
Block a user