diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/conversation-page/conversation-page.vue | 6 | ||||
| -rw-r--r-- | src/components/conversation/conversation.js | 70 | ||||
| -rw-r--r-- | src/components/conversation/conversation.vue | 50 | ||||
| -rw-r--r-- | src/components/emoji-input/emoji-input.js | 107 | ||||
| -rw-r--r-- | src/components/emoji-input/emoji-input.vue | 64 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 4 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 70 | ||||
| -rw-r--r-- | src/components/side_drawer/side_drawer.js | 18 | ||||
| -rw-r--r-- | src/components/side_drawer/side_drawer.vue | 24 | ||||
| -rw-r--r-- | src/components/status/status.js | 1 | ||||
| -rw-r--r-- | src/components/status/status.vue | 10 | ||||
| -rw-r--r-- | src/components/status_or_conversation/status_or_conversation.js | 22 | ||||
| -rw-r--r-- | src/components/status_or_conversation/status_or_conversation.vue | 14 | ||||
| -rw-r--r-- | src/components/timeline/timeline.js | 4 | ||||
| -rw-r--r-- | src/components/timeline/timeline.vue | 8 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.js | 4 | ||||
| -rw-r--r-- | src/components/user_settings/user_settings.vue | 29 |
17 files changed, 345 insertions, 160 deletions
diff --git a/src/components/conversation-page/conversation-page.vue b/src/components/conversation-page/conversation-page.vue index b03eea28..9e322cf5 100644 --- a/src/components/conversation-page/conversation-page.vue +++ b/src/components/conversation-page/conversation-page.vue @@ -1,5 +1,9 @@ <template> - <conversation :collapsable="false" :statusoid="statusoid"></conversation> + <conversation + :collapsable="false" + isPage="true" + :statusoid="statusoid" + ></conversation> </template> <script src="./conversation-page.js"></script> diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index e806be8e..69058bf6 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,10 +1,12 @@ -import { reduce, filter } from 'lodash' +import { reduce, filter, findIndex } from 'lodash' import { set } from 'vue' import Status from '../status/status.vue' const sortById = (a, b) => { - const seqA = Number(a.id) - const seqB = Number(b.id) + const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id + const idB = b.type === 'retweet' ? b.retweeted_status.id : b.id + const seqA = Number(idA) + const seqB = Number(idB) const isSeqA = !Number.isNaN(seqA) const isSeqB = !Number.isNaN(seqB) if (isSeqA && isSeqB) { @@ -14,12 +16,19 @@ const sortById = (a, b) => { } else if (!isSeqA && isSeqB) { return 1 } else { - return a.id < b.id ? -1 : 1 + return idA < idB ? -1 : 1 } } -const sortAndFilterConversation = (conversation) => { - conversation = filter(conversation, (status) => status.type !== 'retweet') +const sortAndFilterConversation = (conversation, statusoid) => { + if (statusoid.type === 'retweet') { + conversation = filter( + conversation, + (status) => (status.type === 'retweet' || status.id !== statusoid.retweeted_status.id) + ) + } else { + conversation = filter(conversation, (status) => status.type !== 'retweet') + } return conversation.filter(_ => _).sort(sortById) } @@ -27,13 +36,20 @@ const conversation = { data () { return { highlight: null, + expanded: false, converationStatusIds: [] } }, props: [ 'statusoid', - 'collapsable' + 'collapsable', + 'isPage' ], + created () { + if (this.isPage) { + this.fetchConversation() + } + }, computed: { status () { return this.statusoid @@ -59,12 +75,22 @@ const conversation = { return [] } + if (!this.isExpanded) { + return [this.status] + } + const statusesObject = this.$store.state.statuses.allStatusesObject const conversation = this.idsToShow.reduce((acc, id) => { acc.push(statusesObject[id]) return acc }, []) - return sortAndFilterConversation(conversation) + + const statusIndex = findIndex(conversation, { id: this.statusId }) + if (statusIndex !== -1) { + conversation[statusIndex] = this.status + } + + return sortAndFilterConversation(conversation, this.status) }, replies () { let i = 1 @@ -82,16 +108,21 @@ const conversation = { i++ return result }, {}) + }, + isExpanded () { + return this.expanded || this.isPage } }, components: { Status }, - created () { - this.fetchConversation() - }, watch: { - '$route': 'fetchConversation' + '$route': 'fetchConversation', + expanded (value) { + if (value) { + this.fetchConversation() + } + } }, methods: { fetchConversation () { @@ -101,9 +132,9 @@ const conversation = { this.$store.dispatch('addNewStatuses', { statuses: ancestors }) this.$store.dispatch('addNewStatuses', { statuses: descendants }) set(this, 'converationStatusIds', [].concat( - ancestors.map(_ => _.id), + ancestors.map(_ => _.id).filter(_ => _ !== this.statusId), this.statusId, - descendants.map(_ => _.id))) + descendants.map(_ => _.id).filter(_ => _ !== this.statusId))) }) .then(() => this.setHighlight(this.statusId)) } else { @@ -117,10 +148,19 @@ const conversation = { return this.replies[id] || [] }, focused (id) { - return id === this.statusId + return (this.isExpanded) && id === this.status.id }, setHighlight (id) { this.highlight = id + }, + getHighlight () { + return this.isExpanded ? this.highlight : null + }, + toggleExpanded () { + this.expanded = !this.expanded + if (!this.expanded) { + this.setHighlight(null) + } } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 5528fef6..c39a3ed9 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -1,26 +1,42 @@ <template> - <div class="timeline panel panel-default"> - <div class="panel-heading conversation-heading"> + <div class="timeline panel-default" :class="[isExpanded ? 'panel' : 'panel-disabled']"> + <div v-if="isExpanded" class="panel-heading conversation-heading"> <span class="title"> {{ $t('timeline.conversation') }} </span> <span v-if="collapsable"> - <a href="#" @click.prevent="$emit('toggleExpanded')">{{ $t('timeline.collapse') }}</a> + <a href="#" @click.prevent="toggleExpanded">{{ $t('timeline.collapse') }}</a> </span> </div> - <div class="panel-body"> - <div class="timeline"> - <status - v-for="status in conversation" - @goto="setHighlight" :key="status.id" - :inlineExpanded="collapsable" :statusoid="status" - :expandable='false' :focused="focused(status.id)" - :inConversation='true' - :highlight="highlight" - :replies="getReplies(status.id)" - class="status-fadein"> - </status> - </div> - </div> + <status + v-for="status in conversation" + @goto="setHighlight" + @toggleExpanded="toggleExpanded" + :key="status.id" + :inlineExpanded="collapsable" + :statusoid="status" + :expandable='!expanded' + :focused="focused(status.id)" + :inConversation="isExpanded" + :highlight="getHighlight()" + :replies="getReplies(status.id)" + class="status-fadein panel-body" + /> </div> </template> <script src="./conversation.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.timeline { + .panel-disabled { + .status-el { + border-left: none; + border-bottom-width: 1px; + border-bottom-style: solid; + border-color: var(--border, $fallback--border); + border-radius: 0; + } + } +} +</style> diff --git a/src/components/emoji-input/emoji-input.js b/src/components/emoji-input/emoji-input.js new file mode 100644 index 00000000..a5bb6eaf --- /dev/null +++ b/src/components/emoji-input/emoji-input.js @@ -0,0 +1,107 @@ +import Completion from '../../services/completion/completion.js' +import { take, filter, map } from 'lodash' + +const EmojiInput = { + props: [ + 'value', + 'placeholder', + 'type', + 'classname' + ], + data () { + return { + highlighted: 0, + caret: 0 + } + }, + computed: { + suggestions () { + const firstchar = this.textAtCaret.charAt(0) + if (firstchar === ':') { + if (this.textAtCaret === ':') { return } + const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.startsWith(this.textAtCaret.slice(1))) + if (matchedEmoji.length <= 0) { + return false + } + return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({ + shortcode: `:${shortcode}:`, + utf: utf || '', + // eslint-disable-next-line camelcase + img: utf ? '' : this.$store.state.instance.server + image_url, + highlighted: index === this.highlighted + })) + } else { + return false + } + }, + textAtCaret () { + return (this.wordAtCaret || {}).word || '' + }, + wordAtCaret () { + const word = Completion.wordAtPosition(this.value, this.caret - 1) || {} + return word + }, + emoji () { + return this.$store.state.instance.emoji || [] + }, + customEmoji () { + return this.$store.state.instance.customEmoji || [] + } + }, + methods: { + replace (replacement) { + const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement) + this.$emit('input', newValue) + this.caret = 0 + }, + replaceEmoji (e) { + const len = this.suggestions.length || 0 + if (this.textAtCaret === ':' || e.ctrlKey) { return } + if (len > 0) { + e.preventDefault() + const emoji = this.suggestions[this.highlighted] + const replacement = emoji.utf || (emoji.shortcode + ' ') + const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement) + this.$emit('input', newValue) + this.caret = 0 + this.highlighted = 0 + } + }, + cycleBackward (e) { + const len = this.suggestions.length || 0 + if (len > 0) { + e.preventDefault() + this.highlighted -= 1 + if (this.highlighted < 0) { + this.highlighted = this.suggestions.length - 1 + } + } else { + this.highlighted = 0 + } + }, + cycleForward (e) { + const len = this.suggestions.length || 0 + if (len > 0) { + if (e.shiftKey) { return } + e.preventDefault() + this.highlighted += 1 + if (this.highlighted >= len) { + this.highlighted = 0 + } + } else { + this.highlighted = 0 + } + }, + onKeydown (e) { + e.stopPropagation() + }, + onInput (e) { + this.$emit('input', e.target.value) + }, + setCaret ({target: {selectionStart}}) { + this.caret = selectionStart + } + } +} + +export default EmojiInput diff --git a/src/components/emoji-input/emoji-input.vue b/src/components/emoji-input/emoji-input.vue new file mode 100644 index 00000000..338b77cd --- /dev/null +++ b/src/components/emoji-input/emoji-input.vue @@ -0,0 +1,64 @@ +<template> + <div class="emoji-input"> + <input + v-if="type !== 'textarea'" + :class="classname" + :type="type" + :value="value" + :placeholder="placeholder" + @input="onInput" + @click="setCaret" + @keyup="setCaret" + @keydown="onKeydown" + @keydown.down="cycleForward" + @keydown.up="cycleBackward" + @keydown.shift.tab="cycleBackward" + @keydown.tab="cycleForward" + @keydown.enter="replaceEmoji" + /> + <textarea + v-else + :class="classname" + :value="value" + :placeholder="placeholder" + @input="onInput" + @click="setCaret" + @keyup="setCaret" + @keydown="onKeydown" + @keydown.down="cycleForward" + @keydown.up="cycleBackward" + @keydown.shift.tab="cycleBackward" + @keydown.tab="cycleForward" + @keydown.enter="replaceEmoji" + ></textarea> + <div class="autocomplete-panel" v-if="suggestions"> + <div class="autocomplete-panel-body"> + <div + v-for="(emoji, index) in suggestions" + :key="index" + @click="replace(emoji.utf || (emoji.shortcode + ' '))" + class="autocomplete-item" + :class="{ highlighted: emoji.highlighted }" + > + <span v-if="emoji.img"> + <img :src="emoji.img" /> + </span> + <span v-else>{{emoji.utf}}</span> + <span>{{emoji.shortcode}}</span> + </div> + </div> + </div> + </div> +</template> + +<script src="./emoji-input.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.emoji-input { + .form-control { + width: 100%; + } +} +</style> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index c5f30ca6..229aefb7 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -1,5 +1,6 @@ import statusPoster from '../../services/status_poster/status_poster.service.js' import MediaUpload from '../media_upload/media_upload.vue' +import EmojiInput from '../emoji-input/emoji-input.vue' import fileTypeService from '../../services/file_type/file_type.service.js' import Completion from '../../services/completion/completion.js' import { take, filter, reject, map, uniqBy } from 'lodash' @@ -28,7 +29,8 @@ const PostStatusForm = { 'subject' ], components: { - MediaUpload + MediaUpload, + EmojiInput }, mounted () { this.resize(this.$refs.textarea) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 612f87c1..9f9f16ba 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -10,12 +10,13 @@ <router-link :to="{ name: 'user-settings' }">{{ $t('post_status.account_not_locked_warning_link') }}</router-link> </i18n> <p v-if="this.newStatus.visibility == 'direct'" class="visibility-notice">{{ $t('post_status.direct_warning') }}</p> - <input + <EmojiInput v-if="newStatus.spoilerText || alwaysShowSubject" type="text" :placeholder="$t('post_status.content_warning')" v-model="newStatus.spoilerText" - class="form-cw"> + classname="form-control" + /> <textarea ref="textarea" @click="setCaret" @@ -55,14 +56,18 @@ </div> </div> </div> - <div style="position:relative;" v-if="candidates"> - <div class="autocomplete-panel"> - <div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))"> - <div class="autocomplete" :class="{ highlighted: candidate.highlighted }"> - <span v-if="candidate.img"><img :src="candidate.img"></img></span> - <span v-else>{{candidate.utf}}</span> - <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span> - </div> + <div class="autocomplete-panel" v-if="candidates"> + <div class="autocomplete-panel-body"> + <div + v-for="(candidate, index) in candidates" + :key="index" + @click="replace(candidate.utf || (candidate.screen_name + ' '))" + class="autocomplete-item" + :class="{ highlighted: candidate.highlighted }" + > + <span v-if="candidate.img"><img :src="candidate.img" /></span> + <span v-else>{{candidate.utf}}</span> + <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span> </div> </div> </div> @@ -261,50 +266,5 @@ cursor: pointer; z-index: 4; } - - .autocomplete-panel { - margin: 0 0.5em 0 0.5em; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - position: absolute; - z-index: 1; - box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); - // this doesn't match original but i don't care, making it uniform. - box-shadow: var(--popupShadow); - min-width: 75%; - background: $fallback--bg; - background: var(--bg, $fallback--bg); - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } - - .autocomplete { - cursor: pointer; - padding: 0.2em 0.4em 0.2em 0.4em; - border-bottom: 1px solid rgba(0, 0, 0, 0.4); - display: flex; - - img { - width: 24px; - height: 24px; - object-fit: contain; - } - - span { - line-height: 24px; - margin: 0 0.1em 0 0.2em; - } - - small { - margin-left: .5em; - color: $fallback--faint; - color: var(--faint, $fallback--faint); - } - - &.highlighted { - background-color: $fallback--fg; - background-color: var(--lightBg, $fallback--fg); - } - } } </style> diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index ad3738d1..567d2e5e 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -1,17 +1,16 @@ import UserCard from '../user_card/user_card.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' - -// TODO: separate touch gesture stuff into their own utils if more components want them -const deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]] - -const touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY]) +import GestureService from '../../services/gesture_service/gesture_service' const SideDrawer = { props: [ 'logout' ], data: () => ({ closed: true, - touchCoord: [0, 0] + closeGesture: undefined }), + created () { + this.closeGesture = GestureService.swipeGesture(GestureService.DIRECTION_LEFT, this.toggleDrawer) + }, components: { UserCard }, computed: { currentUser () { @@ -46,13 +45,10 @@ const SideDrawer = { this.toggleDrawer() }, touchStart (e) { - this.touchCoord = touchEventCoord(e) + GestureService.beginSwipe(e, this.closeGesture) }, touchMove (e) { - const delta = deltaCoord(this.touchCoord, touchEventCoord(e)) - if (delta[0] < -30 && Math.abs(delta[1]) < Math.abs(delta[0]) && !this.closed) { - this.toggleDrawer() - } + GestureService.updateSwipe(e, this.closeGesture) } } } diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 95ee21b4..e5046496 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -2,6 +2,7 @@ <div class="side-drawer-container" :class="{ 'side-drawer-container-closed': closed, 'side-drawer-container-open': !closed }" > + <div class="side-drawer-darken" :class="{ 'side-drawer-darken-closed': closed}" /> <div class="side-drawer" :class="{'side-drawer-closed': closed}" @touchstart="touchStart" @@ -111,16 +112,32 @@ height: 100%; display: flex; align-items: stretch; + transition-duration: 0s; + transition-property: transform; } .side-drawer-container-open { + transform: translate(0%); +} + +.side-drawer-container-closed { + transition-delay: 0.35s; + transform: translate(-100%); +} + +.side-drawer-darken { + top: 0; + left: 0; + width: 100vw; + height: 100vh; + position: fixed; + z-index: -1; transition: 0.35s; transition-property: background-color; background-color: rgba(0, 0, 0, 0.5); } -.side-drawer-container-closed { - left: -100%; +.side-drawer-darken-closed { background-color: rgba(0, 0, 0, 0); } @@ -130,8 +147,9 @@ .side-drawer { overflow-x: hidden; - transition: 0.35s; transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + transition: 0.35s; + transition-property: transform; margin: 0 0 0 -100px; padding: 0 0 1em 100px; width: 80%; diff --git a/src/components/status/status.js b/src/components/status/status.js index c90da6d4..550fe76f 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -310,7 +310,6 @@ const Status = { this.replying = !this.replying }, gotoOriginal (id) { - // only handled by conversation, not status_or_conversation if (this.inConversation) { this.$emit('goto', id) } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 1f6d0325..1f415534 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -12,7 +12,7 @@ </div> </template> <template v-else> - <div v-if="retweet && !noHeading" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info"> + <div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info"> <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/> <div class="media-body faint"> <span class="user-name"> @@ -24,7 +24,7 @@ </div> </div> - <div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet }]" :style="[ userStyle ]" class="media status"> + <div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet && !inConversation }]" :style="[ userStyle ]" class="media status"> <div v-if="!noHeading" class="media-left"> <router-link :to="userProfileLink" @click.stop.prevent.capture.native="toggleUserExpanded"> <UserAvatar :compact="compact" :betterShadow="betterShadow" :src="status.user.profile_image_url_original"/> @@ -135,9 +135,8 @@ <div v-if="!noHeading && !isPreview" class='status-actions media-body'> <div v-if="loggedIn"> - <a href="#" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')"> - <i class="button-icon icon-reply" :class="{'icon-reply-active': replying}"></i> - </a> + <i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'icon-reply-active': replying}"></i> + <span v-if="status.replies_count > 0">{{status.replies_count}}</span> </div> <retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button> <favorite-button :loggedIn='loggedIn' :status='status'></favorite-button> @@ -551,6 +550,7 @@ $status-margin: 0.75em; .icon-reply:hover { color: $fallback--cBlue; color: var(--cBlue, $fallback--cBlue); + cursor: pointer; } .icon-reply.icon-reply-active { diff --git a/src/components/status_or_conversation/status_or_conversation.js b/src/components/status_or_conversation/status_or_conversation.js deleted file mode 100644 index 441552ca..00000000 --- a/src/components/status_or_conversation/status_or_conversation.js +++ /dev/null @@ -1,22 +0,0 @@ -import Status from '../status/status.vue' -import Conversation from '../conversation/conversation.vue' - -const statusOrConversation = { - props: ['statusoid'], - data () { - return { - expanded: false - } - }, - components: { - Status, - Conversation - }, - methods: { - toggleExpanded () { - this.expanded = !this.expanded - } - } -} - -export default statusOrConversation diff --git a/src/components/status_or_conversation/status_or_conversation.vue b/src/components/status_or_conversation/status_or_conversation.vue deleted file mode 100644 index 9647d5eb..00000000 --- a/src/components/status_or_conversation/status_or_conversation.vue +++ /dev/null @@ -1,14 +0,0 @@ -<template> - <div> - <conversation v-if="expanded" @toggleExpanded="toggleExpanded" :collapsable="true" :statusoid="statusoid"></conversation> - <status v-if="!expanded" @toggleExpanded="toggleExpanded" :expandable="true" :inConversation="false" :focused="false" :statusoid="statusoid"></status> - </div> -</template> - -<script src="./status_or_conversation.js"></script> - -<style lang="scss"> - .spacer { - height: 1em - } -</style> diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index c45f8947..1da7d5cc 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -1,6 +1,6 @@ import Status from '../status/status.vue' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' -import StatusOrConversation from '../status_or_conversation/status_or_conversation.vue' +import Conversation from '../conversation/conversation.vue' import { throttle } from 'lodash' const Timeline = { @@ -43,7 +43,7 @@ const Timeline = { }, components: { Status, - StatusOrConversation + Conversation }, created () { const store = this.$store diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 8f28d65c..e0a34bd1 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -16,7 +16,13 @@ </div> <div :class="classes.body"> <div class="timeline"> - <status-or-conversation v-for="status in timeline.visibleStatuses" :key="status.id" v-bind:statusoid="status" class="status-fadein"></status-or-conversation> + <conversation + v-for="status in timeline.visibleStatuses" + class="status-fadein" + :key="status.id" + :statusoid="status" + :collapsable="true" + /> </div> </div> <div :class="classes.footer"> diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index 72e7bb53..5cb23b97 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -7,6 +7,7 @@ import StyleSwitcher from '../style_switcher/style_switcher.vue' import fileSizeFormatService from '../../services/file_size_format/file_size_format.js' import BlockCard from '../block_card/block_card.vue' import MuteCard from '../mute_card/mute_card.vue' +import EmojiInput from '../emoji-input/emoji-input.vue' import withSubscription from '../../hocs/with_subscription/with_subscription' import withList from '../../hocs/with_list/with_list' @@ -69,7 +70,8 @@ const UserSettings = { TabSwitcher, ImageCropper, BlockList, - MuteList + MuteList, + EmojiInput }, computed: { user () { diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index c9e68808..52df143c 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -22,9 +22,18 @@ <div class="setting-item" > <h2>{{$t('settings.name_bio')}}</h2> <p>{{$t('settings.name')}}</p> - <input class='name-changer' id='username' v-model="newName"></input> + <EmojiInput + type="text" + v-model="newName" + id="username" + classname="name-changer" + /> <p>{{$t('settings.bio')}}</p> - <textarea class="bio" v-model="newBio"></textarea> + <EmojiInput + type="textarea" + v-model="newBio" + classname="bio" + /> <p> <input type="checkbox" v-model="newLocked" id="account-locked"> <label for="account-locked">{{$t('settings.lock_account_description')}}</label> @@ -61,7 +70,7 @@ <h2>{{$t('settings.avatar')}}</h2> <p class="visibility-notice">{{$t('settings.avatar_size_instruction')}}</p> <p>{{$t('settings.current_avatar')}}</p> - <img :src="user.profile_image_url_original" class="current-avatar"></img> + <img :src="user.profile_image_url_original" class="current-avatar" /> <p>{{$t('settings.set_new_avatar')}}</p> <button class="btn" type="button" id="pick-avatar" v-show="pickAvatarBtnVisible">{{$t('settings.upload_a_photo')}}</button> <image-cropper trigger="#pick-avatar" :submitHandler="submitAvatar" @open="pickAvatarBtnVisible=false" @close="pickAvatarBtnVisible=true" /> @@ -69,12 +78,11 @@ <div class="setting-item"> <h2>{{$t('settings.profile_banner')}}</h2> <p>{{$t('settings.current_profile_banner')}}</p> - <img :src="user.cover_photo" class="banner"></img> + <img :src="user.cover_photo" class="banner" /> <p>{{$t('settings.set_new_profile_banner')}}</p> - <img class="banner" v-bind:src="bannerPreview" v-if="bannerPreview"> - </img> + <img class="banner" v-bind:src="bannerPreview" v-if="bannerPreview" /> <div> - <input type="file" @change="uploadFile('banner', $event)" ></input> + <input type="file" @change="uploadFile('banner', $event)" /> </div> <i class=" icon-spin4 animate-spin uploading" v-if="bannerUploading"></i> <button class="btn btn-default" v-else-if="bannerPreview" @click="submitBanner">{{$t('general.submit')}}</button> @@ -86,10 +94,9 @@ <div class="setting-item"> <h2>{{$t('settings.profile_background')}}</h2> <p>{{$t('settings.set_new_profile_background')}}</p> - <img class="bg" v-bind:src="backgroundPreview" v-if="backgroundPreview"> - </img> + <img class="bg" v-bind:src="backgroundPreview" v-if="backgroundPreview" /> <div> - <input type="file" @change="uploadFile('background', $event)" ></input> + <input type="file" @change="uploadFile('background', $event)" /> </div> <i class=" icon-spin4 animate-spin uploading" v-if="backgroundUploading"></i> <button class="btn btn-default" v-else-if="backgroundPreview" @click="submitBg">{{$t('general.submit')}}</button> @@ -165,7 +172,7 @@ <h2>{{$t('settings.follow_import')}}</h2> <p>{{$t('settings.import_followers_from_a_csv_file')}}</p> <form> - <input type="file" ref="followlist" v-on:change="followListChange"></input> + <input type="file" ref="followlist" v-on:change="followListChange" /> </form> <i class=" icon-spin4 animate-spin uploading" v-if="followListUploading"></i> <button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button> |
