serverless-ircd 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +6 -1
- package/.gitmodules +3 -0
- package/CHANGELOG.md +214 -0
- package/README.md +110 -23
- package/apps/aws-stack/package.json +1 -1
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/src/origin-allowlist.ts +99 -0
- package/apps/cf-worker/src/worker.ts +89 -9
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +18 -0
- package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
- package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
- package/apps/cf-worker/tests/smoke.test.ts +28 -2
- package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
- package/apps/cf-worker/wrangler.test.toml +23 -0
- package/apps/cf-worker/wrangler.toml +54 -0
- package/apps/local-cli/package.json +1 -1
- package/apps/web/landing/index.html +217 -0
- package/apps/web/package.json +28 -0
- package/apps/web/scripts/build.mjs +125 -0
- package/apps/web/src/build-env.ts +43 -0
- package/apps/web/src/config-schema.ts +138 -0
- package/apps/web/static/config.json +28 -0
- package/apps/web/static/config.prod.json +28 -0
- package/apps/web/static/config.staging.json +28 -0
- package/apps/web/tests/build-env.test.ts +63 -0
- package/apps/web/tests/build-smoke.test.ts +103 -0
- package/apps/web/tests/config-schema.test.ts +432 -0
- package/apps/web/tests/workspace.test.ts +12 -0
- package/apps/web/tsconfig.json +10 -0
- package/apps/web/upstream/.browserslistrc +3 -0
- package/apps/web/upstream/.editorconfig +9 -0
- package/apps/web/upstream/.eslintignore +3 -0
- package/apps/web/upstream/.eslintrc.js +90 -0
- package/apps/web/upstream/.github/FUNDING.yml +12 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/apps/web/upstream/.github/workflows/push.yml +26 -0
- package/apps/web/upstream/.github/workflows/release.yml +33 -0
- package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
- package/apps/web/upstream/.prettierrc.js +9 -0
- package/apps/web/upstream/.stylelintignore +5 -0
- package/apps/web/upstream/.stylelintrc.js +21 -0
- package/apps/web/upstream/LICENSE +201 -0
- package/apps/web/upstream/README.md +76 -0
- package/apps/web/upstream/babel.config.js +25 -0
- package/apps/web/upstream/crowdin.yml +3 -0
- package/apps/web/upstream/index.html +35 -0
- package/apps/web/upstream/jest.config.js +7 -0
- package/apps/web/upstream/jsdoc.json +22 -0
- package/apps/web/upstream/package.json +80 -0
- package/apps/web/upstream/scripts/pre-commit +43 -0
- package/apps/web/upstream/src/components/App.vue +516 -0
- package/apps/web/upstream/src/components/AppSettings.vue +587 -0
- package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
- package/apps/web/upstream/src/components/Avatar.vue +105 -0
- package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
- package/apps/web/upstream/src/components/BufferKey.vue +121 -0
- package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
- package/apps/web/upstream/src/components/Captcha.vue +85 -0
- package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
- package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
- package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
- package/apps/web/upstream/src/components/ChannelList.vue +345 -0
- package/apps/web/upstream/src/components/Container.vue +401 -0
- package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
- package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
- package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
- package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
- package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
- package/apps/web/upstream/src/components/MessageList.vue +1028 -0
- package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
- package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
- package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
- package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
- package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
- package/apps/web/upstream/src/components/Nicklist.vue +309 -0
- package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
- package/apps/web/upstream/src/components/NotConnected.vue +271 -0
- package/apps/web/upstream/src/components/SelfUser.vue +283 -0
- package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
- package/apps/web/upstream/src/components/ServerView.vue +126 -0
- package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
- package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
- package/apps/web/upstream/src/components/Sidebar.vue +429 -0
- package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
- package/apps/web/upstream/src/components/SidebarState.vue +145 -0
- package/apps/web/upstream/src/components/StartupError.vue +35 -0
- package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
- package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
- package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
- package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
- package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
- package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
- package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
- package/apps/web/upstream/src/components/UserBox.vue +728 -0
- package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
- package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
- package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
- package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
- package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
- package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
- package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
- package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
- package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
- package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
- package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
- package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
- package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
- package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
- package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
- package/apps/web/upstream/src/helpers/Colours.js +128 -0
- package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
- package/apps/web/upstream/src/helpers/Md5.js +193 -0
- package/apps/web/upstream/src/helpers/Misc.js +401 -0
- package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
- package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
- package/apps/web/upstream/src/libs/AudioManager.js +60 -0
- package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
- package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
- package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
- package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
- package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
- package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
- package/apps/web/upstream/src/libs/IPC.js +51 -0
- package/apps/web/upstream/src/libs/InputHandler.js +921 -0
- package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
- package/apps/web/upstream/src/libs/Logger.js +71 -0
- package/apps/web/upstream/src/libs/Message.js +169 -0
- package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
- package/apps/web/upstream/src/libs/MessageParser.js +249 -0
- package/apps/web/upstream/src/libs/Notifications.js +78 -0
- package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
- package/apps/web/upstream/src/libs/ServerSession.js +102 -0
- package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
- package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
- package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
- package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
- package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
- package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
- package/apps/web/upstream/src/libs/bufferTools.js +177 -0
- package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
- package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
- package/apps/web/upstream/src/libs/settingTools.js +31 -0
- package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
- package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
- package/apps/web/upstream/src/libs/state/UserState.js +89 -0
- package/apps/web/upstream/src/libs/state/common.js +26 -0
- package/apps/web/upstream/src/libs/state.js +961 -0
- package/apps/web/upstream/src/libs/storage/Local.js +51 -0
- package/apps/web/upstream/src/main.js +566 -0
- package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
- package/apps/web/upstream/src/res/configTemplates.js +373 -0
- package/apps/web/upstream/src/res/globalStyle.css +277 -0
- package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
- package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
- package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
- package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
- package/apps/web/upstream/src/res/localesList.json +27 -0
- package/apps/web/upstream/src/res/logo.png +0 -0
- package/apps/web/upstream/src/thirdparty/about.html +43 -0
- package/apps/web/upstream/src/thirdparty/index.js +5 -0
- package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
- package/apps/web/upstream/static/config.json +28 -0
- package/apps/web/upstream/static/emoticons/smile.png +0 -0
- package/apps/web/upstream/static/favicon.png +0 -0
- package/apps/web/upstream/static/highlight.mp3 +0 -0
- package/apps/web/upstream/static/highlight.ogg +0 -0
- package/apps/web/upstream/static/locales/.gitignore +2 -0
- package/apps/web/upstream/static/plugins/customise.html.example +10 -0
- package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
- package/apps/web/upstream/static/themes/common/base.css +1064 -0
- package/apps/web/upstream/static/themes/dark/theme.css +196 -0
- package/apps/web/upstream/static/themes/default/theme.css +92 -0
- package/apps/web/upstream/static/themes/elite/theme.css +248 -0
- package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
- package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
- package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
- package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
- package/apps/web/upstream/static/themes/sky/theme.css +107 -0
- package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
- package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
- package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
- package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
- package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
- package/apps/web/upstream/vue.config.js +150 -0
- package/apps/web/upstream/yarn.lock +9767 -0
- package/apps/web/vitest.config.ts +13 -0
- package/biome.json +1 -0
- package/docs/Home.md +1 -0
- package/docs/WebClientGuide.md +535 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/tests/cf-harness.ts +12 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/reports/mutation/mutation.html +342 -0
- package/packages/irc-core/tests/batch.test.ts +15 -0
- package/packages/irc-core/tests/numerics.test.ts +13 -0
- package/packages/irc-core/tests/parser.test.ts +109 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/scenarios.ts +24 -2
- package/tools/ci-hardening/package.json +1 -1
- package/tools/load-test/package.json +1 -1
- package/tools/tcp-ws-forwarder/package.json +1 -1
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:key="'messagelist-' + buffer.name"
|
|
4
|
+
ref="scroller"
|
|
5
|
+
v-resizeobserver="onListResize"
|
|
6
|
+
class="kiwi-messagelist"
|
|
7
|
+
:class="{'kiwi-messagelist--smoothscroll': smooth_scroll}"
|
|
8
|
+
@click.self="onListClick"
|
|
9
|
+
>
|
|
10
|
+
<div v-resizeobserver="onListResize">
|
|
11
|
+
<div
|
|
12
|
+
v-if="shouldShowChathistoryTools"
|
|
13
|
+
class="kiwi-messagelist-scrollback"
|
|
14
|
+
>
|
|
15
|
+
<a
|
|
16
|
+
v-if="!buffer.flag('is_requesting_chathistory')"
|
|
17
|
+
class="u-link"
|
|
18
|
+
@click="buffer.requestScrollback()"
|
|
19
|
+
>
|
|
20
|
+
{{ $t('messages_load') }}
|
|
21
|
+
</a>
|
|
22
|
+
<a v-else>{{ $t('messages_loading') }}</a>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<transition-group tag="div">
|
|
26
|
+
<template v-for="day in filteredMessagesGroupedDay">
|
|
27
|
+
<div
|
|
28
|
+
v-if="filteredMessagesGroupedDay.length > 1 && day.messages.length > 0"
|
|
29
|
+
:key="'msgdatemarker' + day.dayNum"
|
|
30
|
+
class="kiwi-messagelist-seperator"
|
|
31
|
+
>
|
|
32
|
+
<span>{{ (new Date(day.messages[0].time)).toDateString() }}</span>
|
|
33
|
+
</div>
|
|
34
|
+
<transition-group :key="day.dayNum" tag="div">
|
|
35
|
+
<template v-for="message in day.messages">
|
|
36
|
+
<div
|
|
37
|
+
v-if="shouldShowUnreadMarker(message)"
|
|
38
|
+
:key="'msgunreadmarker' + message.id"
|
|
39
|
+
class="kiwi-messagelist-seperator"
|
|
40
|
+
>
|
|
41
|
+
<span>{{ $t('unread_messages') }}</span>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div
|
|
45
|
+
:key="'msg' + message.id"
|
|
46
|
+
:class="[
|
|
47
|
+
'kiwi-messagelist-item',
|
|
48
|
+
selectedMessages[message.id] ?
|
|
49
|
+
'kiwi-messagelist-item--selected' :
|
|
50
|
+
''
|
|
51
|
+
]"
|
|
52
|
+
>
|
|
53
|
+
<!-- message.template is checked first for a custom component,
|
|
54
|
+
then each message layout checks for a message.bodyTemplate
|
|
55
|
+
custom component to apply only to the body area
|
|
56
|
+
-->
|
|
57
|
+
<div
|
|
58
|
+
v-if="message.render() &&
|
|
59
|
+
message.template &&
|
|
60
|
+
message.template.$el &&
|
|
61
|
+
isTemplateVue(message.template)"
|
|
62
|
+
v-rawElement="message.template.$el"
|
|
63
|
+
/>
|
|
64
|
+
<component
|
|
65
|
+
:is="message.template"
|
|
66
|
+
v-else-if="message.render() && message.template"
|
|
67
|
+
v-bind="message.templateProps"
|
|
68
|
+
:buffer="buffer"
|
|
69
|
+
:message="message"
|
|
70
|
+
:idx="filteredMessages.indexOf(message)"
|
|
71
|
+
:ml="thisMl"
|
|
72
|
+
/>
|
|
73
|
+
<message-list-message-modern
|
|
74
|
+
v-else-if="listType === 'modern'"
|
|
75
|
+
:message="message"
|
|
76
|
+
:idx="filteredMessages.indexOf(message)"
|
|
77
|
+
:ml="thisMl"
|
|
78
|
+
/>
|
|
79
|
+
<message-list-message-inline
|
|
80
|
+
v-else-if="listType === 'inline'"
|
|
81
|
+
:message="message"
|
|
82
|
+
:idx="filteredMessages.indexOf(message)"
|
|
83
|
+
:ml="thisMl"
|
|
84
|
+
/>
|
|
85
|
+
<message-list-message-compact
|
|
86
|
+
v-else-if="listType === 'compact'"
|
|
87
|
+
:message="message"
|
|
88
|
+
:idx="filteredMessages.indexOf(message)"
|
|
89
|
+
:ml="thisMl"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
</transition-group>
|
|
94
|
+
</template>
|
|
95
|
+
</transition-group>
|
|
96
|
+
|
|
97
|
+
<transition name="kiwi-messagelist-joinloadertrans">
|
|
98
|
+
<div v-if="shouldShowJoiningLoader" class="kiwi-messagelist-joinloader">
|
|
99
|
+
<LoadingAnimation />
|
|
100
|
+
</div>
|
|
101
|
+
</transition>
|
|
102
|
+
|
|
103
|
+
<buffer-key
|
|
104
|
+
v-if="shouldRequestChannelKey"
|
|
105
|
+
:buffer="buffer"
|
|
106
|
+
:network="buffer.getNetwork()"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</template>
|
|
111
|
+
|
|
112
|
+
<script>
|
|
113
|
+
'kiwi public';
|
|
114
|
+
|
|
115
|
+
import Vue from 'vue';
|
|
116
|
+
import strftime from 'strftime';
|
|
117
|
+
import Logger from '@/libs/Logger';
|
|
118
|
+
import * as bufferTools from '@/libs/bufferTools';
|
|
119
|
+
import BufferKey from './BufferKey';
|
|
120
|
+
import MessageListMessageCompact from './MessageListMessageCompact';
|
|
121
|
+
import MessageListMessageModern from './MessageListMessageModern';
|
|
122
|
+
import MessageListMessageInline from './MessageListMessageInline';
|
|
123
|
+
import LoadingAnimation from './LoadingAnimation.vue';
|
|
124
|
+
|
|
125
|
+
require('@/libs/polyfill/Element.closest');
|
|
126
|
+
|
|
127
|
+
let log = Logger.namespace('MessageList.vue');
|
|
128
|
+
|
|
129
|
+
// If we're scrolled up more than this many pixels, don't auto scroll down to the bottom
|
|
130
|
+
// of the message list
|
|
131
|
+
const BOTTOM_SCROLL_MARGIN = 60;
|
|
132
|
+
|
|
133
|
+
export default {
|
|
134
|
+
components: {
|
|
135
|
+
BufferKey,
|
|
136
|
+
MessageListMessageModern,
|
|
137
|
+
MessageListMessageCompact,
|
|
138
|
+
MessageListMessageInline,
|
|
139
|
+
LoadingAnimation,
|
|
140
|
+
},
|
|
141
|
+
props: ['buffer'],
|
|
142
|
+
data() {
|
|
143
|
+
return {
|
|
144
|
+
smooth_scroll: false,
|
|
145
|
+
auto_scroll: true,
|
|
146
|
+
force_smooth_scroll: null,
|
|
147
|
+
chathistoryAvailable: true,
|
|
148
|
+
hover_nick: '',
|
|
149
|
+
message_info_open: null,
|
|
150
|
+
timeToClose: false,
|
|
151
|
+
startClosing: false,
|
|
152
|
+
selectedMessages: Object.create(null),
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
computed: {
|
|
156
|
+
thisMl() {
|
|
157
|
+
return this;
|
|
158
|
+
},
|
|
159
|
+
shouldAutoEmbed() {
|
|
160
|
+
if (this.buffer.isChannel() && this.buffer.setting('inline_link_auto_previews')) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
if (this.buffer.isQuery() && this.buffer.setting('inline_link_auto_previews_query')) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
},
|
|
168
|
+
listType() {
|
|
169
|
+
if (this.$state.setting('messageLayout')) {
|
|
170
|
+
log.info('Deprecation Warning: The config option \'messageLayout\' has been moved to buffers.messageLayout');
|
|
171
|
+
}
|
|
172
|
+
return this.buffer.setting('messageLayout') || this.$state.setting('messageLayout');
|
|
173
|
+
},
|
|
174
|
+
useExtraFormatting() {
|
|
175
|
+
// Enables simple markdown formatting
|
|
176
|
+
return this.buffer.setting('extra_formatting');
|
|
177
|
+
},
|
|
178
|
+
shouldShowChathistoryTools() {
|
|
179
|
+
// Only show it if we're connected
|
|
180
|
+
if (this.buffer.getNetwork().state !== 'connected') {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let isCorrectBufferType = (this.buffer.isChannel() || this.buffer.isQuery());
|
|
185
|
+
let isSupported = !!this.buffer.getNetwork().ircClient.chathistory.isSupported();
|
|
186
|
+
return isCorrectBufferType && isSupported && this.buffer.flags.chathistory_available;
|
|
187
|
+
},
|
|
188
|
+
shouldRequestChannelKey() {
|
|
189
|
+
return this.buffer.getNetwork().state === 'connected' &&
|
|
190
|
+
this.buffer.isChannel() &&
|
|
191
|
+
this.buffer.flags.channel_badkey;
|
|
192
|
+
},
|
|
193
|
+
ourNick() {
|
|
194
|
+
return this.buffer ?
|
|
195
|
+
this.buffer.getNetwork().nick :
|
|
196
|
+
'';
|
|
197
|
+
},
|
|
198
|
+
filteredMessagesGroupedDay() {
|
|
199
|
+
// Group messages by day
|
|
200
|
+
|
|
201
|
+
const days = [];
|
|
202
|
+
let lastDay = null;
|
|
203
|
+
this.filteredMessages.forEach((message) => {
|
|
204
|
+
if (!lastDay || message.day_num !== lastDay) {
|
|
205
|
+
days.push({ dayNum: message.day_num, messages: [] });
|
|
206
|
+
lastDay = message.day_num;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
days[days.length - 1].messages.push(message);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return days;
|
|
213
|
+
},
|
|
214
|
+
filteredMessages() {
|
|
215
|
+
return bufferTools.orderedMessages(this.buffer);
|
|
216
|
+
},
|
|
217
|
+
shouldShowJoiningLoader() {
|
|
218
|
+
return this.buffer.isChannel() &&
|
|
219
|
+
this.buffer.enabled &&
|
|
220
|
+
!this.buffer.joined &&
|
|
221
|
+
this.buffer.getNetwork().state === 'connected';
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
watch: {
|
|
225
|
+
filteredMessages() {
|
|
226
|
+
// Data has changed and now preparing to update the DOM.
|
|
227
|
+
// Check our scrolling state before the DOM updates so that we know if we're scrolled
|
|
228
|
+
// at the bottom before new messages are added
|
|
229
|
+
this.checkScrollingState();
|
|
230
|
+
|
|
231
|
+
// Wait until after the DOM has updated before possibly scrolling down based on the
|
|
232
|
+
// previous check
|
|
233
|
+
this.$nextTick(() => {
|
|
234
|
+
this.maybeScrollToBottom();
|
|
235
|
+
});
|
|
236
|
+
},
|
|
237
|
+
buffer(newBuffer, oldBuffer) {
|
|
238
|
+
if (oldBuffer) {
|
|
239
|
+
oldBuffer.isMessageTrimming = true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!newBuffer) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
this.message_info_open = null;
|
|
247
|
+
|
|
248
|
+
if (this.buffer.getNetwork().state === 'connected') {
|
|
249
|
+
newBuffer.flags.has_opened = true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.auto_scroll = true;
|
|
253
|
+
this.force_smooth_scroll = false;
|
|
254
|
+
this.$nextTick(() => {
|
|
255
|
+
this.scrollToBottom();
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
mounted() {
|
|
260
|
+
this.addCopyListeners();
|
|
261
|
+
|
|
262
|
+
this.$nextTick(() => {
|
|
263
|
+
this.scrollToBottom();
|
|
264
|
+
// this.smooth_scroll = true;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
this.listen(this.$state, 'mediaviewer.opened', () => {
|
|
268
|
+
this.$nextTick(this.maybeScrollToBottom.apply(this));
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
this.listen(this.$state, 'messagelist.scrollto', (opt) => {
|
|
272
|
+
if (opt && opt.id) {
|
|
273
|
+
this.maybeScrollToId(opt.id);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
},
|
|
277
|
+
methods: {
|
|
278
|
+
isTemplateVue(template) {
|
|
279
|
+
const isVue = template instanceof Vue;
|
|
280
|
+
if (isVue && !window.kiwi_deprecations_messageTemplate) {
|
|
281
|
+
window.kiwi_deprecations_messageTemplate = true;
|
|
282
|
+
// eslint-disable-next-line no-console
|
|
283
|
+
console.warn('deprecated message.template or message.bodyTemplate, please use `message.template = kiwi.Vue.extend(component object)`');
|
|
284
|
+
}
|
|
285
|
+
return isVue;
|
|
286
|
+
},
|
|
287
|
+
isHoveringOverMessage(message) {
|
|
288
|
+
return message.nick && message.nick.toLowerCase() === this.hover_nick.toLowerCase();
|
|
289
|
+
},
|
|
290
|
+
toggleMessageInfo(message) {
|
|
291
|
+
if (!message) {
|
|
292
|
+
this.message_info_open = null;
|
|
293
|
+
} else if (this.message_info_open === message) {
|
|
294
|
+
// It's already open, so don't do anything
|
|
295
|
+
} else if (this.canShowInfoForMessage(message)) {
|
|
296
|
+
// If in the process of selecting text, don't show the info box
|
|
297
|
+
let sel = window.getSelection();
|
|
298
|
+
if (sel.rangeCount > 0) {
|
|
299
|
+
let range = sel.getRangeAt(0);
|
|
300
|
+
if (range && !range.collapsed) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.message_info_open = message;
|
|
306
|
+
this.$nextTick(() => this.maybeScrollToId(message.id));
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
shouldShowUnreadMarker(message) {
|
|
310
|
+
let idx = this.filteredMessages.indexOf(message);
|
|
311
|
+
let previous = this.filteredMessages[idx - 1];
|
|
312
|
+
let current = this.filteredMessages[idx];
|
|
313
|
+
let lastRead = this.buffer.last_read;
|
|
314
|
+
|
|
315
|
+
if (!lastRead) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!current) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// If the last message has been read, and this message not read
|
|
324
|
+
if (previous && previous.time < lastRead && current.time > lastRead) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return false;
|
|
329
|
+
},
|
|
330
|
+
shouldShowDateChangeMarker(idx) {
|
|
331
|
+
let previous = this.filteredMessages[idx - 1];
|
|
332
|
+
let current = this.filteredMessages[idx];
|
|
333
|
+
|
|
334
|
+
if (!previous && (new Date(current.time)).getDay() !== (new Date()).getDay()) {
|
|
335
|
+
// The first message of the lsit and it's not today
|
|
336
|
+
return true;
|
|
337
|
+
} else if (!previous) {
|
|
338
|
+
// The first message of the lsit but it's today
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// If the last message has been read, and this message not read
|
|
343
|
+
if ((new Date(previous.time)).getDay() !== (new Date(current.time)).getDay()) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return false;
|
|
348
|
+
},
|
|
349
|
+
canShowInfoForMessage(message) {
|
|
350
|
+
let showInfoForTypes = ['privmsg', 'notice', 'action'];
|
|
351
|
+
return showInfoForTypes.indexOf(message.type) > -1;
|
|
352
|
+
},
|
|
353
|
+
bufferSetting(key) {
|
|
354
|
+
return this.buffer.setting(key);
|
|
355
|
+
},
|
|
356
|
+
formatTime(time) {
|
|
357
|
+
return strftime(this.buffer.setting('timestamp_format') || '%T', new Date(time));
|
|
358
|
+
},
|
|
359
|
+
formatTimeFull(time) {
|
|
360
|
+
let format = this.buffer.setting('timestamp_full_format');
|
|
361
|
+
return format ?
|
|
362
|
+
strftime(format, new Date(time)) :
|
|
363
|
+
(new Date(time)).toLocaleString();
|
|
364
|
+
},
|
|
365
|
+
formatMessage(message) {
|
|
366
|
+
return message.toHtml(this);
|
|
367
|
+
},
|
|
368
|
+
isMessageHighlight(message) {
|
|
369
|
+
// Highlighting ourselves when we join or leave a channel is silly
|
|
370
|
+
if (message.type === 'traffic') {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return message.isHighlight;
|
|
375
|
+
},
|
|
376
|
+
userColour(user) {
|
|
377
|
+
if (user && this.bufferSetting('colour_nicknames_in_messages')) {
|
|
378
|
+
return user.getColour();
|
|
379
|
+
}
|
|
380
|
+
return '';
|
|
381
|
+
},
|
|
382
|
+
openUserBox(nick) {
|
|
383
|
+
let user = this.$state.getUser(this.buffer.networkid, nick);
|
|
384
|
+
if (user) {
|
|
385
|
+
this.$state.$emit('userbox.show', user, {
|
|
386
|
+
buffer: this.buffer,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
onListClick(event) {
|
|
391
|
+
this.toggleMessageInfo();
|
|
392
|
+
},
|
|
393
|
+
onMessageDblClick(event, message) {
|
|
394
|
+
clearTimeout(this.messageClickTmr);
|
|
395
|
+
|
|
396
|
+
let dataNick = event.target.getAttribute('data-nick');
|
|
397
|
+
if (!dataNick) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
let network = this.buffer.getNetwork();
|
|
402
|
+
let user = network.userByName(dataNick);
|
|
403
|
+
// The user might have left use dataNick as fallback
|
|
404
|
+
let nick = (user && user.nick) ?
|
|
405
|
+
user.nick :
|
|
406
|
+
dataNick;
|
|
407
|
+
this.$state.$emit('input.insertnick', nick);
|
|
408
|
+
},
|
|
409
|
+
onMessageClick(event, message, delay) {
|
|
410
|
+
// Delaying the click for 200ms allows us to check for a second click. ie. double click
|
|
411
|
+
// Quick hack as we only need double click for nicks, nothing else
|
|
412
|
+
if (delay && event.target.getAttribute('data-nick')) {
|
|
413
|
+
clearTimeout(this.messageClickTmr);
|
|
414
|
+
this.messageClickTmr = setTimeout(this.onMessageClick, 200, event, message, false);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let isLink = event.target.tagName === 'A';
|
|
419
|
+
|
|
420
|
+
let channelName = event.target.getAttribute('data-channel-name');
|
|
421
|
+
if (channelName && isLink) {
|
|
422
|
+
let network = this.buffer.getNetwork();
|
|
423
|
+
this.$state.addBuffer(this.buffer.networkid, channelName);
|
|
424
|
+
network.ircClient.join(channelName);
|
|
425
|
+
this.$state.setActiveBuffer(this.buffer.networkid, channelName);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
let userNick = event.target.getAttribute('data-nick');
|
|
430
|
+
if (userNick && isLink) {
|
|
431
|
+
this.openUserBox(userNick);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
let url = event.target.getAttribute('data-url');
|
|
436
|
+
if (url && isLink) {
|
|
437
|
+
if (this.$state.setting('buffers.inline_link_auto_previews')) {
|
|
438
|
+
message.embed.type = 'url';
|
|
439
|
+
message.embed.payload = url;
|
|
440
|
+
} else {
|
|
441
|
+
this.$state.$emit('mediaviewer.show', url);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (this.message_info_open && this.message_info_open !== message) {
|
|
446
|
+
// Clicking on another message while another info is open, just close the info
|
|
447
|
+
this.toggleMessageInfo();
|
|
448
|
+
event.preventDefault();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (this.$state.ui.is_touch && this.$state.setting('buffers.show_message_info')) {
|
|
453
|
+
if (this.canShowInfoForMessage(message) && event.target.nodeName === 'A') {
|
|
454
|
+
// We show message info boxes on touch screen devices so that the user has an
|
|
455
|
+
// option to preview the links or do other stuff.
|
|
456
|
+
event.preventDefault();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
this.toggleMessageInfo(message);
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
checkScrollingState() {
|
|
463
|
+
let el = this.$el;
|
|
464
|
+
let scrolledUpByPx = el.scrollHeight - (el.offsetHeight + el.scrollTop);
|
|
465
|
+
|
|
466
|
+
// We need to know at this point (before the DOM has updated with new messages) if we
|
|
467
|
+
// are at the bottom of the messagelist or not, otherwise once the DOM has updated then
|
|
468
|
+
// it is too late to determine if we should auto scroll down
|
|
469
|
+
if (scrolledUpByPx > BOTTOM_SCROLL_MARGIN) {
|
|
470
|
+
this.auto_scroll = false;
|
|
471
|
+
this.buffer.isMessageTrimming = false;
|
|
472
|
+
} else {
|
|
473
|
+
this.auto_scroll = true;
|
|
474
|
+
this.buffer.isMessageTrimming = true;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (this.force_smooth_scroll !== null) {
|
|
478
|
+
this.smooth_scroll = this.force_smooth_scroll;
|
|
479
|
+
this.force_smooth_scroll = null;
|
|
480
|
+
// TODO: Enabling smooth_scroll breaks the auto-scroll-to-bottom on fast buffers as
|
|
481
|
+
// it takes time to scroll down and it looks like we're scrolled too far up when
|
|
482
|
+
// detecting if were scrolled up or not. Look into ways around this so that we
|
|
483
|
+
// can enable it as it does look a lot better.
|
|
484
|
+
// } else if (scrolledUpByPx < BOTTOM_SCROLL_MARGIN) {
|
|
485
|
+
// this.smooth_scroll = true;
|
|
486
|
+
} else {
|
|
487
|
+
this.smooth_scroll = false;
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
onListResize(e) {
|
|
491
|
+
// The messagelist or interface has resized or had new content added
|
|
492
|
+
// check if we should auto scroll down to the bottom
|
|
493
|
+
this.maybeScrollToBottom();
|
|
494
|
+
},
|
|
495
|
+
scrollToBottom() {
|
|
496
|
+
this.$el.scrollTop = this.$el.scrollHeight;
|
|
497
|
+
},
|
|
498
|
+
maybeScrollToBottom() {
|
|
499
|
+
if (this.auto_scroll) {
|
|
500
|
+
this.scrollToBottom();
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
maybeScrollToId(id, position = 'middle') {
|
|
504
|
+
let msgEl = this.$el.querySelector('.kiwi-messagelist-message[data-message-id="' + id + '"]');
|
|
505
|
+
if (!msgEl) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
let newTop = 0;
|
|
510
|
+
if (position === 'top') {
|
|
511
|
+
// There maybe a sticky unread marker at the top
|
|
512
|
+
newTop = msgEl.offsetTop;
|
|
513
|
+
} else if (position === 'bottom') {
|
|
514
|
+
newTop = Math.floor(
|
|
515
|
+
msgEl.offsetTop - this.$refs.scroller.offsetHeight + msgEl.offsetHeight
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
newTop = Math.floor(
|
|
519
|
+
msgEl.offsetTop - ((this.$refs.scroller.offsetHeight - msgEl.offsetHeight) / 2)
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
this.auto_scroll = false;
|
|
524
|
+
this.$refs.scroller.scrollTo({ top: newTop, behavior: 'smooth' });
|
|
525
|
+
},
|
|
526
|
+
getSelectedMessages() {
|
|
527
|
+
let sel = document.getSelection();
|
|
528
|
+
let r = sel.getRangeAt(0);
|
|
529
|
+
let messageEls = [...this.$el.querySelectorAll('.kiwi-messagelist-message')];
|
|
530
|
+
let selectedMessageEls = messageEls.filter((el) => r.intersectsNode(el));
|
|
531
|
+
return selectedMessageEls;
|
|
532
|
+
},
|
|
533
|
+
restrictTextSelection() { // Prevents the selection cursor escaping the message list.
|
|
534
|
+
document.querySelector('body').classList.add('kiwi-unselectable');
|
|
535
|
+
this.$el.style.userSelect = 'text';
|
|
536
|
+
},
|
|
537
|
+
unrestrictTextSelection() { // Allows all page elements to be selected again.
|
|
538
|
+
document.querySelector('body').classList.remove('kiwi-unselectable');
|
|
539
|
+
this.$el.style.userSelect = 'auto';
|
|
540
|
+
},
|
|
541
|
+
removeSelections(removeNative = false) {
|
|
542
|
+
this.selectedMessages = Object.create(null);
|
|
543
|
+
|
|
544
|
+
let selection = document.getSelection();
|
|
545
|
+
if (removeNative && selection) {
|
|
546
|
+
// stops the native browser selection being left behind after ctrl+c
|
|
547
|
+
selection.removeAllRanges();
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
addCopyListeners() { // Better copy pasting
|
|
551
|
+
const LogFormatter = (msg) => {
|
|
552
|
+
let text = '';
|
|
553
|
+
|
|
554
|
+
switch (msg.type) {
|
|
555
|
+
case 'privmsg':
|
|
556
|
+
text = `<${msg.nick}> ${msg.message}`;
|
|
557
|
+
break;
|
|
558
|
+
case 'nick':
|
|
559
|
+
case 'mode':
|
|
560
|
+
case 'action':
|
|
561
|
+
case 'traffic':
|
|
562
|
+
text = `${msg.message}`;
|
|
563
|
+
break;
|
|
564
|
+
default:
|
|
565
|
+
text = msg.message;
|
|
566
|
+
}
|
|
567
|
+
if (text.length) {
|
|
568
|
+
return `[${(new Date(msg.time)).toLocaleTimeString({ hour: '2-digit', minute: '2-digit', second: '2-digit' })}] ${text}`;
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
let copyData = '';
|
|
574
|
+
let selecting = false;
|
|
575
|
+
let selectionChangeOff = null;
|
|
576
|
+
|
|
577
|
+
this.listen(document, 'selectstart', (e) => {
|
|
578
|
+
if (!this.$el.contains(e.target)) {
|
|
579
|
+
// Selected elsewhere on the page
|
|
580
|
+
copyData = '';
|
|
581
|
+
this.removeSelections();
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
this.removeSelections();
|
|
586
|
+
selectionChangeOff = this.listen(document, 'selectionchange', onSelectionChange);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
this.listen(document, 'mouseup', (e) => {
|
|
590
|
+
selectionChangeOff && selectionChangeOff();
|
|
591
|
+
this.unrestrictTextSelection();
|
|
592
|
+
if (selecting) {
|
|
593
|
+
e.preventDefault();
|
|
594
|
+
}
|
|
595
|
+
selecting = false;
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
let onSelectionChange = (e) => {
|
|
599
|
+
if (!this.$el) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
copyData = ''; // Store the text data to be copied in this.
|
|
604
|
+
|
|
605
|
+
let selection = document.getSelection();
|
|
606
|
+
|
|
607
|
+
if (!selection
|
|
608
|
+
|| !selection.anchorNode
|
|
609
|
+
|| !selection.anchorNode.parentNode.closest('.' + this.$el.className)) {
|
|
610
|
+
this.unrestrictTextSelection();
|
|
611
|
+
this.removeSelections();
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
this.removeSelections();
|
|
616
|
+
// Prevent the selection escaping the message list
|
|
617
|
+
this.restrictTextSelection();
|
|
618
|
+
if (selection.rangeCount > 0) {
|
|
619
|
+
selecting = true;
|
|
620
|
+
|
|
621
|
+
let selectedMesssageEls = this.getSelectedMessages();
|
|
622
|
+
let selectedMessages = [];
|
|
623
|
+
selectedMesssageEls.forEach((el) => {
|
|
624
|
+
let m = this.buffer.messagesObj.messageIds[el.dataset.messageId];
|
|
625
|
+
if (m) {
|
|
626
|
+
selectedMessages.push(m);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
// If only 1 message is selected then treat the selection as native text
|
|
631
|
+
// selection. Most likely copying part of a message only.
|
|
632
|
+
if (selectedMessages.length === 1) {
|
|
633
|
+
selectedMessages = [];
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
this.selectedMessages = Object.create(null);
|
|
637
|
+
selectedMessages.forEach((m) => {
|
|
638
|
+
this.selectedMessages[m.id] = m;
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// Iterate through the selected messages, format and store as a
|
|
642
|
+
// string to be used in the copy handler
|
|
643
|
+
copyData = selectedMessages
|
|
644
|
+
.sort((a, b) => (a.time > b.time ? 1 : -1))
|
|
645
|
+
.filter((m) => m.message.trim().length)
|
|
646
|
+
.map(LogFormatter)
|
|
647
|
+
.join('\r\n');
|
|
648
|
+
} else {
|
|
649
|
+
this.unrestrictTextSelection();
|
|
650
|
+
}
|
|
651
|
+
return false;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
this.listen(document, 'copy', (e) => {
|
|
655
|
+
if (!copyData || !copyData.length) { // Just do a normal copy if no special data
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (navigator.clipboard) { // Supports Clipboard API
|
|
660
|
+
navigator.clipboard.writeText(copyData);
|
|
661
|
+
} else {
|
|
662
|
+
let input = document.createElement('textarea');
|
|
663
|
+
document.body.appendChild(input);
|
|
664
|
+
input.innerHTML = copyData;
|
|
665
|
+
input.select();
|
|
666
|
+
document.execCommand('copy');
|
|
667
|
+
document.body.removeChild(input);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return true;
|
|
671
|
+
});
|
|
672
|
+
},
|
|
673
|
+
// Move a messages embeded content to the main media preview
|
|
674
|
+
openEmbedInPreview(message) {
|
|
675
|
+
// First open the embed in the main media preview
|
|
676
|
+
let embed = message.embed;
|
|
677
|
+
if (embed.type === 'url') {
|
|
678
|
+
this.$state.$emit('mediaviewer.show', embed.payload);
|
|
679
|
+
} else if (embed.type === 'component') {
|
|
680
|
+
this.$state.$emit('mediaviewer.show', {
|
|
681
|
+
component: embed.payload,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Remove the embed from the message
|
|
686
|
+
embed.payload = null;
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
};
|
|
690
|
+
</script>
|
|
691
|
+
|
|
692
|
+
<style lang="less">
|
|
693
|
+
|
|
694
|
+
.kiwi-unselectable * {
|
|
695
|
+
-webkit-touch-callout: none;
|
|
696
|
+
-webkit-user-select: none;
|
|
697
|
+
-moz-user-select: none;
|
|
698
|
+
-ms-user-select: none;
|
|
699
|
+
user-select: none;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
div.kiwi-messagelist-item.kiwi-messagelist-item--selected {
|
|
703
|
+
border-left: 7px solid var(--brand-primary);
|
|
704
|
+
transform: translateX(20px);
|
|
705
|
+
transition: transform 0.1s;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
div.kiwi-messagelist-item.kiwi-messagelist-item--selected .kiwi-messagelist-message {
|
|
709
|
+
border-left-width: 0;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.kiwi-messagelist-item.kiwi-messagelist-item--selected .kiwi-messagelist-message *::selection {
|
|
713
|
+
background-color: unset;
|
|
714
|
+
color: unset;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.kiwi-unselectable .kiwi-messagelist-scrollback {
|
|
718
|
+
-webkit-touch-callout: none;
|
|
719
|
+
-webkit-user-select: none;
|
|
720
|
+
-moz-user-select: none;
|
|
721
|
+
-ms-user-select: none;
|
|
722
|
+
user-select: none;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.kiwi-messagelist {
|
|
726
|
+
overflow-y: auto;
|
|
727
|
+
overflow-x: hidden;
|
|
728
|
+
box-sizing: border-box;
|
|
729
|
+
margin-bottom: 25px;
|
|
730
|
+
position: relative;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.kiwi-messagelist--smoothscroll {
|
|
734
|
+
scroll-behavior: smooth;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.kiwi-messagelist * {
|
|
738
|
+
user-select: text;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.kiwi-messagelist::-webkit-scrollbar-track {
|
|
742
|
+
border-radius: 10px;
|
|
743
|
+
background: transparent;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.kiwi-messagelist::-webkit-scrollbar {
|
|
747
|
+
width: 8px;
|
|
748
|
+
background: transparent;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.kiwi-messagelist::-webkit-scrollbar-thumb {
|
|
752
|
+
border-radius: 3px;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.kiwi-messagelist-item {
|
|
756
|
+
/* Allow child elements to make use of margins+padding within messagelist items */
|
|
757
|
+
overflow: hidden;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.kiwi-messagelist-message {
|
|
761
|
+
padding: 0 10px;
|
|
762
|
+
|
|
763
|
+
/* some message highlights add a left border so add a default invisble one in preperation */
|
|
764
|
+
border-left: 3px solid transparent;
|
|
765
|
+
overflow: hidden;
|
|
766
|
+
line-height: 1.5em;
|
|
767
|
+
margin: 0;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.kiwi-wrap--monospace .kiwi-messagelist-message,
|
|
771
|
+
.kiwi-messagelist-message.kiwi-messagelist-message-help {
|
|
772
|
+
font-family: Consolas, monaco, monospace;
|
|
773
|
+
font-size: 80%;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/* Remove the styling for none user messages, as they make the page look bloated */
|
|
777
|
+
.kiwi-messagelist-message-mode,
|
|
778
|
+
.kiwi-messagelist-message-traffic {
|
|
779
|
+
padding-top: 5px;
|
|
780
|
+
padding-bottom: 5px;
|
|
781
|
+
min-height: 0;
|
|
782
|
+
line-height: normal;
|
|
783
|
+
text-align: left;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* Remove the min height from the message, as again, makes the page look bloated */
|
|
787
|
+
.kiwi-messagelist-body {
|
|
788
|
+
min-height: 0;
|
|
789
|
+
text-align: left;
|
|
790
|
+
line-height: 1.5em;
|
|
791
|
+
font-size: 1.05em;
|
|
792
|
+
margin: 0;
|
|
793
|
+
padding: 0;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/* Channel messages - e.g 'server on #testing22 ' message and such */
|
|
797
|
+
.kiwi-messagelist-message-mode,
|
|
798
|
+
.kiwi-messagelist-message-traffic,
|
|
799
|
+
.kiwi-messagelist-message-nick {
|
|
800
|
+
margin: 10px 0;
|
|
801
|
+
opacity: 0.85;
|
|
802
|
+
text-align: center;
|
|
803
|
+
border: none;
|
|
804
|
+
|
|
805
|
+
&:hover {
|
|
806
|
+
opacity: 1;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/* Absolute position the time on these messages so it's not above the message, it looks awful */
|
|
811
|
+
.kiwi-messagelist-message-mode .kiwi-messagelist-time,
|
|
812
|
+
.kiwi-messagelist-message-traffic .kiwi-messagelist-time {
|
|
813
|
+
position: absolute;
|
|
814
|
+
top: 1px;
|
|
815
|
+
right: 10px;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.kiwi-messagelist-message--authorrepeat {
|
|
819
|
+
border-top: none;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.kiwi-messagelist-message--authorrepeat .kiwi-messagelist-nick,
|
|
823
|
+
.kiwi-messagelist-message--authorrepeat .kiwi-messagelist-time {
|
|
824
|
+
/* Setting the opacity instead visible:none ensures it's still selectable when copying text */
|
|
825
|
+
opacity: 0;
|
|
826
|
+
cursor: default;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.kiwi-container--sidebar-drawn .kiwi-messagelist::after {
|
|
830
|
+
content: '';
|
|
831
|
+
z-index: 3;
|
|
832
|
+
left: 0;
|
|
833
|
+
top: 0;
|
|
834
|
+
width: 100%;
|
|
835
|
+
height: 100%;
|
|
836
|
+
opacity: 0.5;
|
|
837
|
+
position: fixed;
|
|
838
|
+
pointer-events: none;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.kiwi-container--sidebar-drawn.kiwi-container--no-sidebar .kiwi-messagelist::after {
|
|
842
|
+
width: 0;
|
|
843
|
+
height: 0;
|
|
844
|
+
display: none;
|
|
845
|
+
pointer-events: inherit;
|
|
846
|
+
position: static;
|
|
847
|
+
z-index: 0;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.kiwi-messagelist-scrollback {
|
|
851
|
+
text-align: center;
|
|
852
|
+
padding: 5px;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.kiwi-messagelist-seperator + .kiwi-messagelist-message {
|
|
856
|
+
border-top: none;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.kiwi-messagelist-message--blur {
|
|
860
|
+
opacity: 0.3;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.kiwi-messagelist-nick {
|
|
864
|
+
text-align: right;
|
|
865
|
+
font-weight: bold;
|
|
866
|
+
text-overflow: ellipsis;
|
|
867
|
+
overflow: hidden;
|
|
868
|
+
vertical-align: top;
|
|
869
|
+
cursor: pointer;
|
|
870
|
+
padding: 2px 4px;
|
|
871
|
+
word-break: break-all;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
.kiwi-messagelist-message-traffic .kiwi-messagelist-nick {
|
|
875
|
+
display: none;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
.kiwi-messagelist-seperator {
|
|
879
|
+
text-align: center;
|
|
880
|
+
display: block;
|
|
881
|
+
margin: 1em auto;
|
|
882
|
+
position: sticky;
|
|
883
|
+
top: -1px;
|
|
884
|
+
z-index: 1;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.kiwi-messagelist-seperator > span {
|
|
888
|
+
display: inline-block;
|
|
889
|
+
position: relative;
|
|
890
|
+
z-index: 1;
|
|
891
|
+
padding: 0 1em;
|
|
892
|
+
user-select: none;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/** Displaying an emoji in a message */
|
|
896
|
+
.kiwi-messagelist-emoji {
|
|
897
|
+
width: 1.3em;
|
|
898
|
+
display: inline-block;
|
|
899
|
+
vertical-align: middle;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
@keyframes emojiIn {
|
|
903
|
+
0% {
|
|
904
|
+
transform: scale(0);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
100% {
|
|
908
|
+
transform: scale(1);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.kiwi-messagelist-emoji--single {
|
|
913
|
+
animation: 0.1s ease-in-out 0s 1 emojiIn;
|
|
914
|
+
font-size: 2em;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/** Message structure */
|
|
918
|
+
.kiwi-messagelist-body .kiwi-nick {
|
|
919
|
+
cursor: pointer;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.kiwi-messagelist-nick:hover {
|
|
923
|
+
overflow: visible;
|
|
924
|
+
width: auto;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/* Topic changes */
|
|
928
|
+
.kiwi-messagelist-message-topic {
|
|
929
|
+
border-radius: 5px;
|
|
930
|
+
margin: 18px;
|
|
931
|
+
margin-left: 0;
|
|
932
|
+
padding: 5px;
|
|
933
|
+
text-align: center;
|
|
934
|
+
position: relative;
|
|
935
|
+
min-height: 0;
|
|
936
|
+
display: block;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.kiwi-messagelist-message-topic .kiwi-messagelist-body {
|
|
940
|
+
min-height: 0;
|
|
941
|
+
margin: 0;
|
|
942
|
+
|
|
943
|
+
&::before {
|
|
944
|
+
display: none;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.kiwi-messagelist-message-topic.kiwi-messagelist-message-topic .kiwi-messagelist-time {
|
|
949
|
+
display: none;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.kiwi-messagelist-message-topic.kiwi-messagelist-message-topic .kiwi-messagelist-nick {
|
|
953
|
+
display: none;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/* Actions */
|
|
957
|
+
.kiwi-messagelist-message-action .kiwi-messagelist-message-body {
|
|
958
|
+
font-style: italic;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/* Traffic (joins, parts, quits, kicks) */
|
|
962
|
+
.kiwi-messagelist-message-traffic.kiwi-messagelist-message-traffic .kiwi-messagelist-nick {
|
|
963
|
+
display: none;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
.kiwi-messagelist-message-traffic .kiwi-messagelist-body {
|
|
967
|
+
font-style: italic;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.kiwi-messagelist-message-action.kiwi-messagelist-message-action .kiwi-messagelist-nick {
|
|
971
|
+
display: none;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/* MOTD */
|
|
975
|
+
.kiwi-messagelist-message-motd {
|
|
976
|
+
font-family: monospace;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.kiwi-messagelist-message.kiwi-messagelist-message--hover,
|
|
980
|
+
.kiwi-messagelist-message.kiwi-messagelist-message--highlight,
|
|
981
|
+
.kiwi-messagelist-message.kiwi-messagelist-message-traffic--hover {
|
|
982
|
+
position: relative;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/* Links */
|
|
986
|
+
.kiwi-messagelist-message-linkhandle {
|
|
987
|
+
margin-left: 4px;
|
|
988
|
+
font-size: 0.8em;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
.kiwi-wrap--touch .kiwi-messagelist-message-linkhandle {
|
|
992
|
+
display: none;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.kiwi-messagelist-joinloader {
|
|
996
|
+
margin: 1em auto;
|
|
997
|
+
width: 100px;
|
|
998
|
+
|
|
999
|
+
/* the magic number below is the exact ratio of the kiwi logo height/width */
|
|
1000
|
+
height: calc (100px * 0.85987261146496815286624203821656);
|
|
1001
|
+
overflow: hidden;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.kiwi-messagelist-joinloadertrans-enter,
|
|
1005
|
+
.kiwi-messagelist-joinloadertrans-leave-to {
|
|
1006
|
+
height: 0;
|
|
1007
|
+
opacity: 0;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.kiwi-messagelist-joinloadertrans-enter-to,
|
|
1011
|
+
.kiwi-messagelist-joinloadertrans-leave {
|
|
1012
|
+
height: 150px;
|
|
1013
|
+
opacity: 1;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
.kiwi-messagelist-joinloadertrans-enter-active,
|
|
1017
|
+
.kiwi-messagelist-joinloadertrans-leave-active {
|
|
1018
|
+
transition: height 0.5s, opacity 0.5s;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
@media screen and (max-width: 700px) {
|
|
1022
|
+
.kiwi-messagelist-message,
|
|
1023
|
+
.kiwi-messageinfo {
|
|
1024
|
+
margin: 0;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
</style>
|