diff options
Diffstat (limited to 'src/components')
20 files changed, 179 insertions, 313 deletions
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js index 6e23c20c..9c4e5b05 100644 --- a/src/components/chat/chat.js +++ b/src/components/chat/chat.js @@ -2,29 +2,26 @@ import _ from 'lodash' import { WSConnectionStatus } from '../../services/api/api.service.js' import { mapGetters, mapState } from 'vuex' import ChatMessage from '../chat_message/chat_message.vue' -import ChatAvatar from '../chat_avatar/chat_avatar.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' import ChatTitle from '../chat_title/chat_title.vue' import chatService from '../../services/chat_service/chat_service.js' -import ChatLayout from './chat_layout.js' import { getScrollPosition, getNewTopPosition, isBottomedOut, scrollableContainerHeight } from './chat_layout_utils.js' const BOTTOMED_OUT_OFFSET = 10 const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 150 +const SAFE_RESIZE_TIME_OFFSET = 100 const Chat = { components: { ChatMessage, ChatTitle, - ChatAvatar, PostStatusForm }, - mixins: [ChatLayout], data () { return { jumpToBottomButtonVisible: false, hoveredMessageChainId: undefined, - scrollPositionBeforeResize: {}, + lastScrollPosition: {}, scrollableContainerHeight: '100%', errorLoadingChat: false } @@ -119,6 +116,7 @@ const Chat = { }, onFilesDropped () { this.$nextTick(() => { + this.handleResize() this.updateScrollableContainerHeight() }) }, @@ -129,13 +127,30 @@ const Chat = { } }) }, - handleLayoutChange () { - this.updateScrollableContainerHeight() - if (this.mobileLayout) { - this.setMobileChatLayout() - } else { - this.unsetMobileChatLayout() + setChatLayout () { + // This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app). + // This layout prevents empty spaces from being visible at the bottom + // of the chat on iOS Safari (`safe-area-inset`) when + // - the on-screen keyboard appears and the user starts typing + // - the user selects the text inside the input area + // - the user selects and deletes the text that is multiple lines long + // TODO: unify the chat layout with the global layout. + let html = document.querySelector('html') + if (html) { + html.classList.add('chat-layout') } + + this.$nextTick(() => { + this.updateScrollableContainerHeight() + }) + }, + unsetChatLayout () { + let html = document.querySelector('html') + if (html) { + html.classList.remove('chat-layout') + } + }, + handleLayoutChange () { this.$nextTick(() => { this.updateScrollableContainerHeight() this.scrollDown() @@ -149,15 +164,24 @@ const Chat = { this.scrollableContainerHeight = scrollableContainerHeight(inner, header, footer) + 'px' }, // Preserves the scroll position when OSK appears or the posting form changes its height. - handleResize (opts) { + handleResize (opts = {}) { + const { expand = false, delayed = false } = opts + + if (delayed) { + setTimeout(() => { + this.handleResize({ ...opts, delayed: false }) + }, SAFE_RESIZE_TIME_OFFSET) + return + } + this.$nextTick(() => { this.updateScrollableContainerHeight() - const { offsetHeight = undefined } = this.scrollPositionBeforeResize - this.scrollPositionBeforeResize = getScrollPosition(this.$refs.scrollable) + const { offsetHeight = undefined } = this.lastScrollPosition + this.lastScrollPosition = getScrollPosition(this.$refs.scrollable) - const diff = this.scrollPositionBeforeResize.offsetHeight - offsetHeight - if (diff < 0 || (!this.bottomedOut() && opts && opts.expand)) { + const diff = this.lastScrollPosition.offsetHeight - offsetHeight + if (diff < 0 || (!this.bottomedOut() && expand)) { this.$nextTick(() => { this.updateScrollableContainerHeight() this.$refs.scrollable.scrollTo({ @@ -281,7 +305,12 @@ const Chat = { .then(data => { this.$store.dispatch('addChatMessages', { chatId: this.currentChat.id, messages: [data] }).then(() => { this.$nextTick(() => { - this.updateScrollableContainerHeight() + this.handleResize() + // When the posting form size changes because of a media attachment, we need an extra resize + // to account for the potential delay in the DOM update. + setTimeout(() => { + this.updateScrollableContainerHeight() + }, SAFE_RESIZE_TIME_OFFSET) this.scrollDown({ forceRead: true }) }) }) diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss index 13c52ea3..6ae7ebc9 100644 --- a/src/components/chat/chat.scss +++ b/src/components/chat/chat.scss @@ -3,14 +3,17 @@ height: calc(100vh - 60px); width: 100%; + .chat-title { + // prevents chat header jumping on when the user avatar loads + height: 28px; + } + .chat-view-inner { height: auto; width: 100%; overflow: visible; display: flex; - margin-top: 0.5em; - margin-left: 0.5em; - margin-right: 0.5em; + margin: 0.5em 0.5em 0 0.5em; } .chat-view-body { @@ -19,23 +22,18 @@ flex-direction: column; width: 100%; overflow: visible; - border-radius: none; min-height: 100%; - margin-left: 0; - margin-right: 0; - margin-bottom: 0em; - margin-top: 0em; + margin: 0 0 0 0; border-radius: 10px 10px 0 0; border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0 ; &::after { - border-radius: none; - box-shadow: none; + border-radius: 0; } } .scrollable-message-list { - padding: 0 10px; + padding: 0 0.8em; height: 100%; overflow-y: scroll; overflow-x: hidden; @@ -45,7 +43,7 @@ .footer { position: sticky; - bottom: 0px; + bottom: 0; } .chat-view-heading { @@ -54,15 +52,19 @@ top: 50px; display: flex; z-index: 2; - border-radius: none; position: sticky; display: flex; overflow: hidden; } .go-back-button { - margin-right: 1.2em; cursor: pointer; + margin-right: 1.4em; + + i { + display: flex; + align-items: center; + } } .jump-to-bottom-button { @@ -135,7 +137,7 @@ overflow: hidden; height: 100%; margin: 0; - border-radius: 0 !important; + border-radius: 0; } .chat-view-heading { diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue index d8c91dbe..62b72e14 100644 --- a/src/components/chat/chat.vue +++ b/src/components/chat/chat.vue @@ -75,7 +75,7 @@ :disable-polls="true" :disable-sensitivity-checkbox="true" :disable-submit="errorLoadingChat || !currentChat" - :request="sendMessage" + :post-handler="sendMessage" :submit-on-enter="!mobileLayout" :preserve-focus="!mobileLayout" :auto-focus="!mobileLayout" diff --git a/src/components/chat/chat_layout.js b/src/components/chat/chat_layout.js deleted file mode 100644 index 07ae3abf..00000000 --- a/src/components/chat/chat_layout.js +++ /dev/null @@ -1,100 +0,0 @@ -const ChatLayout = { - methods: { - setChatLayout () { - if (this.mobileLayout) { - this.setMobileChatLayout() - } - }, - unsetChatLayout () { - this.unsetMobileChatLayout() - }, - setMobileChatLayout () { - // This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app). - // This layout prevents empty spaces from being visible at the bottom - // of the chat on iOS Safari (`safe-area-inset`) when - // - the on-screen keyboard appears and the user starts typing - // - the user selects the text inside the input area - // - the user selects and deletes the text that is multiple lines long - // TODO: unify the chat layout with the global layout. - - let html = document.querySelector('html') - if (html) { - html.style.overflow = 'hidden' - html.style.height = '100%' - } - - let body = document.querySelector('body') - if (body) { - body.style.height = '100%' - } - - let app = document.getElementById('app') - if (app) { - app.style.height = '100%' - app.style.overflow = 'hidden' - app.style.minHeight = 'auto' - } - - let appBgWrapper = window.document.getElementById('app_bg_wrapper') - if (appBgWrapper) { - appBgWrapper.style.overflow = 'hidden' - } - - let main = document.getElementsByClassName('main')[0] - if (main) { - main.style.overflow = 'hidden' - main.style.height = '100%' - } - - let content = document.getElementById('content') - if (content) { - content.style.paddingTop = '0' - content.style.height = '100%' - content.style.overflow = 'visible' - } - - this.$nextTick(() => { - this.updateScrollableContainerHeight() - }) - }, - unsetMobileChatLayout () { - let html = document.querySelector('html') - if (html) { - html.style.overflow = 'visible' - html.style.height = 'unset' - } - - let body = document.querySelector('body') - if (body) { - body.style.height = 'unset' - } - - let app = document.getElementById('app') - if (app) { - app.style.height = '100%' - app.style.overflow = 'visible' - app.style.minHeight = '100vh' - } - - let appBgWrapper = document.getElementById('app_bg_wrapper') - if (appBgWrapper) { - appBgWrapper.style.overflow = 'visible' - } - - let main = document.getElementsByClassName('main')[0] - if (main) { - main.style.overflow = 'visible' - main.style.height = 'unset' - } - - let content = document.getElementById('content') - if (content) { - content.style.paddingTop = '60px' - content.style.height = 'unset' - content.style.overflow = 'unset' - } - } - } -} - -export default ChatLayout diff --git a/src/components/chat/chat_layout_utils.js b/src/components/chat/chat_layout_utils.js index f07ba2a1..609dc0c9 100644 --- a/src/components/chat/chat_layout_utils.js +++ b/src/components/chat/chat_layout_utils.js @@ -22,6 +22,5 @@ export const isBottomedOut = (el, offset = 0) => { // Height of the scrollable container. The dynamic height is needed to ensure the mobile browser panel doesn't overlap or hide the posting form. export const scrollableContainerHeight = (inner, header, footer) => { - const height = parseFloat(getComputedStyle(inner, null).height.replace('px', '')) - return height - header.clientHeight - footer.clientHeight + return inner.offsetHeight - header.clientHeight - footer.clientHeight } diff --git a/src/components/chat_avatar/chat_avatar.js b/src/components/chat_avatar/chat_avatar.js deleted file mode 100644 index 7b26e07c..00000000 --- a/src/components/chat_avatar/chat_avatar.js +++ /dev/null @@ -1,23 +0,0 @@ -import StillImage from '../still-image/still-image.vue' -import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' -import { mapState } from 'vuex' - -const ChatAvatar = { - props: ['user', 'width', 'height'], - components: { - StillImage - }, - methods: { - getUserProfileLink (user) { - if (!user) { return } - return generateProfileLink(user.id, user.screen_name) - } - }, - computed: { - ...mapState({ - betterShadow: state => state.interface.browserSupport.cssFilter - }) - } -} - -export default ChatAvatar diff --git a/src/components/chat_avatar/chat_avatar.vue b/src/components/chat_avatar/chat_avatar.vue deleted file mode 100644 index f54a7151..00000000 --- a/src/components/chat_avatar/chat_avatar.vue +++ /dev/null @@ -1,53 +0,0 @@ -<template> - <router-link - :to="getUserProfileLink(user) || ''" - > - <StillImage - v-if="user" - :style="{ 'width': width, 'height': height }" - class="avatar chat-avatar single-user" - :alt="user.screen_name" - :title="user.screen_name" - :src="user.profile_image_url_original" - error-src="/images/avi.png" - :class="{ 'better-shadow': betterShadow }" - /> - <div - v-else - class="avatar chat-avatar single-user" - :style="{ 'width': width, 'height': height }" - /> - </router-link> -</template> - -<script src="./chat_avatar.js"></script> -<style lang="scss"> -@import '../../_variables.scss'; - -.chat-avatar { - display: inline-block; - vertical-align: middle; - - &.single-user { - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); - } - - .avatar.still-image { - width: 48px; - height: 48px; - - box-shadow: var(--avatarStatusShadow); - border-radius: 0; - - &.better-shadow { - box-shadow: var(--avatarStatusShadowInset); - filter: var(--avatarStatusShadowFilter) - } - - &.animated::before { - display: none; - } - } -} -</style> diff --git a/src/components/chat_list_item/chat_list_item.js b/src/components/chat_list_item/chat_list_item.js index 1c27088c..b6b0519a 100644 --- a/src/components/chat_list_item/chat_list_item.js +++ b/src/components/chat_list_item/chat_list_item.js @@ -1,7 +1,7 @@ import { mapState } from 'vuex' import StatusContent from '../status_content/status_content.vue' import fileType from 'src/services/file_type/file_type.service' -import ChatAvatar from '../chat_avatar/chat_avatar.vue' +import UserAvatar from '../user_avatar/user_avatar.vue' import AvatarList from '../avatar_list/avatar_list.vue' import Timeago from '../timeago/timeago.vue' import ChatTitle from '../chat_title/chat_title.vue' @@ -12,7 +12,7 @@ const ChatListItem = { 'chat' ], components: { - ChatAvatar, + UserAvatar, AvatarList, Timeago, ChatTitle, diff --git a/src/components/chat_list_item/chat_list_item.scss b/src/components/chat_list_item/chat_list_item.scss index 12269f89..3ec59ea2 100644 --- a/src/components/chat_list_item/chat_list_item.scss +++ b/src/components/chat_list_item/chat_list_item.scss @@ -1,17 +1,8 @@ .chat-list-item { - &:hover .animated.avatar { - canvas { - display: none; - } - img { - visibility: visible; - } - } - display: flex; flex-direction: row; padding: 0.75em; - height: 4.85em; + height: 5em; overflow: hidden; box-sizing: border-box; cursor: pointer; @@ -22,7 +13,7 @@ &:hover { background-color: var(--selectedPost, $fallback--lightBg); - box-shadow: 0 0px 3px 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.1); } .chat-list-item-left { @@ -47,12 +38,6 @@ white-space: nowrap; } - .member-count { - color: $fallback--text; - color: var(--faintText, $fallback--text); - margin-right: 2px; - } - .name-and-account-name { text-overflow: ellipsis; white-space: nowrap; @@ -65,7 +50,7 @@ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - margin: 0.35rem 0; + margin: 0.35em 0; height: 1.2em; line-height: 1.2em; color: $fallback--text; @@ -78,17 +63,24 @@ pointer-events: none; } - .unread-indicator-wrapper { - display: flex; - align-items: center; - margin-left: 10px; + &:hover .animated.avatar { + canvas { + display: none; + } + img { + visibility: visible; + } } - .unread-indicator { - border-radius: 100%; - height: 8px; - width: 8px; - background-color: $fallback--link; - background-color: var(--link, $fallback--link); + .avatar.still-image { + border-radius: $fallback--avatarAltRadius; + border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + } + + .status-body { + img.emoji { + width: 1.4em; + height: 1.4em; + } } } diff --git a/src/components/chat_list_item/chat_list_item.vue b/src/components/chat_list_item/chat_list_item.vue index 26ad581b..640426b8 100644 --- a/src/components/chat_list_item/chat_list_item.vue +++ b/src/components/chat_list_item/chat_list_item.vue @@ -4,7 +4,7 @@ @click.capture.prevent="openChat" > <div class="chat-list-item-left"> - <ChatAvatar + <UserAvatar :user="chat.account" height="48px" width="48px" diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js index aba95074..4d737e42 100644 --- a/src/components/chat_message/chat_message.js +++ b/src/components/chat_message/chat_message.js @@ -66,9 +66,9 @@ const ChatMessage = { } if (this.isCurrentUser) { - res.right = '5px' + res.right = '0.4rem' } else { - res.left = '5px' + res.left = '0.4rem' } return res diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss index e4028537..9d7b7936 100644 --- a/src/components/chat_message/chat_message.scss +++ b/src/components/chat_message/chat_message.scss @@ -12,19 +12,15 @@ } } - &:last-child { - margin-bottom: 16px; - } - .chat-message-menu { transition: opacity 0.1s; opacity: 0; position: absolute; - top: -10px; + top: -0.8em; button { - padding-top: 3px; - padding-bottom: 3px; + padding-top: 0.2em; + padding-bottom: 0.2em; } } @@ -41,21 +37,21 @@ } .popover { - width: 12rem; + width: 12em; } .chat-message { display: flex; - padding-bottom: 7px; + padding-bottom: 0.5em; } .avatar-wrapper { - margin-right: 10px; + margin-right: 0.72em; width: 32px; } .link-preview, .attachments { - margin-bottom: 0.9em; + margin-bottom: 1em; } .chat-message-inner { @@ -63,7 +59,7 @@ flex-direction: column; align-items: flex-start; max-width: 80%; - min-width: 10rem; + min-width: 10em; width: 100%; &.with-media { @@ -87,19 +83,18 @@ } .created-at { + position: relative; float: right; font-size: 0.8em; - margin: -10px 0 -5px 4px; + margin: -1em 0 -0.5em 0; font-style: italic; opacity: 0.8; } .without-attachment { .status-content { - white-space: normal; - &::after { - margin-right: 75px; + margin-right: 5.4em; content: " "; display: inline-block; } diff --git a/src/components/chat_new/chat_new.js b/src/components/chat_new/chat_new.js index 0da681f7..d023efc0 100644 --- a/src/components/chat_new/chat_new.js +++ b/src/components/chat_new/chat_new.js @@ -1,4 +1,3 @@ -import { throttle } from 'lodash' import { mapState, mapGetters } from 'vuex' import BasicUserCard from '../basic_user_card/basic_user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' @@ -54,7 +53,7 @@ const chatNew = { removeUser (userId) { this.selectedUserIds = this.selectedUserIds.filter(id => id !== userId) }, - search: throttle(function (query) { + search (query) { if (!query) { this.loading = false return @@ -67,7 +66,7 @@ const chatNew = { this.loading = false this.userIds = data.accounts.map(a => a.id) }) - }) + } } } diff --git a/src/components/chat_new/chat_new.scss b/src/components/chat_new/chat_new.scss index 39216677..11305444 100644 --- a/src/components/chat_new/chat_new.scss +++ b/src/components/chat_new/chat_new.scss @@ -15,7 +15,7 @@ } .member-list { - padding-bottom: 0.67rem; + padding-bottom: 0.7rem; } .basic-user-card:hover { diff --git a/src/components/chat_title/chat_title.js b/src/components/chat_title/chat_title.js index 2723d5f5..e424bb1f 100644 --- a/src/components/chat_title/chat_title.js +++ b/src/components/chat_title/chat_title.js @@ -1,10 +1,11 @@ import Vue from 'vue' -import ChatAvatar from '../chat_avatar/chat_avatar.vue' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' +import UserAvatar from '../user_avatar/user_avatar.vue' export default Vue.component('chat-title', { name: 'ChatTitle', components: { - ChatAvatar + UserAvatar }, props: [ 'user', 'withAvatar' @@ -16,5 +17,10 @@ export default Vue.component('chat-title', { htmlTitle () { return this.user ? this.user.name_html : '' } + }, + methods: { + getUserProfileLink (user) { + return generateProfileLink(user.id, user.screen_name) + } } }) diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue index fd42d125..cfd1e6d1 100644 --- a/src/components/chat_title/chat_title.vue +++ b/src/components/chat_title/chat_title.vue @@ -4,16 +4,16 @@ class="chat-title" :title="title" > - <ChatAvatar - v-if="withAvatar" - :user="user" - width="23px" - height="23px" - /> - <span - v-if="withAvatar" - style="margin-right: 0.5em" - /> + <router-link + v-if="withAvatar && user" + :to="getUserProfileLink(user)" + > + <UserAvatar + :user="user" + width="23px" + height="23px" + /> + </router-link> <span class="username" v-html="htmlTitle" @@ -32,11 +32,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - - a { - display: flex; - align-items: center; - } + align-items: center; .username { max-width: 100%; @@ -52,5 +48,18 @@ object-fit: contain } } + + .still-image.avatar { + width: 23px; + height: 23px; + margin-right: 0.5em; + + border-radius: $fallback--avatarAltRadius; + border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + + &.animated::before { + display: none; + } + } } </style> diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index a27da090..f0123447 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -88,6 +88,11 @@ const EmojiInput = { required: false, type: String, // 'auto', 'top', 'bottom' default: 'auto' + }, + newlineOnCtrlEnter: { + required: false, + type: Boolean, + default: false } }, data () { @@ -204,7 +209,7 @@ const EmojiInput = { this.$emit('input', newValue) this.caret = 0 }, - insert ({ insertion, keepOpen }) { + insert ({ insertion, keepOpen, surroundingSpace = true }) { const before = this.value.substring(0, this.caret) || '' const after = this.value.substring(this.caret) || '' @@ -223,8 +228,8 @@ const EmojiInput = { * them, masto seem to be rendering :emoji::emoji: correctly now so why not */ const isSpaceRegex = /\s/ - const spaceBefore = !isSpaceRegex.exec(before.slice(-1)) && before.length && this.padEmoji > 0 ? ' ' : '' - const spaceAfter = !isSpaceRegex.exec(after[0]) && this.padEmoji ? ' ' : '' + const spaceBefore = (surroundingSpace && !isSpaceRegex.exec(before.slice(-1)) && before.length && this.padEmoji > 0) ? ' ' : '' + const spaceAfter = (surroundingSpace && !isSpaceRegex.exec(after[0]) && this.padEmoji) ? ' ' : '' const newValue = [ before, @@ -381,6 +386,18 @@ const EmojiInput = { }, onKeyDown (e) { const { ctrlKey, shiftKey, key } = e + if (this.newlineOnCtrlEnter && ctrlKey && key === 'Enter') { + this.insert({ insertion: '\n', surroundingSpace: false }) + // Ensure only one new line is added on macos + e.stopPropagation() + e.preventDefault() + + // Scroll the input element to the position of the cursor + this.$nextTick(() => { + this.input.elm.blur() + this.input.elm.focus() + }) + } // Disable suggestions hotkeys if suggestions are hidden if (!this.temporarilyHideSuggestions) { if (key === 'Tab') { diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue index d719eae1..c8865d77 100644 --- a/src/components/media_upload/media_upload.vue +++ b/src/components/media_upload/media_upload.vue @@ -33,19 +33,6 @@ @import '../../_variables.scss'; .media-upload { - &.disabled { - .new-icon { - cursor: not-allowed; - } - - &:hover { - i, label { - color: $fallback--faint; - color: var(--faint, $fallback--faint); - } - } - } - .label { display: inline-block; } diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 90d0fa81..59e4dc26 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -43,7 +43,7 @@ const PostStatusForm = { 'disableSubmit', 'placeholder', 'maxHeight', - 'request', + 'postHandler', 'preserveFocus', 'autoFocus', 'fileLimit', @@ -221,10 +221,6 @@ const PostStatusForm = { event.stopPropagation() event.preventDefault() } - if (opts.control && this.submitOnEnter) { - newStatus.status = `${newStatus.status}\n` - return - } if (this.emptyStatus) { this.error = this.$t('post_status.empty_status_error') @@ -259,9 +255,9 @@ const PostStatusForm = { poll } - const request = this.request ? this.request : statusPoster.postStatus + const postHandler = this.postHandler ? this.postHandler : statusPoster.postStatus - request(postingOptions).then((data) => { + postHandler(postingOptions).then((data) => { if (!data.error) { this.newStatus = { status: '', @@ -345,11 +341,7 @@ const PostStatusForm = { }, addMediaFile (fileInfo) { this.newStatus.files.push(fileInfo) - - // TODO: use fixed dimensions instead so relying on timeout - setTimeout(() => { - this.$emit('resize') - }, 150) + this.$emit('resize', { delayed: true }) }, removeMediaFile (fileInfo) { let index = this.newStatus.files.indexOf(fileInfo) @@ -364,6 +356,7 @@ const PostStatusForm = { this.uploadingFiles = true }, finishedUploadingFiles () { + this.$emit('resize') this.uploadingFiles = false }, type (fileInfo) { @@ -417,7 +410,7 @@ const PostStatusForm = { // Reset to default height for empty form, nothing else to do here. if (target.value === '') { target.style.height = null - this.$emit('resize', null) + this.$emit('resize') this.$refs['emoji-input'].resize() return } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index d8df68d6..7454958b 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -131,6 +131,7 @@ class="form-control main-input" enable-emoji-picker hide-emoji-button + :newline-on-ctrl-enter="submitOnEnter" enable-sticker-picker @input="onEmojiInputInput" @sticker-uploaded="addMediaFile" @@ -146,8 +147,8 @@ class="form-post-body" :class="{ 'scrollable-form': !!maxHeight }" @keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)" - @keydown.meta.enter="postStatus($event, newStatus, { control: true })" - @keydown.ctrl.enter="postStatus($event, newStatus)" + @keydown.meta.enter="postStatus($event, newStatus)" + @keydown.ctrl.enter="!submitOnEnter && postStatus($event, newStatus)" @input="resize" @compositionupdate="resize" @paste="paste" @@ -435,6 +436,19 @@ color: var(--lightText, $fallback--lightText); } } + + &.disabled { + i { + cursor: not-allowed; + color: $fallback--icon; + color: var(--btnDisabledText, $fallback--icon); + + &:hover { + color: $fallback--icon; + color: var(--btnDisabledText, $fallback--icon); + } + } + } } // Order is not necessary but a good indicator @@ -628,7 +642,7 @@ } // todo: unify with attachment.vue (otherwise the uploaded images are not minified unless a status with an attachment was displayed before) -img.media-upload { +img.media-upload, .media-upload-container > video { line-height: 0; max-height: 200px; max-width: 100%; |
