Compare commits

..

23 commits

Author SHA1 Message Date
Ilja e594252668 clean up leftover
There was a comment to enable something for eslint. This was a leftover from when it was dissabled.
But the dissabling was removed in a privious commit f9393b0dab
2022-03-21 09:59:52 +01:00
Ilja 88ad5033a8 Use empty array for emji instead
Instead of relying on a key that isn't actually usefull, I just provide an empty array directly.
2022-03-21 09:49:06 +01:00
Ilja 89c409c6d2 Add default array for RichContent emoji
Reports don't currently return an emoji key. There's an MR to add it, but in case something doesn't return this key, we now have a default empty array.
2022-03-21 08:57:59 +01:00
Ilja d0bfd9a808 Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma-fe into feat/report-notification 2022-03-20 09:53:57 +01:00
Ilja f9393b0dab Use RichContent component for Reports
Note that this expects an emoji list for the reports. I made an MR in BE to provide that: https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3650
2022-03-20 09:45:35 +01:00
Ilja 1d42d05c1f Use Select component
After merging develop, the dropdown didn't format properly any more because selects have been made into a component. Here I turn the select into a component as well.
2022-03-20 09:31:36 +01:00
Ilja c9e4b6e7a1 Make linter happy
`npm run lint` gave warnings for two files, fixed them with `./node_modules/.bin/eslint --fix $FILENAME`
2022-02-26 02:13:43 +01:00
Ilja d0c4ad22cd Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma-fe into feat/report-notification 2022-02-26 02:08:13 +01:00
Ilja 819b760261 Fix up and code review
* Check if it works properly
    * Notifs are shown as BE returns them
    * The Interaction view has Reports, but only when you're mod or admin
* Do some extra translations
* Fix some console spam
2022-02-26 01:53:01 +01:00
Shpuld Shpuldson 52c22e863e Fix setting report state, add proper error handling 2021-02-03 12:02:37 +02:00
Shpuld Shpuldson 3e6309ef94 remove logs 2021-02-01 14:43:01 +02:00
Shpuld Shpuldson 3822a73a49 Fix report modal not working, add include_types 2021-02-01 12:55:23 +02:00
Shpuld Shpuldson 8334649c11 Merge branch 'develop' into feat/report-notification 2021-01-27 13:24:19 +02:00
Shpuld Shpuldson 54def7d210 remove mock data 2021-01-27 13:13:59 +02:00
Shpuld Shpuldson 06f795d1d6 add proper state switcher 2021-01-27 13:13:10 +02:00
Shpuld Shpuldson e73553dca7 wip 2021-01-18 15:26:08 +02:00
Shpuld Shpuldson 9613f80f8e Merge branch 'develop' into feat/report-notification 2021-01-13 12:53:53 +02:00
Shpuld Shpuldson 8674f20023 separated component 2021-01-11 19:32:58 +02:00
Shpuld Shpuldson a4e3cccf1c somewhat workign version still with fixture 2021-01-06 18:31:34 +02:00
Shpuld Shpuldson ab2c2c66bf Merge branch 'develop' into feat/report-notification 2021-01-05 13:58:52 +02:00
Shpuld Shpuldson 5e96260a4f add test data for dev 2020-12-04 12:48:15 +02:00
Shpuld Shpuldson 1fd1553a1c Merge branch 'develop' into feat/report-notification 2020-12-04 11:20:01 +02:00
Shpuld Shpuldson 15bed586dc report notification wip 2020-11-15 13:57:02 +02:00
148 changed files with 2327 additions and 2519 deletions

View file

@ -1,5 +1,5 @@
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"],
"presets": ["@babel/preset-env", "@vue/babel-preset-jsx"],
"plugins": ["@babel/plugin-transform-runtime", "lodash"],
"comments": false
}

View file

@ -1,7 +1,7 @@
# This file is a template, and might need editing before it works on your project.
# Official framework image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/node/tags/
image: node:12
image: node:10
stages:
- lint

View file

@ -4,7 +4,6 @@ var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
var CopyPlugin = require('copy-webpack-plugin');
var { VueLoaderPlugin } = require('vue-loader')
var env = process.env.NODE_ENV
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
@ -30,16 +29,16 @@ module.exports = {
}
},
resolve: {
extensions: ['.js', '.jsx', '.vue'],
extensions: ['.js', '.vue'],
modules: [
path.join(__dirname, '../node_modules')
],
alias: {
'vue$': 'vue/dist/vue.runtime.common',
'static': path.resolve(__dirname, '../static'),
'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components'),
'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js'
'components': path.resolve(__dirname, '../src/components')
}
},
module: {
@ -59,28 +58,9 @@ module.exports = {
}
}
},
{
enforce: 'post',
test: /\.(json5?|ya?ml)$/, // target json, json5, yaml and yml files
type: 'javascript/auto',
loader: '@intlify/vue-i18n-loader',
include: [ // Use `Rule.include` to specify the files of locale messages to be pre-compiled
path.resolve(__dirname, '../src/i18n')
]
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
isCustomElement(tag) {
if (tag === 'pinch-zoom') {
return true
}
return false
}
}
}
use: 'vue-loader'
},
{
test: /\.jsx?$/,
@ -115,7 +95,6 @@ module.exports = {
entry: path.join(__dirname, '..', 'src/sw.js'),
filename: 'sw-pleroma.js'
}),
new VueLoaderPlugin(),
// This copies Ruffle's WASM to a directory so that JS side can access it
new CopyPlugin({
patterns: [

View file

@ -21,9 +21,7 @@ module.exports = merge(baseWebpackConfig, {
new webpack.DefinePlugin({
'process.env': config.dev.env,
'COMMIT_HASH': JSON.stringify('DEV'),
'DEV_OVERRIDES': JSON.stringify(config.dev.settings),
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_DEVTOOLS__': false
'DEV_OVERRIDES': JSON.stringify(config.dev.settings)
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),

View file

@ -36,9 +36,7 @@ var webpackConfig = merge(baseWebpackConfig, {
new webpack.DefinePlugin({
'process.env': env,
'COMMIT_HASH': JSON.stringify(commitHash),
'DEV_OVERRIDES': JSON.stringify(undefined),
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_DEVTOOLS__': false
'DEV_OVERRIDES': JSON.stringify(undefined)
}),
// extract css into its own file
new MiniCssExtractPlugin({

View file

@ -52,10 +52,7 @@ module.exports = {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
ws: true,
headers: {
'Origin': target
}
ws: true
},
'/oauth/revoke': {
target,

View file

@ -16,47 +16,44 @@
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
},
"dependencies": {
"@babel/runtime": "7.17.8",
"@chenfengyuan/vue-qrcode": "2.0.0",
"@babel/runtime": "7.17.7",
"@chenfengyuan/vue-qrcode": "1.0.2",
"@fortawesome/fontawesome-svg-core": "1.3.0",
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/vue-fontawesome": "3.0.0-5",
"@fortawesome/vue-fontawesome": "2.0.6",
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
"@vuelidate/core": "2.0.0-alpha.35",
"@vuelidate/validators": "2.0.0-alpha.27",
"body-scroll-lock": "2.7.1",
"chromatism": "3.0.0",
"click-outside-vue3": "4.0.1",
"cropperjs": "1.5.12",
"diff": "3.5.0",
"escape-html": "1.0.3",
"localforage": "1.10.0",
"localforage": "1.7.3",
"parse-link-header": "1.0.1",
"phoenix": "1.6.2",
"phoenix": "1.4.0",
"portal-vue": "2.1.7",
"punycode.js": "2.1.0",
"qrcode": "1",
"ruffle-mirror": "2021.12.31",
"vue": "^3.2.31",
"vue-i18n": "^9.2.0-beta.34",
"vue-router": "4.0.14",
"ruffle-mirror": "2021.4.11",
"v-click-outside": "2.1.5",
"vue": "2.6.11",
"vue-i18n": "7.8.1",
"vue-router": "3.0.2",
"vue-template-compiler": "2.6.11",
"vuex": "4.0.2"
"vuelidate": "0.7.7",
"vuex": "3.0.1"
},
"devDependencies": {
"@babel/core": "7.17.8",
"@babel/core": "7.17.7",
"@babel/plugin-transform-runtime": "7.17.0",
"@babel/preset-env": "7.16.11",
"@babel/register": "7.17.7",
"@intlify/vue-i18n-loader": "^5.0.0",
"@ungap/event-target": "0.2.3",
"@vue/babel-helper-vue-jsx-merge-props": "1.2.1",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/compiler-sfc": "^3.1.0",
"@vue/test-utils": "2.0.0-rc.17",
"@vue/babel-preset-jsx": "1.2.4",
"@vue/test-utils": "1.0.0-beta.28",
"autoprefixer": "6.7.7",
"babel-eslint": "7.2.3",
"babel-loader": "8.2.4",
"babel-loader": "8.2.3",
"babel-plugin-lodash": "3.3.4",
"chai": "3.5.0",
"chalk": "1.1.3",
@ -69,26 +66,26 @@
"eslint": "5.16.0",
"eslint-config-standard": "12.0.0",
"eslint-friendly-formatter": "2.0.7",
"eslint-loader": "2.2.1",
"eslint-plugin-import": "2.25.4",
"eslint-loader": "2.1.2",
"eslint-plugin-import": "2.17.2",
"eslint-plugin-node": "7.0.1",
"eslint-plugin-promise": "4.3.1",
"eslint-plugin-standard": "4.1.0",
"eslint-plugin-promise": "4.1.1",
"eslint-plugin-standard": "4.0.0",
"eslint-plugin-vue": "5.2.3",
"eventsource-polyfill": "0.9.6",
"express": "4.17.3",
"express": "4.16.4",
"file-loader": "3.0.1",
"function-bind": "1.1.1",
"html-webpack-plugin": "3.2.0",
"http-proxy-middleware": "0.21.0",
"http-proxy-middleware": "0.17.4",
"inject-loader": "2.0.1",
"iso-639-1": "2.1.13",
"iso-639-1": "2.0.3",
"isparta-loader": "2.0.0",
"json-loader": "0.5.7",
"karma": "6.3.17",
"karma": "3.1.4",
"karma-coverage": "1.1.2",
"karma-firefox-launcher": "1.3.0",
"karma-mocha": "2.0.1",
"karma-firefox-launcher": "1.1.0",
"karma-mocha": "1.3.0",
"karma-mocha-reporter": "2.2.5",
"karma-sinon-chai": "2.0.2",
"karma-sourcemap-loader": "0.3.8",
@ -96,11 +93,11 @@
"karma-webpack": "4.0.2",
"lodash": "4.17.21",
"lolex": "1.6.0",
"mini-css-extract-plugin": "0.12.0",
"mini-css-extract-plugin": "0.5.0",
"mocha": "3.5.3",
"nightwatch": "0.9.21",
"opn": "4.0.2",
"ora": "0.4.1",
"ora": "0.3.0",
"postcss-loader": "3.0.0",
"raw-loader": "0.5.1",
"sass": "1.20.1",
@ -115,7 +112,7 @@
"stylelint-config-standard": "20.0.0",
"stylelint-rscss": "0.4.0",
"url-loader": "1.1.2",
"vue-loader": "^16.0.0",
"vue-loader": "14.2.4",
"vue-style-loader": "4.1.2",
"webpack": "4.46.0",
"webpack-dev-middleware": "3.7.3",

View file

@ -46,7 +46,7 @@ export default {
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
window.addEventListener('resize', this.updateMobileState)
},
unmounted () {
destroyed () {
window.removeEventListener('resize', this.updateMobileState)
},
computed: {
@ -65,7 +65,7 @@ export default {
}
}
},
shout () { return this.$store.state.shout.joined },
shout () { return this.$store.state.shout.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&

View file

@ -572,7 +572,7 @@ nav {
.fade-enter-active, .fade-leave-active {
transition: opacity .2s
}
.fade-enter-from, .fade-leave-active {
.fade-enter, .fade-leave-active {
opacity: 0
}

View file

@ -1,6 +1,6 @@
<template>
<div
id="app-loaded"
id="app"
:style="bgStyle"
>
<div
@ -59,7 +59,7 @@
<UserReportingModal />
<PostStatusModal />
<SettingsModal />
<div id="modal" />
<portal-target name="modal" />
<GlobalNoticeList />
</div>
</template>

View file

@ -1,13 +1,7 @@
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import vClickOutside from 'click-outside-vue3'
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
import App from '../App.vue'
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
import VBodyScrollLock from 'src/directives/body_scroll_lock'
import App from '../App.vue'
import { windowWidth } from '../services/window_utils/window_utils'
import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
@ -373,32 +367,25 @@ const afterStoreSetup = async ({ store, i18n }) => {
getTOS({ store })
getStickers({ store })
const router = createRouter({
history: createWebHistory(),
const router = new VueRouter({
mode: 'history',
routes: routes(store),
scrollBehavior: (to, _from, savedPosition) => {
if (to.matched.some(m => m.meta.dontScroll)) {
return false
}
return savedPosition || { left: 0, top: 0 }
return savedPosition || { x: 0, y: 0 }
}
})
const app = createApp(App)
app.use(router)
app.use(store)
app.use(i18n)
app.use(vClickOutside)
app.use(VBodyScrollLock)
app.component('FAIcon', FontAwesomeIcon)
app.component('FALayers', FontAwesomeLayers)
app.mount('#app')
return app
/* eslint-disable no-new */
return new Vue({
router,
store,
i18n,
el: '#app',
render: h => h(App)
})
}
export default afterStoreSetup

View file

@ -46,7 +46,7 @@ export default (store) => {
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
{ name: 'remote-user-profile-acct',
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
path: '/remote-users/(@?):username([^/@]+)@:hostname([^/@]+)',
component: RemoteUserResolver,
beforeEnter: validateAuthenticatedRoute
},
@ -69,7 +69,7 @@ export default (store) => {
{ name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) },
{ name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },
{ name: 'about', path: '/about', component: About },
{ name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile }
{ name: 'user-profile', path: '/(users/)?:name', component: UserProfile }
]
if (store.state.instance.pleromaChatMessagesAvailable) {

View file

@ -19,7 +19,6 @@
<script>
export default {
emits: ['resetAsyncComponent'],
methods: {
retry () {
this.$emit('resetAsyncComponent')

View file

@ -1,4 +1,3 @@
import { h, resolveComponent } from 'vue'
import LoginForm from '../login_form/login_form.vue'
import MFARecoveryForm from '../mfa_form/recovery_form.vue'
import MFATOTPForm from '../mfa_form/totp_form.vue'
@ -6,8 +5,8 @@ import { mapGetters } from 'vuex'
const AuthForm = {
name: 'AuthForm',
render () {
return h(resolveComponent(this.authForm))
render (createElement) {
return createElement('component', { is: this.authForm })
},
computed: {
authForm () {

View file

@ -4,7 +4,7 @@
<UserAvatar
class="avatar"
:user="user"
@click.prevent="toggleUserExpanded"
@click.prevent.native="toggleUserExpanded"
/>
</router-link>
<div

View file

@ -9,7 +9,7 @@ const Bookmarks = {
components: {
Timeline
},
unmounted () {
destroyed () {
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
}
}

View file

@ -57,7 +57,7 @@ const Chat = {
})
this.setChatLayout()
},
unmounted () {
destroyed () {
window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleLayoutChange)
this.unsetChatLayout()

View file

@ -26,71 +26,73 @@
/>
</div>
</div>
<div
ref="scrollable"
class="scrollable-message-list"
:style="{ height: scrollableContainerHeight }"
@scroll="handleScroll"
>
<template v-if="!errorLoadingChat">
<ChatMessage
v-for="chatViewItem in chatViewItems"
:key="chatViewItem.id"
:author="recipient"
:chat-view-item="chatViewItem"
:hovered-message-chain="chatViewItem.messageChainId === hoveredMessageChainId"
@hover="onMessageHover"
/>
</template>
<template>
<div
v-else
class="chat-loading-error"
ref="scrollable"
class="scrollable-message-list"
:style="{ height: scrollableContainerHeight }"
@scroll="handleScroll"
>
<div class="alert error">
{{ $t('chats.error_loading_chat') }}
<template v-if="!errorLoadingChat">
<ChatMessage
v-for="chatViewItem in chatViewItems"
:key="chatViewItem.id"
:author="recipient"
:chat-view-item="chatViewItem"
:hovered-message-chain="chatViewItem.messageChainId === hoveredMessageChainId"
@hover="onMessageHover"
/>
</template>
<div
v-else
class="chat-loading-error"
>
<div class="alert error">
{{ $t('chats.error_loading_chat') }}
</div>
</div>
</div>
</div>
<div
ref="footer"
class="panel-body footer"
>
<div
class="jump-to-bottom-button"
:class="{ 'visible': jumpToBottomButtonVisible }"
@click="scrollDown({ behavior: 'smooth' })"
ref="footer"
class="panel-body footer"
>
<span>
<FAIcon icon="chevron-down" />
<div
v-if="newMessageCount"
class="badge badge-notification unread-chat-count unread-message-count"
>
{{ newMessageCount }}
</div>
</span>
<div
class="jump-to-bottom-button"
:class="{ 'visible': jumpToBottomButtonVisible }"
@click="scrollDown({ behavior: 'smooth' })"
>
<span>
<FAIcon icon="chevron-down" />
<div
v-if="newMessageCount"
class="badge badge-notification unread-chat-count unread-message-count"
>
{{ newMessageCount }}
</div>
</span>
</div>
<PostStatusForm
:disable-subject="true"
:disable-scope-selector="true"
:disable-notice="true"
:disable-lock-warning="true"
:disable-polls="true"
:disable-sensitivity-checkbox="true"
:disable-submit="errorLoadingChat || !currentChat"
:disable-preview="true"
:optimistic-posting="true"
:post-handler="sendMessage"
:submit-on-enter="!mobileLayout"
:preserve-focus="!mobileLayout"
:auto-focus="!mobileLayout"
:placeholder="formPlaceholder"
:file-limit="1"
max-height="160"
emoji-picker-placement="top"
@resize="handleResize"
/>
</div>
<PostStatusForm
:disable-subject="true"
:disable-scope-selector="true"
:disable-notice="true"
:disable-lock-warning="true"
:disable-polls="true"
:disable-sensitivity-checkbox="true"
:disable-submit="errorLoadingChat || !currentChat"
:disable-preview="true"
:optimistic-posting="true"
:post-handler="sendMessage"
:submit-on-enter="!mobileLayout"
:preserve-focus="!mobileLayout"
:auto-focus="!mobileLayout"
:placeholder="formPlaceholder"
:file-limit="1"
max-height="160"
emoji-picker-placement="top"
@resize="handleResize"
/>
</div>
</template>
</div>
</div>
</div>

View file

@ -27,7 +27,6 @@ const ChatMessage = {
'chatViewItem',
'hoveredMessageChain'
],
emits: ['hover'],
components: {
Popover,
Attachment,

View file

@ -1,12 +1,11 @@
import Vue from 'vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import UserAvatar from '../user_avatar/user_avatar.vue'
import RichContent from 'src/components/rich_content/rich_content.jsx'
export default {
export default Vue.component('chat-title', {
name: 'ChatTitle',
components: {
UserAvatar,
RichContent
UserAvatar
},
props: [
'user', 'withAvatar'
@ -24,4 +23,4 @@ export default {
return generateProfileLink(user.id, user.screen_name)
}
}
}
})

View file

@ -14,11 +14,10 @@
/>
</router-link>
<RichContent
v-if="user"
class="username"
:title="'@'+user.screen_name_ui"
:html="htmlTitle"
:emoji="user.emoji || []"
:emoji="user.emoji"
/>
</div>
</template>

View file

@ -6,9 +6,9 @@
<input
type="checkbox"
:disabled="disabled"
:checked="modelValue"
:indeterminate="indeterminate"
@change="$emit('update:modelValue', $event.target.checked)"
:checked="checked"
:indeterminate.prop="indeterminate"
@change="$emit('change', $event.target.checked)"
>
<i class="checkbox-indicator" />
<span
@ -22,9 +22,12 @@
<script>
export default {
emits: ['update:modelValue'],
model: {
prop: 'checked',
event: 'change'
},
props: [
'modelValue',
'checked',
'indeterminate',
'disabled'
]

View file

@ -11,28 +11,28 @@
</label>
<Checkbox
v-if="typeof fallback !== 'undefined' && showOptionalTickbox"
:model-value="present"
:checked="present"
:disabled="disabled"
class="opt"
@update:modelValue="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)"
@change="$emit('input', typeof value === 'undefined' ? fallback : undefined)"
/>
<div class="input color-input-field">
<input
:id="name + '-t'"
class="textColor unstyled"
type="text"
:value="modelValue || fallback"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('update:modelValue', $event.target.value)"
@input="$emit('input', $event.target.value)"
>
<input
v-if="validColor"
:id="name"
class="nativeColor unstyled"
type="color"
:value="modelValue || fallback"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('update:modelValue', $event.target.value)"
@input="$emit('input', $event.target.value)"
>
<div
v-if="transparentColor"
@ -67,7 +67,7 @@ export default {
},
// Color value, should be required but vue cannot tell the difference
// between "property missing" and "property set to undefined"
modelValue: {
value: {
required: false,
type: String,
default: undefined
@ -91,19 +91,18 @@ export default {
default: true
}
},
emits: ['update:modelValue'],
computed: {
present () {
return typeof this.modelValue !== 'undefined'
return typeof this.value !== 'undefined'
},
validColor () {
return hex2rgb(this.modelValue || this.fallback)
return hex2rgb(this.value || this.fallback)
},
transparentColor () {
return this.modelValue === 'transparent'
return this.value === 'transparent'
},
computedColor () {
return this.modelValue && this.modelValue.startsWith('--')
return this.value && this.value.startsWith('--')
}
}
}

View file

@ -27,24 +27,20 @@
v-if="shouldShowAllConversationButton"
class="conversation-dive-to-top-level-box"
>
<i18n-t
keypath="status.show_all_conversation_with_icon"
<i18n
path="status.show_all_conversation_with_icon"
tag="button"
class="button-unstyled -link"
@click.prevent="diveToTopLevel"
scope="global"
>
<template #icon>
<FAIcon
icon="angle-double-left"
/>
</template>
<template #text>
<span>
{{ $tc('status.show_all_conversation', otherTopLevelCount, { numStatus: otherTopLevelCount }) }}
</span>
</template>
</i18n-t>
<FAIcon
place="icon"
icon="angle-double-left"
/>
<span place="text">
{{ $tc('status.show_all_conversation', otherTopLevelCount, { numStatus: otherTopLevelCount }) }}
</span>
</i18n>
</div>
<div
v-if="shouldShowAncestors"
@ -100,24 +96,20 @@
<div
class="thread-ancestor-dive-box-inner"
>
<i18n-t
<i18n
tag="button"
scope="global"
keypath="status.ancestor_follow_with_icon"
path="status.ancestor_follow_with_icon"
class="button-unstyled -link thread-tree-show-replies-button"
@click.prevent="diveIntoStatus(status.id)"
>
<template #icon>
<FAIcon
icon="angle-double-right"
/>
</template>
<template #text>
<span>
{{ $tc('status.ancestor_follow', getReplies(status.id).length - 1, { numReplies: getReplies(status.id).length - 1 }) }}
</span>
</template>
</i18n-t>
<FAIcon
place="icon"
icon="angle-double-right"
/>
<span place="text">
{{ $tc('status.ancestor_follow', getReplies(status.id).length - 1, { numReplies: getReplies(status.id).length - 1 }) }}
</span>
</i18n>
</div>
</div>
</div>

View file

@ -34,7 +34,7 @@
<search-bar
v-if="currentUser || !privateMode"
@toggled="onSearchBarToggled"
@click.stop
@click.stop.native
/>
<button
class="button-unstyled nav-icon"

View file

@ -31,7 +31,6 @@ library.add(
*/
const EmojiInput = {
emits: ['update:modelValue', 'shown'],
props: {
suggest: {
/**
@ -58,7 +57,8 @@ const EmojiInput = {
required: true,
type: Function
},
modelValue: {
// TODO VUE3: change to modelValue, change 'input' event to 'input'
value: {
/**
* Used for v-model
*/
@ -137,8 +137,8 @@ const EmojiInput = {
return (this.wordAtCaret || {}).word || ''
},
wordAtCaret () {
if (this.modelValue && this.caret) {
const word = Completion.wordAtPosition(this.modelValue, this.caret - 1) || {}
if (this.value && this.caret) {
const word = Completion.wordAtPosition(this.value, this.caret - 1) || {}
return word
}
}
@ -189,11 +189,8 @@ const EmojiInput = {
img: imageUrl || ''
}))
},
suggestions: {
handler (newValue) {
this.$nextTick(this.resize)
},
deep: true
suggestions (newValue) {
this.$nextTick(this.resize)
}
},
methods: {
@ -228,13 +225,13 @@ const EmojiInput = {
}
},
replace (replacement) {
const newValue = Completion.replaceWord(this.modelValue, this.wordAtCaret, replacement)
this.$emit('update:modelValue', newValue)
const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement)
this.$emit('input', newValue)
this.caret = 0
},
insert ({ insertion, keepOpen, surroundingSpace = true }) {
const before = this.modelValue.substring(0, this.caret) || ''
const after = this.modelValue.substring(this.caret) || ''
const before = this.value.substring(0, this.caret) || ''
const after = this.value.substring(this.caret) || ''
/* Using a bit more smart approach to padding emojis with spaces:
* - put a space before cursor if there isn't one already, unless we
@ -262,7 +259,7 @@ const EmojiInput = {
after
].join('')
this.keepOpen = keepOpen
this.$emit('update:modelValue', newValue)
this.$emit('input', newValue)
const position = this.caret + (insertion + spaceAfter + spaceBefore).length
if (!keepOpen) {
this.input.focus()
@ -281,8 +278,8 @@ const EmojiInput = {
if (len > 0 || suggestion) {
const chosenSuggestion = suggestion || this.suggestions[this.highlighted]
const replacement = chosenSuggestion.replacement
const newValue = Completion.replaceWord(this.modelValue, this.wordAtCaret, replacement)
this.$emit('update:modelValue', newValue)
const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement)
this.$emit('input', newValue)
this.highlighted = 0
const position = this.wordAtCaret.start + replacement.length
@ -458,7 +455,7 @@ const EmojiInput = {
this.showPicker = false
this.setCaret(e)
this.resize()
this.$emit('update:modelValue', e.target.value)
this.$emit('input', e.target.value)
},
onClickInput (e) {
this.showPicker = false

View file

@ -1,4 +1,3 @@
import { defineAsyncComponent } from 'vue'
import Checkbox from '../checkbox/checkbox.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -58,7 +57,7 @@ const EmojiPicker = {
}
},
components: {
StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')),
StickerPicker: () => import('../sticker_picker/sticker_picker.vue'),
Checkbox
},
methods: {
@ -80,7 +79,7 @@ const EmojiPicker = {
},
highlight (key) {
const ref = this.$refs['group-' + key]
const top = ref.offsetTop
const top = ref[0].offsetTop
this.setShowStickers(false)
this.activeGroup = key
this.$nextTick(() => {
@ -97,7 +96,7 @@ const EmojiPicker = {
}
},
triggerLoadMore (target) {
const ref = this.$refs['group-end-custom']
const ref = this.$refs['group-end-custom'][0]
if (!ref) return
const bottom = ref.offsetTop + ref.offsetHeight
@ -120,7 +119,7 @@ const EmojiPicker = {
this.$nextTick(() => {
this.emojisView.forEach(group => {
const ref = this.$refs['group-' + group.id]
if (ref.offsetTop <= top) {
if (ref[0].offsetTop <= top) {
this.activeGroup = group.id
}
})

View file

@ -15,8 +15,18 @@ const Exporter = {
type: String,
default: 'export.csv'
},
exportButtonLabel: { type: String },
processingMessage: { type: String }
exportButtonLabel: {
type: String,
default () {
return this.$t('exporter.export')
}
},
processingMessage: {
type: String,
default () {
return this.$t('exporter.processing')
}
}
},
data () {
return {

View file

@ -7,14 +7,14 @@
spin
/>
<span>{{ processingMessage || $t('exporter.processing') }}</span>
<span>{{ processingMessage }}</span>
</div>
<button
v-else
class="btn button-default"
@click="process"
>
{{ exportButtonLabel || $t('exporter.export') }}
{{ exportButtonLabel }}
</button>
</div>
</template>

View file

@ -1,4 +1,4 @@
import { set } from 'lodash'
import { set } from 'vue'
import Select from '../select/select.vue'
export default {
@ -6,12 +6,11 @@ export default {
Select
},
props: [
'name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit'
'name', 'label', 'value', 'fallback', 'options', 'no-inherit'
],
emits: ['update:modelValue'],
data () {
return {
lValue: this.modelValue,
lValue: this.value,
availableOptions: [
this.noInherit ? '' : 'inherit',
'custom',
@ -23,7 +22,7 @@ export default {
}
},
beforeUpdate () {
this.lValue = this.modelValue
this.lValue = this.value
},
computed: {
present () {
@ -38,7 +37,7 @@ export default {
},
set (v) {
set(this.lValue, 'family', v)
this.$emit('update:modelValue', this.lValue)
this.$emit('input', this.lValue)
}
},
isCustom () {

View file

@ -15,14 +15,13 @@
class="opt exlcude-disabled"
type="checkbox"
:checked="present"
@change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)"
@input="$emit('input', typeof value === 'undefined' ? fallback : undefined)"
>
<label
v-if="typeof fallback !== 'undefined'"
class="opt-l"
:for="name + '-o'"
/>
{{ ' ' }}
<Select
:id="name + '-font-switcher'"
v-model="preset"

View file

@ -1,5 +1,5 @@
import Attachment from '../attachment/attachment.vue'
import { sumBy, set } from 'lodash'
import { sumBy } from 'lodash'
const Gallery = {
props: [
@ -85,7 +85,7 @@ const Gallery = {
},
methods: {
onNaturalSizeLoad ({ id, width, height }) {
set(this.sizes, id, { width, height })
this.$set(this.sizes, id, { width, height })
},
rowStyle (row) {
if (row.audio) {

View file

@ -22,6 +22,7 @@
class="gallery-item"
:nsfw="nsfw"
:attachment="attachment"
:allow-play="false"
:size="size"
:editable="editable"
:remove="removeAttachment"

View file

@ -117,7 +117,7 @@ const ImageCropper = {
const fileInput = this.$refs.input
fileInput.addEventListener('change', this.readFile)
},
beforeUnmount: function () {
beforeDestroy: function () {
// remove the event listeners
const trigger = this.getTriggerDOM()
if (trigger) {

View file

@ -15,9 +15,24 @@ const Importer = {
type: Function,
required: true
},
submitButtonLabel: { type: String },
successMessage: { type: String },
errorMessage: { type: String }
submitButtonLabel: {
type: String,
default () {
return this.$t('importer.submit')
}
},
successMessage: {
type: String,
default () {
return this.$t('importer.success')
}
},
errorMessage: {
type: String,
default () {
return this.$t('importer.error')
}
}
},
data () {
return {

View file

@ -18,31 +18,21 @@
class="btn button-default"
@click="submit"
>
{{ submitButtonLabel || $t('importer.submit') }}
{{ submitButtonLabel }}
</button>
<div v-if="success">
<button
class="button-unstyled"
<FAIcon
icon="times"
@click="dismiss"
>
<FAIcon
icon="times"
/>
</button>
{{ ' ' }}
<span>{{ successMessage || $t('importer.success') }}</span>
/>
<p>{{ successMessage }}</p>
</div>
<div v-else-if="error">
<button
class="button-unstyled"
<FAIcon
icon="times"
@click="dismiss"
>
<FAIcon
icon="times"
/>
</button>
{{ ' ' }}
<span>{{ errorMessage || $t('importer.error') }}</span>
/>
<p>{{ errorMessage }}</p>
</div>
</div>
</template>

View file

@ -1,10 +1,11 @@
import Notifications from '../notifications/notifications.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
const tabModeDict = {
mentions: ['mention'],
'likes+repeats': ['repeat', 'like'],
follows: ['follow'],
reactions: ['pleroma:emoji_reaction'],
reports: ['pleroma:report'],
moves: ['move']
}
@ -12,7 +13,8 @@ const Interactions = {
data () {
return {
allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
filterMode: tabModeDict['mentions']
filterMode: tabModeDict['mentions'],
canSeeReports: ['moderator', 'admin'].includes(this.$store.state.users.currentUser.role)
}
},
methods: {
@ -21,8 +23,7 @@ const Interactions = {
}
},
components: {
Notifications,
TabSwitcher
Notifications
}
}

View file

@ -21,6 +21,15 @@
key="follows"
:label="$t('interactions.follows')"
/>
<span
key="reactions"
:label="$t('interactions.emoji_reactions')"
/>
<span
v-if="canSeeReports"
key="reports"
:label="$t('interactions.reports')"
/>
<span
v-if="!allowFollowingMove"
key="moves"

View file

@ -3,7 +3,6 @@
<label for="interface-language-switcher">
{{ $t('settings.interfaceLanguage') }}
</label>
{{ ' ' }}
<Select
id="interface-language-switcher"
v-model="language"

View file

@ -76,15 +76,11 @@
>
<div class="alert error">
{{ error }}
<button
class="button-unstyled"
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearError"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
/>
</button>
/>
</div>
</div>
</div>

View file

@ -142,7 +142,7 @@ const MediaModal = {
document.addEventListener('keyup', this.handleKeyupEvent)
document.addEventListener('keydown', this.handleKeydownEvent)
},
unmounted () {
destroyed () {
window.removeEventListener('popstate', this.hide)
document.removeEventListener('keyup', this.handleKeyupEvent)
document.removeEventListener('keydown', this.handleKeydownEvent)

View file

@ -41,12 +41,10 @@
class="serverName"
:class="{ '-faded': shouldFadeDomain }"
v-html="'@' + serverName"
/>
</span>
<span
/></span><span
v-if="isYou && shouldShowYous"
:class="{ '-you': shouldBoldenYou }"
> {{ ' ' + $t('status.you') }}</span>
> {{ $t('status.you') }}</span>
<!-- eslint-enable vue/no-v-html -->
</a><span
v-if="shouldShowTooltip"

View file

@ -6,6 +6,7 @@
class="mention-link"
:content="mention.content"
:url="mention.url"
:first-mention="false"
/><span
v-if="manyMentions"
class="extraMentions"
@ -20,6 +21,7 @@
class="mention-link"
:content="mention.content"
:url="mention.url"
:first-mention="false"
/>
</span><button
v-if="!expanded"

View file

@ -56,15 +56,11 @@
>
<div class="alert error">
{{ error }}
<button
class="button-unstyled"
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearError"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
/>
</button>
/>
</div>
</div>
</div>

View file

@ -58,16 +58,12 @@
>
<div class="alert error">
{{ error }}
<button
class="button-unstyled"
<FAIcon
size="lg"
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearError"
>
<FAIcon
size="lg"
class="fa-scale-110 fa-old-padding"
icon="times"
/>
</button>
/>
</div>
</div>
</div>

View file

@ -99,9 +99,6 @@
width: 100%;
position: fixed;
box-sizing: border-box;
a {
color: var(--topBarLink, $fallback--link);
}
}
.mobile-inner-nav {

View file

@ -29,7 +29,7 @@ const MobilePostStatusButton = {
}
window.addEventListener('resize', this.handleOSK)
},
unmounted () {
destroyed () {
if (this.autohideFloatingPostButton) {
this.deactivateFloatingPostButtonAutohide()
}

View file

@ -1,12 +1,13 @@
<template>
<button
v-if="isLoggedIn"
class="MobilePostButton button-default new-status-button"
:class="{ 'hidden': isHidden, 'always-show': isPersistent }"
@click="openPostForm"
>
<FAIcon icon="pen" />
</button>
<div v-if="isLoggedIn">
<button
class="button-default new-status-button"
:class="{ 'hidden': isHidden, 'always-show': isPersistent }"
@click="openPostForm"
>
<FAIcon icon="pen" />
</button>
</div>
</template>
<script src="./mobile_post_status_button.js"></script>
@ -14,27 +15,25 @@
<style lang="scss">
@import '../../_variables.scss';
.MobilePostButton {
&.button-default {
width: 5em;
height: 5em;
border-radius: 100%;
position: fixed;
bottom: 1.5em;
right: 1.5em;
// TODO: this needs its own color, it has to stand out enough and link color
// is not very optimal for this particular use.
background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg);
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3);
z-index: 10;
.new-status-button {
width: 5em;
height: 5em;
border-radius: 100%;
position: fixed;
bottom: 1.5em;
right: 1.5em;
// TODO: this needs its own color, it has to stand out enough and link color
// is not very optimal for this particular use.
background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg);
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3);
z-index: 10;
transition: 0.35s transform;
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
}
transition: 0.35s transform;
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
&.hidden {
transform: translateY(150%);

View file

@ -132,7 +132,7 @@
</button>
</template>
</Popover>
<teleport to="#modal">
<portal to="modal">
<DialogModal
v-if="showDeleteUserDialog"
:on-cancel="deleteUserDialog.bind(this, false)"
@ -156,7 +156,7 @@
</button>
</template>
</DialogModal>
</teleport>
</portal>
</div>
</template>

View file

@ -4,6 +4,7 @@ import Status from '../status/status.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
import UserCard from '../user_card/user_card.vue'
import Timeago from '../timeago/timeago.vue'
import Report from '../report/report.vue'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
@ -46,6 +47,7 @@ const Notification = {
UserCard,
Timeago,
Status,
Report,
RichContent
},
methods: {

View file

@ -33,7 +33,7 @@
>
<a
class="avatar-container"
:href="$router.resolve(userProfileLink).href"
:href="notification.from_profile.statusnet_profile_url"
@click.stop.prevent.capture="toggleUserExpanded"
>
<UserAvatar
@ -65,16 +65,12 @@
v-else
class="username"
:title="'@'+notification.from_profile.screen_name_ui"
>
{{ notification.from_profile.name }}
</span>
{{ ' ' }}
>{{ notification.from_profile.name }}</span>
<span v-if="notification.type === 'like'">
<FAIcon
class="type-icon"
icon="star"
/>
{{ ' ' }}
<small>{{ $t('notifications.favorited_you') }}</small>
</span>
<span v-if="notification.type === 'repeat'">
@ -83,7 +79,6 @@
icon="retweet"
:title="$t('tool_tip.repeat')"
/>
{{ ' ' }}
<small>{{ $t('notifications.repeated_you') }}</small>
</span>
<span v-if="notification.type === 'follow'">
@ -91,7 +86,6 @@
class="type-icon"
icon="user-plus"
/>
{{ ' ' }}
<small>{{ $t('notifications.followed_you') }}</small>
</span>
<span v-if="notification.type === 'follow_request'">
@ -99,7 +93,6 @@
class="type-icon"
icon="user"
/>
{{ ' ' }}
<small>{{ $t('notifications.follow_request') }}</small>
</span>
<span v-if="notification.type === 'move'">
@ -107,19 +100,18 @@
class="type-icon"
icon="suitcase-rolling"
/>
{{ ' ' }}
<small>{{ $t('notifications.migrated_to') }}</small>
</span>
<span v-if="notification.type === 'pleroma:emoji_reaction'">
<small>
<i18n-t
scope="global"
keypath="notifications.reacted_with"
>
<i18n path="notifications.reacted_with">
<span class="emoji-reaction-emoji">{{ notification.emoji }}</span>
</i18n-t>
</i18n>
</small>
</span>
<span v-if="notification.type === 'pleroma:report'">
<small>{{ $t('notifications.submitted_report') }}</small>
</span>
</div>
<div
v-if="isStatusNotification"
@ -172,26 +164,18 @@
v-if="notification.type === 'follow_request'"
style="white-space: nowrap;"
>
<button
class="button-unstyled"
<FAIcon
icon="check"
class="fa-scale-110 fa-old-padding follow-request-accept"
:title="$t('tool_tip.accept_follow_request')"
@click="approveUser()"
>
<FAIcon
icon="check"
class="fa-scale-110 fa-old-padding follow-request-accept"
/>
</button>
<button
class="button-unstyled"
/>
<FAIcon
icon="times"
class="fa-scale-110 fa-old-padding follow-request-reject"
:title="$t('tool_tip.reject_follow_request')"
@click="denyUser()"
>
<FAIcon
icon="times"
class="fa-scale-110 fa-old-padding follow-request-reject"
/>
</button>
/>
</div>
</div>
<div
@ -202,6 +186,10 @@
@{{ notification.target.screen_name_ui }}
</router-link>
</div>
<Report
v-else-if="notification.type === 'pleroma:report'"
:report-id="notification.report.id"
/>
<template v-else>
<StatusContent
class="faint"

View file

@ -59,11 +59,15 @@
height: 32px;
}
--link: var(--faintLink);
--text: var(--faint);
.faint {
--link: var(--faintLink);
--text: var(--faint);
}
}
.follow-request-accept {
cursor: pointer;
&:hover {
color: $fallback--text;
color: var(--text, $fallback--text);
@ -71,6 +75,8 @@
}
.follow-request-reject {
cursor: pointer;
&:hover {
color: $fallback--cRed;
color: var(--cRed, $fallback--cRed);

View file

@ -11,21 +11,21 @@
</label>
<Checkbox
v-if="typeof fallback !== 'undefined'"
:model-value="present"
:checked="present"
:disabled="disabled"
class="opt"
@update:modelValue="$emit('update:modelValue', !present ? fallback : undefined)"
@change="$emit('input', !present ? fallback : undefined)"
/>
<input
:id="name"
class="input-number"
type="number"
:value="modelValue || fallback"
:value="value || fallback"
:disabled="!present || disabled"
max="1"
min="0"
step=".05"
@input="$emit('update:modelValue', $event.target.value)"
@input="$emit('input', $event.target.value)"
>
</div>
</template>
@ -37,12 +37,11 @@ export default {
Checkbox
},
props: [
'name', 'modelValue', 'fallback', 'disabled'
'name', 'value', 'fallback', 'disabled'
],
emits: ['update:modelValue'],
computed: {
present () {
return typeof this.modelValue !== 'undefined'
return typeof this.value !== 'undefined'
}
}
}

View file

@ -21,7 +21,7 @@ export default {
}
this.$store.dispatch('trackPoll', this.pollId)
},
unmounted () {
destroyed () {
this.$store.dispatch('untrackPoll', this.pollId)
},
computed: {

View file

@ -71,18 +71,13 @@
{{ $tc("polls.votes_count", poll.votes_count, { count: poll.votes_count }) }}&nbsp;·&nbsp;
</template>
</div>
<span>
<i18n-t
scope="global"
:keypath="expired ? 'polls.expired' : 'polls.expires_in'"
>
<Timeago
:time="expiresAt"
:auto-update="60"
:now-threshold="0"
/>
</i18n-t>
</span>
<i18n :path="expired ? 'polls.expired' : 'polls.expires_in'">
<Timeago
:time="expiresAt"
:auto-update="60"
:now-threshold="0"
/>
</i18n>
</div>
</div>
</template>

View file

@ -72,7 +72,6 @@
:max="maxExpirationInCurrentUnit"
@change="expiryAmountChange"
>
{{ ' ' }}
<Select
v-model="expiryUnit"
unstyled="true"

View file

@ -178,7 +178,7 @@ const Popover = {
created () {
document.addEventListener('click', this.onClickOutside)
},
unmounted () {
destroyed () {
document.removeEventListener('click', this.onClickOutside)
this.hidePopover()
}

View file

@ -149,30 +149,5 @@
}
}
.button-default.dropdown-item {
&,
i[class*=icon-] {
color: $fallback--text;
color: var(--btnText, $fallback--text);
}
&:active {
background-color: $fallback--lightBg;
background-color: var(--selectedMenuPopover, $fallback--lightBg);
color: $fallback--link;
color: var(--selectedMenuPopoverText, $fallback--link);
}
&:disabled {
color: $fallback--text;
color: var(--btnDisabledText, $fallback--text);
}
&.toggled {
color: $fallback--text;
color: var(--btnToggledText, $fallback--text);
}
}
}
</style>

View file

@ -78,12 +78,6 @@ const PostStatusForm = {
'emojiPickerPlacement',
'optimisticPosting'
],
emits: [
'posted',
'resize',
'mediaplay',
'mediapause'
],
components: {
MediaUpload,
EmojiInput,

View file

@ -18,12 +18,11 @@
<FAIcon :icon="uploadFileLimitReached ? 'ban' : 'upload'" />
</div>
<div class="form-group">
<i18n-t
<i18n
v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private' && !disableLockWarning"
keypath="post_status.account_not_locked_warning"
path="post_status.account_not_locked_warning"
tag="p"
class="visibility-notice"
scope="global"
>
<button
class="button-unstyled -link"
@ -31,7 +30,7 @@
>
{{ $t('post_status.account_not_locked_warning_link') }}
</button>
</i18n-t>
</i18n>
<p
v-if="!hideScopeNotice && newStatus.visibility === 'public'"
class="visibility-notice notice-dismissible"
@ -282,15 +281,11 @@
class="alert error"
>
Error: {{ error }}
<button
class="button-unstyled"
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearError"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
/>
</button>
/>
</div>
<gallery
v-if="newStatus.files && newStatus.files.length > 0"

View file

@ -9,7 +9,7 @@ const PublicAndExternalTimeline = {
created () {
this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' })
},
unmounted () {
destroyed () {
this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal')
}
}

View file

@ -9,7 +9,7 @@ const PublicTimeline = {
created () {
this.$store.dispatch('startFetchingTimeline', { timeline: 'public' })
},
unmounted () {
destroyed () {
this.$store.dispatch('stopFetchingTimeline', 'public')
}

View file

@ -15,7 +15,7 @@
class="opt"
type="checkbox"
:checked="present"
@change="$emit('update:modelValue', !present ? fallback : undefined)"
@input="$emit('input', !present ? fallback : undefined)"
>
<label
v-if="typeof fallback !== 'undefined'"
@ -26,23 +26,23 @@
:id="name"
class="input-number"
type="range"
:value="modelValue || fallback"
:value="value || fallback"
:disabled="!present || disabled"
:max="max || hardMax || 100"
:min="min || hardMin || 0"
:step="step || 1"
@input="$emit('update:modelValue', $event.target.value)"
@input="$emit('input', $event.target.value)"
>
<input
:id="name"
class="input-number"
type="number"
:value="modelValue || fallback"
:value="value || fallback"
:disabled="!present || disabled"
:max="hardMax"
:min="hardMin"
:step="step || 1"
@input="$emit('update:modelValue', $event.target.value)"
@input="$emit('input', $event.target.value)"
>
</div>
</template>
@ -50,12 +50,11 @@
<script>
export default {
props: [
'name', 'modelValue', 'fallback', 'disabled', 'label', 'max', 'min', 'step', 'hardMin', 'hardMax'
'name', 'value', 'fallback', 'disabled', 'label', 'max', 'min', 'step', 'hardMin', 'hardMax'
],
emits: ['update:modelValue'],
computed: {
present () {
return typeof this.modelValue !== 'undefined'
return typeof this.value !== 'undefined'
}
}
}

View file

@ -1,9 +1,9 @@
import useVuelidate from '@vuelidate/core'
import { required, requiredIf, sameAs } from '@vuelidate/validators'
import { validationMixin } from 'vuelidate'
import { required, requiredIf, sameAs } from 'vuelidate/lib/validators'
import { mapActions, mapState } from 'vuex'
const registration = {
setup () { return { v$: useVuelidate() } },
mixins: [validationMixin],
data: () => ({
user: {
email: '',
@ -24,7 +24,7 @@ const registration = {
password: { required },
confirm: {
required,
sameAs: sameAs(this.user.password)
sameAsPassword: sameAs('password')
},
reason: { required: requiredIf(() => this.accountApprovalRequired) }
}
@ -65,9 +65,9 @@ const registration = {
this.user.captcha_token = this.captcha.token
this.user.captcha_answer_data = this.captcha.answer_data
this.v$.$touch()
this.$v.$touch()
if (!this.v$.$invalid) {
if (!this.$v.$invalid) {
try {
await this.signUp(this.user)
this.$router.push({ name: 'friends' })

View file

@ -12,7 +12,7 @@
<div class="text-fields">
<div
class="form-group"
:class="{ 'form-group--error': v$.user.username.$error }"
:class="{ 'form-group--error': $v.user.username.$error }"
>
<label
class="form--label"
@ -20,18 +20,18 @@
>{{ $t('login.username') }}</label>
<input
id="sign-up-username"
v-model.trim="v$.user.username.$model"
v-model.trim="$v.user.username.$model"
:disabled="isPending"
class="form-control"
:placeholder="$t('registration.username_placeholder')"
>
</div>
<div
v-if="v$.user.username.$dirty"
v-if="$v.user.username.$dirty"
class="form-error"
>
<ul>
<li v-if="!v$.user.username.required">
<li v-if="!$v.user.username.required">
<span>{{ $t('registration.validations.username_required') }}</span>
</li>
</ul>
@ -39,7 +39,7 @@
<div
class="form-group"
:class="{ 'form-group--error': v$.user.fullname.$error }"
:class="{ 'form-group--error': $v.user.fullname.$error }"
>
<label
class="form--label"
@ -47,18 +47,18 @@
>{{ $t('registration.fullname') }}</label>
<input
id="sign-up-fullname"
v-model.trim="v$.user.fullname.$model"
v-model.trim="$v.user.fullname.$model"
:disabled="isPending"
class="form-control"
:placeholder="$t('registration.fullname_placeholder')"
>
</div>
<div
v-if="v$.user.fullname.$dirty"
v-if="$v.user.fullname.$dirty"
class="form-error"
>
<ul>
<li v-if="!v$.user.fullname.required">
<li v-if="!$v.user.fullname.required">
<span>{{ $t('registration.validations.fullname_required') }}</span>
</li>
</ul>
@ -66,7 +66,7 @@
<div
class="form-group"
:class="{ 'form-group--error': v$.user.email.$error }"
:class="{ 'form-group--error': $v.user.email.$error }"
>
<label
class="form--label"
@ -74,18 +74,18 @@
>{{ $t('registration.email') }}</label>
<input
id="email"
v-model="v$.user.email.$model"
v-model="$v.user.email.$model"
:disabled="isPending"
class="form-control"
type="email"
>
</div>
<div
v-if="v$.user.email.$dirty"
v-if="$v.user.email.$dirty"
class="form-error"
>
<ul>
<li v-if="!v$.user.email.required">
<li v-if="!$v.user.email.required">
<span>{{ $t('registration.validations.email_required') }}</span>
</li>
</ul>
@ -107,7 +107,7 @@
<div
class="form-group"
:class="{ 'form-group--error': v$.user.password.$error }"
:class="{ 'form-group--error': $v.user.password.$error }"
>
<label
class="form--label"
@ -122,11 +122,11 @@
>
</div>
<div
v-if="v$.user.password.$dirty"
v-if="$v.user.password.$dirty"
class="form-error"
>
<ul>
<li v-if="!v$.user.password.required">
<li v-if="!$v.user.password.required">
<span>{{ $t('registration.validations.password_required') }}</span>
</li>
</ul>
@ -134,7 +134,7 @@
<div
class="form-group"
:class="{ 'form-group--error': v$.user.confirm.$error }"
:class="{ 'form-group--error': $v.user.confirm.$error }"
>
<label
class="form--label"
@ -149,14 +149,14 @@
>
</div>
<div
v-if="v$.user.confirm.$dirty"
v-if="$v.user.confirm.$dirty"
class="form-error"
>
<ul>
<li v-if="!v$.user.confirm.required">
<li v-if="!$v.user.confirm.required">
<span>{{ $t('registration.validations.password_confirmation_required') }}</span>
</li>
<li v-if="!v$.user.confirm.sameAsPassword">
<li v-if="!$v.user.confirm.sameAsPassword">
<span>{{ $t('registration.validations.password_confirmation_match') }}</span>
</li>
</ul>

View file

@ -0,0 +1,34 @@
import Select from '../select/select.vue'
import StatusContent from '../status_content/status_content.vue'
import Timeago from '../timeago/timeago.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
const Report = {
props: [
'reportId'
],
components: {
Select,
StatusContent,
Timeago
},
computed: {
report () {
return this.$store.state.reports.reports[this.reportId] || {}
},
state: {
get: function () { return this.report.state },
set: function (val) { this.setReportState(val) }
}
},
methods: {
generateUserProfileLink (user) {
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
},
setReportState (state) {
return this.$store.dispatch('setReportState', { id: this.report.id, state })
}
}
}
export default Report

View file

@ -0,0 +1,43 @@
@import '../../_variables.scss';
.Report {
.report-content {
margin: 0.5em 0 1em;
}
.report-state {
margin: 0.5em 0 1em;
}
.reported-status {
border: 1px solid $fallback--faint;
border-color: var(--faint, $fallback--faint);
border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius);
color: $fallback--text;
color: var(--text, $fallback--text);
display: block;
padding: 0.5em;
margin: 0.5em 0;
.status-content {
pointer-events: none;
}
.reported-status-heading {
display: flex;
width: 100%;
justify-content: space-between;
margin-bottom: 0.2em;
}
.reported-status-name {
font-weight: bold;
}
}
.note {
width: 100%;
margin-bottom: 0.5em;
}
}

View file

@ -0,0 +1,74 @@
<template>
<div class="Report">
<div class="reported-user">
<span>{{ $t('report.reported_user') }}</span>
<router-link :to="generateUserProfileLink(report.acct)">
@{{ report.acct.screen_name }}
</router-link>
</div>
<div class="reporter">
<span>{{ $t('report.reporter') }}</span>
<router-link :to="generateUserProfileLink(report.actor)">
@{{ report.actor.screen_name }}
</router-link>
</div>
<div class="report-state">
<span>{{ $t('report.state') }}</span>
<Select
:id="report-state"
v-model="state"
class="form-control"
>
<option
v-for="state in ['open', 'closed', 'resolved']"
:key="state"
:value="state"
>
{{ $t('report.state_' + state) }}
</option>
</Select>
</div>
<RichContent
class="report-content"
:html="report.content"
:emoji="[]"
/>
<div v-if="report.statuses.length">
<small>{{ $t('report.reported_statuses') }}</small>
<router-link
v-for="status in report.statuses"
:key="status.id"
:to="{ name: 'conversation', params: { id: status.id } }"
class="reported-status"
>
<div class="reported-status-heading">
<span class="reported-status-name">{{ status.user.name }}</span>
<Timeago
:time="status.created_at"
:auto-update="240"
class="faint"
/>
</div>
<status-content :status="status" />
</router-link>
</div>
<div v-if="report.notes.length">
<small>{{ $t('report.notes') }}</small>
<div
v-for="note in report.notes"
:key="note.id"
class="note"
>
<span>{{ note.content }}</span>
<Timeago
:time="note.created_at"
:auto-update="240"
class="faint"
/>
</div>
</div>
</div>
</template>
<script src="./report.js"></script>
<style src="./report.scss" lang="scss"></style>

View file

@ -1,3 +1,4 @@
import Vue from 'vue'
import { unescape, flattenDeep } from 'lodash'
import { getTagName, processTextForEmoji, getAttrs } from 'src/services/html_converter/utility.service.js'
import { convertHtmlToTree } from 'src/services/html_converter/html_tree_converter.service.js'
@ -26,12 +27,8 @@ import './rich_content.scss'
*
* Apart from that one small hiccup with emit in render this _should_ be vue3-ready
*/
export default {
export default Vue.component('RichContent', {
name: 'RichContent',
components: {
MentionsLine,
HashtagLink
},
props: {
// Original html content
html: {
@ -61,7 +58,7 @@ export default {
}
},
// NEVER EVER TOUCH DATA INSIDE RENDER
render () {
render (h) {
// Pre-process HTML
const { newHtml: html } = preProcessPerLine(this.html, this.greentext)
let currentMentions = null // Current chain of mentions, we group all mentions together
@ -79,19 +76,18 @@ export default {
const renderImage = (tag) => {
return <StillImage
{...getAttrs(tag)}
{...{ attrs: getAttrs(tag) }}
class="img"
/>
}
const renderHashtag = (attrs, children, encounteredTextReverse) => {
const { index, ...linkData } = getLinkData(attrs, children, tagsIndex++)
const linkData = getLinkData(attrs, children, tagsIndex++)
writtenTags.push(linkData)
if (!encounteredTextReverse) {
lastTags.push(linkData)
}
const { url, tag, content } = linkData
return <HashtagLink url={url} tag={tag} content={content}/>
return <HashtagLink {...{ props: linkData }}/>
}
const renderMention = (attrs, children) => {
@ -226,7 +222,7 @@ export default {
attrs.target = '_blank'
const newChildren = [...children].reverse().map(processItemReverse).reverse()
return <a {...attrs}>
return <a {...{ attrs }}>
{ newChildren }
</a>
}
@ -239,7 +235,7 @@ export default {
const newChildren = Array.isArray(children)
? [...children].reverse().map(processItemReverse).reverse()
: children
return <Tag {...getAttrs(opener)}>
return <Tag {...{ attrs: getAttrs(opener) }}>
{ newChildren }
</Tag>
} else {
@ -270,7 +266,7 @@ export default {
return result
}
}
})
const getLinkData = (attrs, children, index) => {
const stripTags = (item) => {

View file

@ -16,7 +16,6 @@
class="fa-scale-110 fa-old-padding"
/>
</button>
{{ ' ' }}
<button
v-if="showPrivate"
class="button-unstyled scope"
@ -30,7 +29,6 @@
class="fa-scale-110 fa-old-padding"
/>
</button>
{{ ' ' }}
<button
v-if="showUnlisted"
class="button-unstyled scope"
@ -44,7 +42,6 @@
class="fa-scale-110 fa-old-padding"
/>
</button>
{{ ' ' }}
<button
v-if="showPublic"
class="button-unstyled scope"

View file

@ -1,7 +1,6 @@
import FollowCard from '../follow_card/follow_card.vue'
import Conversation from '../conversation/conversation.vue'
import Status from '../status/status.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import map from 'lodash/map'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -18,8 +17,7 @@ const Search = {
components: {
FollowCard,
Conversation,
Status,
TabSwitcher
Status
},
props: [
'query'

View file

@ -8,9 +8,12 @@ library.add(
)
export default {
emits: ['update:modelValue'],
model: {
prop: 'value',
event: 'change'
},
props: [
'modelValue',
'value',
'disabled',
'unstyled',
'kind'

View file

@ -1,3 +1,4 @@
<template>
<label
class="Select input"
@ -5,12 +6,11 @@
>
<select
:disabled="disabled"
:value="modelValue"
@change="$emit('update:modelValue', $event.target.value)"
:value="value"
@change="$emit('change', $event.target.value)"
>
<slot />
</select>
{{ ' ' }}
<FAIcon
class="select-down-icon"
icon="chevron-down"
@ -23,8 +23,7 @@
<style lang="scss">
@import '../../_variables.scss';
/* TODO fix order of styles */
label.Select {
.Select {
padding: 0;
select {

View file

@ -6,9 +6,9 @@
>
<div class="selectable-list-checkbox-wrapper">
<Checkbox
:model-value="allSelected"
:checked="allSelected"
:indeterminate="someSelected"
@update:model-value="toggleAll"
@change="toggleAll"
>
{{ $t('selectable_list.select_all') }}
</Checkbox>
@ -31,8 +31,8 @@
>
<div class="selectable-list-checkbox-wrapper">
<Checkbox
:model-value="isSelected(item)"
@update:model-value="checked => toggle(checked, item)"
:checked="isSelected(item)"
@change="checked => toggle(checked, item)"
/>
</div>
<slot

View file

@ -4,9 +4,9 @@
class="BooleanSetting"
>
<Checkbox
:model-value="state"
:checked="state"
:disabled="disabled"
@update:modelValue="update"
@change="update"
>
<span
v-if="!!$slots.default"
@ -14,7 +14,6 @@
>
<slot />
</span>
{{ ' ' }}
<ModifiedIndicator :changed="isChanged" /><ServerSideIndicator :server-side="isServerSide" /> </Checkbox>
</label>
</template>

View file

@ -4,11 +4,10 @@
class="ChoiceSetting"
>
<slot />
{{ ' ' }}
<Select
:model-value="state"
:value="state"
:disabled="disabled"
@update:modelValue="update"
@change="update"
>
<option
v-for="option in options"

View file

@ -8,7 +8,7 @@ export default {
path: String,
disabled: Boolean,
min: Number,
expert: [Number, String]
expert: Number
},
computed: {
pathDefault () {

View file

@ -16,7 +16,6 @@
:value="state"
@change="update"
>
{{ ' ' }}
<ModifiedIndicator :changed="isChanged" />
</span>
</template>

View file

@ -56,8 +56,8 @@ const SettingsModal = {
SettingsModalContent: getResettableAsyncComponent(
() => import('./settings_modal_content.vue'),
{
loadingComponent: PanelLoading,
errorComponent: AsyncComponentError,
loading: PanelLoading,
error: AsyncComponentError,
delay: 0
}
)

View file

@ -54,10 +54,5 @@
>* {
margin-right: 0.5em;
}
.extra-content {
display: flex;
flex-grow: 1;
}
}
}

View file

@ -11,7 +11,7 @@
{{ $t('settings.settings') }}
</span>
<transition name="fade">
<div v-if="currentSaveStateNotice">
<template v-if="currentSaveStateNotice">
<div
v-if="currentSaveStateNotice.error"
class="alert error"
@ -27,7 +27,7 @@
>
{{ $t('settings.saving_ok') }}
</div>
</div>
</template>
</transition>
<button
class="btn button-default"
@ -68,7 +68,6 @@
:title="$t('general.close')"
>
<span>{{ $t("settings.file_export_import.backup_restore") }}</span>
{{ ' ' }}
<FAIcon
icon="chevron-down"
/>
@ -110,16 +109,9 @@
</template>
</Popover>
<Checkbox
:model-value="!!expertLevel"
@update:modelValue="expertLevel = Number($event)"
>
<Checkbox v-model="expertLevel">
{{ $t("settings.expert_mode") }}
</Checkbox>
<span
id="unscrolled-content"
class="extra-content"
/>
</div>
</div>
</Modal>

View file

@ -1,4 +1,4 @@
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
import DataImportExportTab from './tabs/data_import_export_tab.vue'
import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue'
@ -53,9 +53,6 @@ const SettingsModalContent = {
},
open () {
return this.$store.state.interface.settingsModalState !== 'hidden'
},
bodyLock () {
return this.$store.state.interface.settingsModalState === 'visible'
}
},
methods: {
@ -63,8 +60,8 @@ const SettingsModalContent = {
const targetTab = this.$store.state.interface.settingsModalTargetTab
// We're being told to open in specific tab
if (targetTab) {
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
return elm.props && elm.props['data-tab-name'] === targetTab
const tabIndex = this.$refs.tabSwitcher.$slots.default.findIndex(elm => {
return elm.data && elm.data.attrs['data-tab-name'] === targetTab
})
if (tabIndex >= 0) {
this.$refs.tabSwitcher.setTab(tabIndex)

View file

@ -4,7 +4,6 @@
class="settings_tab-switcher"
:side-tab-bar="true"
:scrollable-tabs="true"
:body-scroll-lock="bodyLock"
>
<div
:label="$t('settings.general')"

View file

@ -72,10 +72,22 @@
<div>{{ $t('settings.filtering_explanation') }}</div>
</li>
<h3>{{ $t('settings.attachments') }}</h3>
<li v-if="expertLevel > 0">
<label for="maxThumbnails">
{{ $t('settings.max_thumbnails') }}
</label>
<input
id="maxThumbnails"
path.number="maxThumbnails"
class="number-input"
type="number"
min="0"
step="1"
>
</li>
<li>
<IntegerSetting
path="maxThumbnails"
expert="1"
:min="0"
>
{{ $t('settings.max_thumbnails') }}

View file

@ -2,7 +2,7 @@ import get from 'lodash/get'
import map from 'lodash/map'
import reject from 'lodash/reject'
import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
import BlockCard from 'src/components/block_card/block_card.vue'
import MuteCard from 'src/components/mute_card/mute_card.vue'
import DomainMuteCard from 'src/components/domain_mute_card/domain_mute_card.vue'

View file

@ -54,20 +54,16 @@
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
background-color: rgba(0, 0, 0, 0.6);
opacity: 0.7;
color: white;
width: 1.5em;
height: 1.5em;
text-align: center;
line-height: 1.5em;
font-size: 1.5em;
cursor: pointer;
&:hover {
opacity: 1;
}
svg {
color: white;
}
}
.oauth-tokens {

View file

@ -68,9 +68,8 @@
class="delete-field button-unstyled -hover-highlight"
@click="deleteField(i)"
>
<!-- TODO something is wrong with v-show here -->
<FAIcon
v-if="newFields.length > 1"
v-show="newFields.length > 1"
icon="times"
/>
</button>
@ -107,17 +106,14 @@
:src="user.profile_image_url_original"
class="current-avatar"
>
<button
<FAIcon
v-if="!isDefaultAvatar && pickAvatarBtnVisible"
:title="$t('settings.reset_avatar')"
class="reset-button"
icon="times"
type="button"
@click="resetAvatar"
class="button-unstyled reset-button"
>
<FAIcon
icon="times"
type="button"
/>
</button>
/>
</div>
<p>{{ $t('settings.set_new_avatar') }}</p>
<button
@ -139,17 +135,14 @@
<h2>{{ $t('settings.profile_banner') }}</h2>
<div class="banner-background-preview">
<img :src="user.cover_photo">
<button
<FAIcon
v-if="!isDefaultBanner"
class="button-unstyled reset-button"
:title="$t('settings.reset_profile_banner')"
class="reset-button"
icon="times"
type="button"
@click="resetBanner"
>
<FAIcon
icon="times"
type="button"
/>
</button>
/>
</div>
<p>{{ $t('settings.set_new_profile_banner') }}</p>
<img
@ -181,17 +174,14 @@
<h2>{{ $t('settings.profile_background') }}</h2>
<div class="banner-background-preview">
<img :src="user.background_image">
<button
<FAIcon
v-if="!isDefaultBackground"
class="button-unstyled reset-button"
:title="$t('settings.reset_profile_background')"
class="reset-button"
icon="times"
type="button"
@click="resetBackground"
>
<FAIcon
icon="times"
type="button"
/>
</button>
/>
</div>
<p>{{ $t('settings.set_new_profile_background') }}</p>
<img

View file

@ -29,14 +29,14 @@
{{ $t('settings.style.preview.content') }}
</h4>
<i18n-t scope="global" keypath="settings.style.preview.text">
<i18n path="settings.style.preview.text">
<code style="font-family: var(--postCodeFont)">
{{ $t('settings.style.preview.mono') }}
</code>
<a style="color: var(--link)">
{{ $t('settings.style.preview.link') }}
</a>
</i18n-t>
</i18n>
<div class="icons">
<FAIcon
@ -72,16 +72,15 @@
:^)
</div>
<div class="content">
<i18n-t
keypath="settings.style.preview.fine_print"
<i18n
path="settings.style.preview.fine_print"
tag="span"
class="faint"
scope="global"
>
<a style="color: var(--faintLink)">
{{ $t('settings.style.preview.faint_link') }}
</a>
</i18n-t>
</i18n>
</div>
</div>
<div class="separator" />

View file

@ -1,3 +1,4 @@
import { set, delete as del } from 'vue'
import {
rgb2hex,
hex2rgb,
@ -33,7 +34,7 @@ import OpacityInput from 'src/components/opacity_input/opacity_input.vue'
import ShadowControl from 'src/components/shadow_control/shadow_control.vue'
import FontControl from 'src/components/font_control/font_control.vue'
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Select from 'src/components/select/select.vue'
@ -319,9 +320,9 @@ export default {
},
set (val) {
if (val) {
this.shadowsLocal[this.shadowSelected] = this.currentShadowFallback.map(_ => Object.assign({}, _))
set(this.shadowsLocal, this.shadowSelected, this.currentShadowFallback.map(_ => Object.assign({}, _)))
} else {
delete this.shadowsLocal[this.shadowSelected]
del(this.shadowsLocal, this.shadowSelected)
}
}
},
@ -333,7 +334,7 @@ export default {
return this.shadowsLocal[this.shadowSelected]
},
set (v) {
this.shadowsLocal[this.shadowSelected] = v
set(this.shadowsLocal, this.shadowSelected, v)
}
},
themeValid () {
@ -377,10 +378,6 @@ export default {
// To separate from other random JSON files and possible future source formats
_pleroma_theme_version: 2, theme, source
}
},
isActive () {
const tabSwitcher = this.$parent
return tabSwitcher ? tabSwitcher.isActive('theme') : false
}
},
components: {
@ -560,7 +557,7 @@ export default {
.filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal'))
.filter(_ => !v1OnlyNames.includes(_))
.forEach(key => {
this.$data[key] = undefined
set(this.$data, key, undefined)
})
},
@ -568,7 +565,7 @@ export default {
Object.keys(this.$data)
.filter(_ => _.endsWith('RadiusLocal'))
.forEach(key => {
this.$data[key] = undefined
set(this.$data, key, undefined)
})
},
@ -576,7 +573,7 @@ export default {
Object.keys(this.$data)
.filter(_ => _.endsWith('OpacityLocal'))
.forEach(key => {
this.$data[key] = undefined
set(this.$data, key, undefined)
})
},

View file

@ -268,6 +268,13 @@
}
}
.apply-container {
justify-content: center;
position: absolute;
bottom: 8px;
right: 5px;
}
.radius-item,
.color-item {
min-width: 20em;
@ -327,25 +334,16 @@
padding: 20px;
}
.apply-container {
.btn {
min-height: 28px;
min-width: 10em;
padding: 0 2em;
}
}
.btn {
margin-left: .25em;
margin-right: .25em;
}
}
.extra-content {
.apply-container {
display: flex;
flex-direction: row;
justify-content: space-around;
flex-grow: 1;
.btn {
flex-grow: 1;
min-height: 28px;
min-width: 0;
max-width: 10em;
padding: 0;
}
}
}

View file

@ -903,7 +903,6 @@
<div class="tab-header shadow-selector">
<div class="select-container">
{{ $t('settings.style.shadows.component') }}
{{ ' ' }}
<Select
id="shadow-switcher"
v-model="shadowSelected"
@ -925,7 +924,6 @@
>
{{ $t('settings.style.shadows.override') }}
</label>
{{ ' ' }}
<input
id="override"
v-model="currentShadowOverriden"
@ -951,30 +949,27 @@
:fallback="currentShadowFallback"
/>
<div v-if="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'">
<i18n-t
scope="global"
keypath="settings.style.shadows.filter_hint.always_drop_shadow"
<i18n
path="settings.style.shadows.filter_hint.always_drop_shadow"
tag="p"
>
<code>filter: drop-shadow()</code>
</i18n-t>
</i18n>
<p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p>
<i18n-t
scope="global"
keypath="settings.style.shadows.filter_hint.drop_shadow_syntax"
<i18n
path="settings.style.shadows.filter_hint.drop_shadow_syntax"
tag="p"
>
<code>drop-shadow</code>
<code>spread-radius</code>
<code>inset</code>
</i18n-t>
<i18n-t
scope="global"
keypath="settings.style.shadows.filter_hint.inset_classic"
</i18n>
<i18n
path="settings.style.shadows.filter_hint.inset_classic"
tag="p"
>
<code>box-shadow</code>
</i18n-t>
</i18n>
<p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p>
</div>
</div>
@ -1021,26 +1016,21 @@
</tab-switcher>
</keep-alive>
<teleport
v-if="isActive"
to="#unscrolled-content"
>
<div class="apply-container">
<button
class="btn button-default submit"
:disabled="!themeValid"
@click="setCustomTheme"
>
{{ $t('general.apply') }}
</button>
<button
class="btn button-default"
@click="clearAll"
>
{{ $t('settings.style.switcher.reset') }}
</button>
</div>
</teleport>
<div class="apply-container">
<button
class="btn button-default submit"
:disabled="!themeValid"
@click="setCustomTheme"
>
{{ $t('general.apply') }}
</button>
<button
class="btn button-default"
@click="clearAll"
>
{{ $t('settings.style.switcher.reset') }}
</button>
</div>
</div>
</template>

View file

@ -28,4 +28,4 @@
</div>
</div>
</template>
<script src="./version_tab.js" />
<script src="./version_tab.js">

View file

@ -30,19 +30,18 @@ const toModel = (object = {}) => ({
})
export default {
// 'modelValue' and 'Fallback' can be undefined, but if they are
// 'Value' and 'Fallback' can be undefined, but if they are
// initially vue won't detect it when they become something else
// therefore i'm using "ready" which should be passed as true when
// data becomes available
props: [
'modelValue', 'fallback', 'ready'
'value', 'fallback', 'ready'
],
emits: ['update:modelValue'],
data () {
return {
selectedId: 0,
// TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason)
cValue: (this.modelValue || this.fallback || []).map(toModel)
cValue: (this.value || this.fallback || []).map(toModel)
}
},
components: {
@ -71,7 +70,7 @@ export default {
}
},
beforeUpdate () {
this.cValue = this.modelValue || this.fallback
this.cValue = this.value || this.fallback
},
computed: {
anyShadows () {
@ -106,7 +105,7 @@ export default {
!this.usingFallback
},
usingFallback () {
return typeof this.modelValue === 'undefined'
return typeof this.value === 'undefined'
},
rgb () {
return hex2rgb(this.selected.color)

View file

@ -204,13 +204,12 @@
v-model="selected.alpha"
:disabled="!present"
/>
<i18n-t
scope="global"
keypath="settings.style.shadows.hintV3"
<i18n
path="settings.style.shadows.hintV3"
tag="p"
>
<code>--variable,mod</code>
</i18n-t>
</i18n>
</div>
</div>
</template>

View file

@ -98,7 +98,7 @@
.icon {
color: $fallback--text;
color: var(--panelText, $fallback--text);
color: var(--text, $fallback--text);
margin-right: 0.5em;
}

View file

@ -49,7 +49,7 @@ const SideDrawer = {
currentUser () {
return this.$store.state.users.currentUser
},
shout () { return this.$store.state.shout.joined },
shout () { return this.$store.state.shout.channel.state === 'joined' },
unseenNotifications () {
return unseenNotificationsFromStore(this.$store)
},

View file

@ -69,7 +69,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => {
const controlledName = `controlled${camelized}`
const uncontrolledName = `uncontrolled${camelized}`
res[name] = function () {
return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName]
return this[toggle] ? this[controlledName] : this[uncontrolledName]
}
return res
}, {})
@ -225,18 +225,12 @@ const Status = {
muteWordHits () {
return muteWordHits(this.status, this.muteWords)
},
rtBotStatus () {
return this.statusoid.user.bot
},
botStatus () {
return this.status.user.bot
},
botIndicator () {
return this.botStatus && !this.hideBotIndication
},
rtBotIndicator () {
return this.rtBotStatus && !this.hideBotIndication
},
mentionsLine () {
if (!this.headTailLinks) return []
const writtenSet = new Set(this.headTailLinks.writtenMentions.map(_ => _.url))
@ -311,7 +305,7 @@ const Status = {
return this.mergedConfig.hideWordFilteredPosts
},
hideStatus () {
return (!this.shouldNotMute) && (
return (this.virtualHidden || !this.shouldNotMute) && (
(this.muted && this.hideFilteredStatuses) ||
(this.userIsMuted && this.hideMutedUsers) ||
(this.status.thread_muted && this.hideMutedThreads) ||
@ -389,9 +383,6 @@ const Status = {
},
threadShowing () {
return this.controlledThreadDisplayStatus === 'showing'
},
visibilityLocalized () {
return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility)
}
},
methods: {
@ -481,6 +472,11 @@ const Status = {
'isSuspendable': function (val) {
this.suspendable = val
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
}
}

View file

@ -406,13 +406,13 @@
margin-left: 20px;
}
.post-avatar {
.avatar:not(.repeater-avatar) {
width: 40px;
height: 40px;
// TODO define those other way somehow?
// stylelint-disable rscss/class-format
&.-compact {
&.avatar-compact {
width: 32px;
height: 32px;
}

View file

@ -1,7 +1,6 @@
<template>
<div
v-if="!hideStatus"
ref="root"
class="Status"
:class="[{ '-focused': isFocused }, { '-conversation': inlineExpanded }]"
>
@ -78,7 +77,7 @@
<UserAvatar
v-if="retweet"
class="left-side repeater-avatar"
:bot="rtBotIndicator"
:bot="botIndicator"
:better-shadow="betterShadow"
:user="statusoid.user"
/>
@ -101,7 +100,6 @@
:to="retweeterProfileLink"
>{{ retweeter }}</router-link>
</span>
{{ ' ' }}
<FAIcon
icon="retweet"
class="repeat-icon"
@ -122,18 +120,17 @@
v-if="!noHeading"
class="left-side"
>
<a
:href="$router.resolve(userProfileLink).href"
@click.stop.prevent.capture="toggleUserExpanded"
<router-link
:to="userProfileLink"
@click.stop.prevent.capture.native="toggleUserExpanded"
>
<UserAvatar
class="post-avatar"
:bot="botIndicator"
:compact="compact"
:better-shadow="betterShadow"
:user="status.user"
/>
</a>
</router-link>
</div>
<div class="right-side">
<UserCard
@ -193,7 +190,7 @@
<span
v-if="status.visibility"
class="visibility-icon"
:title="visibilityLocalized"
:title="status.visibility | capitalize"
>
<FAIcon
fixed-width
@ -276,7 +273,6 @@
icon="reply"
flip="horizontal"
/>
{{ ' ' }}
<span
class="reply-to-text"
>
@ -296,6 +292,7 @@
:url="replyProfileLink"
:user-id="status.in_reply_to_user_id"
:user-screen-name="status.in_reply_to_screen_name"
:first-mention="false"
/>
</span>
@ -457,7 +454,6 @@
>
<div class="left-side">
<UserAvatar
class="post-avatar"
:compact="compact"
:bot="botIndicator"
/>

View file

@ -15,14 +15,14 @@
:emoji="status.emojis"
/>
<button
v-show="longSubject && showingLongSubject"
v-if="longSubject && showingLongSubject"
class="button-unstyled -link tall-subject-hider"
@click.prevent="toggleShowingLongSubject"
>
{{ $t("status.hide_full_subject") }}
</button>
<button
v-show="longSubject && !showingLongSubject"
v-else-if="longSubject"
class="button-unstyled -link tall-subject-hider"
@click.prevent="toggleShowingLongSubject"
>
@ -34,7 +34,7 @@
class="text-wrapper"
>
<button
v-show="hideTallStatus"
v-if="hideTallStatus"
class="button-unstyled -link tall-status-hider"
:class="{ '-focused': focused }"
@click.prevent="toggleShowMore"
@ -54,7 +54,7 @@
/>
<button
v-show="hideSubjectStatus"
v-if="hideSubjectStatus"
class="button-unstyled -link cw-status-hider"
@click.prevent="toggleShowMore"
>
@ -85,7 +85,7 @@
/>
</button>
<button
v-show="showingMore && !fullContent"
v-if="showingMore && !fullContent"
class="button-unstyled -link status-unhider"
@click.prevent="toggleShowMore"
>

Some files were not shown because too many files have changed in this diff Show more