Merge branch 'rc/2.1.2' into 'master'
Merge 2.1.2 to MASTER See merge request pleroma/pleroma-fe!1236
This commit is contained in:
commit
b225c3578f
|
@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
## [Unreleased patch]
|
||||||
|
|
||||||
|
## [2.1.2] - 2020-09-17
|
||||||
|
### Fixed
|
||||||
|
- Fixed chats list not updating its order when new messages come in
|
||||||
|
- Fixed chat messages sometimes getting lost when you receive a message at the same time
|
||||||
|
|
||||||
|
|
||||||
## [2.1.1] - 2020-09-08
|
## [2.1.1] - 2020-09-08
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -13,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Autocomplete won't stop at the second @, so it'll still work with "@lain@l" and not start over.
|
- Autocomplete won't stop at the second @, so it'll still work with "@lain@l" and not start over.
|
||||||
- Fixed weird autocomplete behavior when you write ":custom_emoji: ?"
|
- Fixed weird autocomplete behavior when you write ":custom_emoji: ?"
|
||||||
|
|
||||||
|
|
||||||
## [2.1.0] - 2020-08-28
|
## [2.1.0] - 2020-08-28
|
||||||
### Added
|
### Added
|
||||||
- Autocomplete domains from list of known instances
|
- Autocomplete domains from list of known instances
|
||||||
|
|
|
@ -204,9 +204,9 @@ const Chat = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
readChat () {
|
readChat () {
|
||||||
if (!(this.currentChatMessageService && this.currentChatMessageService.lastMessage)) { return }
|
if (!(this.currentChatMessageService && this.currentChatMessageService.maxId)) { return }
|
||||||
if (document.hidden) { return }
|
if (document.hidden) { return }
|
||||||
const lastReadId = this.currentChatMessageService.lastMessage.id
|
const lastReadId = this.currentChatMessageService.maxId
|
||||||
this.$store.dispatch('readChat', { id: this.currentChat.id, lastReadId })
|
this.$store.dispatch('readChat', { id: this.currentChat.id, lastReadId })
|
||||||
},
|
},
|
||||||
bottomedOut (offset) {
|
bottomedOut (offset) {
|
||||||
|
@ -244,7 +244,7 @@ const Chat = {
|
||||||
|
|
||||||
const chatId = chatMessageService.chatId
|
const chatId = chatMessageService.chatId
|
||||||
const fetchOlderMessages = !!maxId
|
const fetchOlderMessages = !!maxId
|
||||||
const sinceId = fetchLatest && chatMessageService.lastMessage && chatMessageService.lastMessage.id
|
const sinceId = fetchLatest && chatMessageService.maxId
|
||||||
|
|
||||||
this.backendInteractor.chatMessages({ id: chatId, maxId, sinceId })
|
this.backendInteractor.chatMessages({ id: chatId, maxId, sinceId })
|
||||||
.then((messages) => {
|
.then((messages) => {
|
||||||
|
@ -303,7 +303,11 @@ const Chat = {
|
||||||
|
|
||||||
return this.backendInteractor.sendChatMessage(params)
|
return this.backendInteractor.sendChatMessage(params)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.$store.dispatch('addChatMessages', { chatId: this.currentChat.id, messages: [data] }).then(() => {
|
this.$store.dispatch('addChatMessages', {
|
||||||
|
chatId: this.currentChat.id,
|
||||||
|
messages: [data],
|
||||||
|
updateMaxId: false
|
||||||
|
}).then(() => {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.handleResize()
|
this.handleResize()
|
||||||
// When the posting form size changes because of a media attachment, we need an extra resize
|
// When the posting form size changes because of a media attachment, we need an extra resize
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
@click.stop.prevent="togglePanel"
|
@click.stop.prevent="togglePanel"
|
||||||
>
|
>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="icon-comment-empty" />
|
<i class="icon-megaphone" />
|
||||||
{{ $t('shoutbox.title') }}
|
{{ $t('shoutbox.title') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,13 +39,16 @@
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
large: {
|
large: {
|
||||||
required: false
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
},
|
},
|
||||||
// TODO: Make theme switcher compute theme initially so that contrast
|
// TODO: Make theme switcher compute theme initially so that contrast
|
||||||
// component won't be called without contrast data
|
// component won't be called without contrast data
|
||||||
contrast: {
|
contrast: {
|
||||||
required: false,
|
required: false,
|
||||||
type: Object
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<span class="result-percentage">
|
<span class="result-percentage">
|
||||||
{{ percentageForOption(option.votes_count) }}%
|
{{ percentageForOption(option.votes_count) }}%
|
||||||
</span>
|
</span>
|
||||||
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
<span v-html="option.title_html" />
|
<span v-html="option.title_html" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -278,7 +278,7 @@
|
||||||
/>
|
/>
|
||||||
<ContrastRatio
|
<ContrastRatio
|
||||||
:contrast="previewContrast.alertErrorText"
|
:contrast="previewContrast.alertErrorText"
|
||||||
large="true"
|
large
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="alertWarningColorLocal"
|
v-model="alertWarningColorLocal"
|
||||||
|
@ -294,7 +294,7 @@
|
||||||
/>
|
/>
|
||||||
<ContrastRatio
|
<ContrastRatio
|
||||||
:contrast="previewContrast.alertWarningText"
|
:contrast="previewContrast.alertWarningText"
|
||||||
large="true"
|
large
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="alertNeutralColorLocal"
|
v-model="alertNeutralColorLocal"
|
||||||
|
@ -310,7 +310,7 @@
|
||||||
/>
|
/>
|
||||||
<ContrastRatio
|
<ContrastRatio
|
||||||
:contrast="previewContrast.alertNeutralText"
|
:contrast="previewContrast.alertNeutralText"
|
||||||
large="true"
|
large
|
||||||
/>
|
/>
|
||||||
<OpacityInput
|
<OpacityInput
|
||||||
v-model="alertOpacityLocal"
|
v-model="alertOpacityLocal"
|
||||||
|
@ -334,7 +334,7 @@
|
||||||
/>
|
/>
|
||||||
<ContrastRatio
|
<ContrastRatio
|
||||||
:contrast="previewContrast.badgeNotificationText"
|
:contrast="previewContrast.badgeNotificationText"
|
||||||
large="true"
|
large
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="color-item">
|
<div class="color-item">
|
||||||
|
@ -359,7 +359,7 @@
|
||||||
/>
|
/>
|
||||||
<ContrastRatio
|
<ContrastRatio
|
||||||
:contrast="previewContrast.panelText"
|
:contrast="previewContrast.panelText"
|
||||||
large="true"
|
large
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="panelLinkColorLocal"
|
v-model="panelLinkColorLocal"
|
||||||
|
@ -369,7 +369,7 @@
|
||||||
/>
|
/>
|
||||||
<ContrastRatio
|
<ContrastRatio
|
||||||
:contrast="previewContrast.panelLink"
|
:contrast="previewContrast.panelLink"
|
||||||
large="true"
|
large
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="color-item">
|
<div class="color-item">
|
||||||
|
@ -740,57 +740,57 @@
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatBgColorLocal"
|
v-model="chatBgColorLocal"
|
||||||
name="chatBgColor"
|
name="chatBgColor"
|
||||||
:fallback="previewTheme.colors.bg || 1"
|
:fallback="previewTheme.colors.bg"
|
||||||
:label="$t('settings.background')"
|
:label="$t('settings.background')"
|
||||||
/>
|
/>
|
||||||
<h5>{{ $t('settings.style.advanced_colors.chat.incoming') }}</h5>
|
<h5>{{ $t('settings.style.advanced_colors.chat.incoming') }}</h5>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageIncomingBgColorLocal"
|
v-model="chatMessageIncomingBgColorLocal"
|
||||||
name="chatMessageIncomingBgColor"
|
name="chatMessageIncomingBgColor"
|
||||||
:fallback="previewTheme.colors.bg || 1"
|
:fallback="previewTheme.colors.bg"
|
||||||
:label="$t('settings.background')"
|
:label="$t('settings.background')"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageIncomingTextColorLocal"
|
v-model="chatMessageIncomingTextColorLocal"
|
||||||
name="chatMessageIncomingTextColor"
|
name="chatMessageIncomingTextColor"
|
||||||
:fallback="previewTheme.colors.text || 1"
|
:fallback="previewTheme.colors.text"
|
||||||
:label="$t('settings.text')"
|
:label="$t('settings.text')"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageIncomingLinkColorLocal"
|
v-model="chatMessageIncomingLinkColorLocal"
|
||||||
name="chatMessageIncomingLinkColor"
|
name="chatMessageIncomingLinkColor"
|
||||||
:fallback="previewTheme.colors.link || 1"
|
:fallback="previewTheme.colors.link"
|
||||||
:label="$t('settings.links')"
|
:label="$t('settings.links')"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageIncomingBorderColorLocal"
|
v-model="chatMessageIncomingBorderColorLocal"
|
||||||
name="chatMessageIncomingBorderLinkColor"
|
name="chatMessageIncomingBorderLinkColor"
|
||||||
:fallback="previewTheme.colors.fg || 1"
|
:fallback="previewTheme.colors.fg"
|
||||||
:label="$t('settings.style.advanced_colors.chat.border')"
|
:label="$t('settings.style.advanced_colors.chat.border')"
|
||||||
/>
|
/>
|
||||||
<h5>{{ $t('settings.style.advanced_colors.chat.outgoing') }}</h5>
|
<h5>{{ $t('settings.style.advanced_colors.chat.outgoing') }}</h5>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageOutgoingBgColorLocal"
|
v-model="chatMessageOutgoingBgColorLocal"
|
||||||
name="chatMessageOutgoingBgColor"
|
name="chatMessageOutgoingBgColor"
|
||||||
:fallback="previewTheme.colors.bg || 1"
|
:fallback="previewTheme.colors.bg"
|
||||||
:label="$t('settings.background')"
|
:label="$t('settings.background')"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageOutgoingTextColorLocal"
|
v-model="chatMessageOutgoingTextColorLocal"
|
||||||
name="chatMessageOutgoingTextColor"
|
name="chatMessageOutgoingTextColor"
|
||||||
:fallback="previewTheme.colors.text || 1"
|
:fallback="previewTheme.colors.text"
|
||||||
:label="$t('settings.text')"
|
:label="$t('settings.text')"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageOutgoingLinkColorLocal"
|
v-model="chatMessageOutgoingLinkColorLocal"
|
||||||
name="chatMessageOutgoingLinkColor"
|
name="chatMessageOutgoingLinkColor"
|
||||||
:fallback="previewTheme.colors.link || 1"
|
:fallback="previewTheme.colors.link"
|
||||||
:label="$t('settings.links')"
|
:label="$t('settings.links')"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="chatMessageOutgoingBorderColorLocal"
|
v-model="chatMessageOutgoingBorderColorLocal"
|
||||||
name="chatMessageOutgoingBorderLinkColor"
|
name="chatMessageOutgoingBorderLinkColor"
|
||||||
:fallback="previewTheme.colors.bg || 1"
|
:fallback="previewTheme.colors.bg"
|
||||||
:label="$t('settings.style.advanced_colors.chat.border')"
|
:label="$t('settings.style.advanced_colors.chat.border')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'chat' }">
|
<router-link :to="{ name: 'chat' }">
|
||||||
<i class="button-icon icon-chat" /> {{ $t("nav.chat") }}
|
<i class="button-icon icon-megaphone" /> {{ $t("shoutbox.title") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -113,7 +113,6 @@
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"administration": "Administration",
|
"administration": "Administration",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"chat": "Local Chat",
|
|
||||||
"friend_requests": "Follow Requests",
|
"friend_requests": "Follow Requests",
|
||||||
"mentions": "Mentions",
|
"mentions": "Mentions",
|
||||||
"interactions": "Interactions",
|
"interactions": "Interactions",
|
||||||
|
|
|
@ -143,6 +143,7 @@ const chats = {
|
||||||
const isNewMessage = (chat.lastMessage && chat.lastMessage.id) !== (updatedChat.lastMessage && updatedChat.lastMessage.id)
|
const isNewMessage = (chat.lastMessage && chat.lastMessage.id) !== (updatedChat.lastMessage && updatedChat.lastMessage.id)
|
||||||
chat.lastMessage = updatedChat.lastMessage
|
chat.lastMessage = updatedChat.lastMessage
|
||||||
chat.unread = updatedChat.unread
|
chat.unread = updatedChat.unread
|
||||||
|
chat.updated_at = updatedChat.updated_at
|
||||||
if (isNewMessage && chat.unread) {
|
if (isNewMessage && chat.unread) {
|
||||||
newChatMessageSideEffects(updatedChat)
|
newChatMessageSideEffects(updatedChat)
|
||||||
}
|
}
|
||||||
|
@ -181,30 +182,16 @@ const chats = {
|
||||||
setChatsLoading (state, { value }) {
|
setChatsLoading (state, { value }) {
|
||||||
state.chats.loading = value
|
state.chats.loading = value
|
||||||
},
|
},
|
||||||
addChatMessages (state, { commit, chatId, messages }) {
|
addChatMessages (state, { chatId, messages, updateMaxId }) {
|
||||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
const chatMessageService = state.openedChatMessageServices[chatId]
|
||||||
if (chatMessageService) {
|
if (chatMessageService) {
|
||||||
chatService.add(chatMessageService, { messages: messages.map(parseChatMessage) })
|
chatService.add(chatMessageService, { messages: messages.map(parseChatMessage), updateMaxId })
|
||||||
commit('refreshLastMessage', { chatId })
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
refreshLastMessage (state, { chatId }) {
|
deleteChatMessage (state, { chatId, messageId }) {
|
||||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
|
||||||
if (chatMessageService) {
|
|
||||||
const chat = getChatById(state, chatId)
|
|
||||||
if (chat) {
|
|
||||||
chat.lastMessage = chatMessageService.lastMessage
|
|
||||||
if (chatMessageService.lastMessage) {
|
|
||||||
chat.updated_at = chatMessageService.lastMessage.created_at
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deleteChatMessage (state, { commit, chatId, messageId }) {
|
|
||||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
const chatMessageService = state.openedChatMessageServices[chatId]
|
||||||
if (chatMessageService) {
|
if (chatMessageService) {
|
||||||
chatService.deleteMessage(chatMessageService, messageId)
|
chatService.deleteMessage(chatMessageService, messageId)
|
||||||
commit('refreshLastMessage', { chatId })
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetChatNewMessageCount (state, _value) {
|
resetChatNewMessageCount (state, _value) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ const empty = (chatId) => {
|
||||||
lastSeenTimestamp: 0,
|
lastSeenTimestamp: 0,
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
minId: undefined,
|
minId: undefined,
|
||||||
lastMessage: undefined
|
maxId: undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ const clear = (storage) => {
|
||||||
storage.newMessageCount = 0
|
storage.newMessageCount = 0
|
||||||
storage.lastSeenTimestamp = 0
|
storage.lastSeenTimestamp = 0
|
||||||
storage.minId = undefined
|
storage.minId = undefined
|
||||||
storage.lastMessage = undefined
|
storage.maxId = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteMessage = (storage, messageId) => {
|
const deleteMessage = (storage, messageId) => {
|
||||||
|
@ -26,8 +26,9 @@ const deleteMessage = (storage, messageId) => {
|
||||||
storage.messages = storage.messages.filter(m => m.id !== messageId)
|
storage.messages = storage.messages.filter(m => m.id !== messageId)
|
||||||
delete storage.idIndex[messageId]
|
delete storage.idIndex[messageId]
|
||||||
|
|
||||||
if (storage.lastMessage && (storage.lastMessage.id === messageId)) {
|
if (storage.maxId === messageId) {
|
||||||
storage.lastMessage = _.maxBy(storage.messages, 'id')
|
const lastMessage = _.maxBy(storage.messages, 'id')
|
||||||
|
storage.maxId = lastMessage.id
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storage.minId === messageId) {
|
if (storage.minId === messageId) {
|
||||||
|
@ -36,7 +37,7 @@ const deleteMessage = (storage, messageId) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const add = (storage, { messages: newMessages }) => {
|
const add = (storage, { messages: newMessages, updateMaxId = true }) => {
|
||||||
if (!storage) { return }
|
if (!storage) { return }
|
||||||
for (let i = 0; i < newMessages.length; i++) {
|
for (let i = 0; i < newMessages.length; i++) {
|
||||||
const message = newMessages[i]
|
const message = newMessages[i]
|
||||||
|
@ -48,8 +49,10 @@ const add = (storage, { messages: newMessages }) => {
|
||||||
storage.minId = message.id
|
storage.minId = message.id
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!storage.lastMessage || message.id > storage.lastMessage.id) {
|
if (!storage.maxId || message.id > storage.maxId) {
|
||||||
storage.lastMessage = message
|
if (updateMaxId) {
|
||||||
|
storage.maxId = message.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!storage.idIndex[message.id]) {
|
if (!storage.idIndex[message.id]) {
|
||||||
|
|
|
@ -405,6 +405,12 @@
|
||||||
"css": "block",
|
"css": "block",
|
||||||
"code": 59434,
|
"code": 59434,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3e674995cacc2b09692c096ea7eb6165",
|
||||||
|
"css": "megaphone",
|
||||||
|
"code": 59435,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -33,12 +33,12 @@ describe('chatService', () => {
|
||||||
const chat = chatService.empty()
|
const chat = chatService.empty()
|
||||||
|
|
||||||
chatService.add(chat, { messages: [ message1 ] })
|
chatService.add(chat, { messages: [ message1 ] })
|
||||||
expect(chat.lastMessage.id).to.eql(message1.id)
|
expect(chat.maxId).to.eql(message1.id)
|
||||||
expect(chat.minId).to.eql(message1.id)
|
expect(chat.minId).to.eql(message1.id)
|
||||||
expect(chat.newMessageCount).to.eql(1)
|
expect(chat.newMessageCount).to.eql(1)
|
||||||
|
|
||||||
chatService.add(chat, { messages: [ message2 ] })
|
chatService.add(chat, { messages: [ message2 ] })
|
||||||
expect(chat.lastMessage.id).to.eql(message2.id)
|
expect(chat.maxId).to.eql(message2.id)
|
||||||
expect(chat.minId).to.eql(message1.id)
|
expect(chat.minId).to.eql(message1.id)
|
||||||
expect(chat.newMessageCount).to.eql(2)
|
expect(chat.newMessageCount).to.eql(2)
|
||||||
|
|
||||||
|
@ -60,15 +60,15 @@ describe('chatService', () => {
|
||||||
chatService.add(chat, { messages: [ message2 ] })
|
chatService.add(chat, { messages: [ message2 ] })
|
||||||
chatService.add(chat, { messages: [ message3 ] })
|
chatService.add(chat, { messages: [ message3 ] })
|
||||||
|
|
||||||
expect(chat.lastMessage.id).to.eql(message3.id)
|
expect(chat.maxId).to.eql(message3.id)
|
||||||
expect(chat.minId).to.eql(message1.id)
|
expect(chat.minId).to.eql(message1.id)
|
||||||
|
|
||||||
chatService.deleteMessage(chat, message3.id)
|
chatService.deleteMessage(chat, message3.id)
|
||||||
expect(chat.lastMessage.id).to.eql(message2.id)
|
expect(chat.maxId).to.eql(message2.id)
|
||||||
expect(chat.minId).to.eql(message1.id)
|
expect(chat.minId).to.eql(message1.id)
|
||||||
|
|
||||||
chatService.deleteMessage(chat, message1.id)
|
chatService.deleteMessage(chat, message1.id)
|
||||||
expect(chat.lastMessage.id).to.eql(message2.id)
|
expect(chat.maxId).to.eql(message2.id)
|
||||||
expect(chat.minId).to.eql(message2.id)
|
expect(chat.minId).to.eql(message2.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue