Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None
diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js
index 3d373283f3..7aa4bbc456 100644
--- a/src/components/settings/settings.js
+++ b/src/components/settings/settings.js
@@ -1,27 +1,31 @@
import StyleSwitcher from '../style_switcher/style_switcher.vue'
const settings = {
data () {
return {
hideAttachmentsLocal: this.$store.state.config.hideAttachments,
hideAttachmentsInConvLocal: this.$store.state.config.hideAttachmentsInConv,
- hideNsfwLocal: this.$store.state.config.hideNsfw
+ hideNsfwLocal: this.$store.state.config.hideNsfw,
+ muteWordsString: this.$store.state.config.muteWords.join('\n')
}
},
components: {
StyleSwitcher
},
watch: {
hideAttachmentsLocal (value) {
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
},
hideAttachmentsInConvLocal (value) {
this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })
},
hideNsfwLocal (value) {
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
+ },
+ muteWordsString (value) {
+ this.$store.dispatch('setOption', { name: 'muteWords', value: value.split('\n') })
}
}
}
export default settings
diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue
index 478d761a9f..33d46e7e3b 100644
--- a/src/components/settings/settings.vue
+++ b/src/components/settings/settings.vue
@@ -1,42 +1,51 @@
<template>
<div class="settings panel panel-default base00-background">
<div class="panel-heading base01-background base04">
Settings
</div>
<div class="panel-body">
<div class="setting-item">
<h2>Theme</h2>
<style-switcher></style-switcher>
</div>
+ <div class="setting-item">
+ <h2>Filtering</h2>
+ <p>All notices containing these words will be muted, one per line</p>
+ <textarea id="muteWords" v-model="muteWordsString"></textarea>
+ </div>
<div class="setting-item">
<h2>Attachments</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
<label for="hideAttachments">Hide attachments in timeline</label>
</li>
<li>
<input type="checkbox" id="hideAttachmentsInConv" v-model="hideAttachmentsInConvLocal">
<label for="hideAttachmentsInConv">Hide attachments in conversations</label>
</li>
<li>
<input type="checkbox" id="hideNsfw" v-model="hideNsfwLocal">
<label for="hideNsfw">Enable clickthrough NSFW attachment hiding</label>
</li>
</ul>
</div>
</div>
</div>
</template>
<script src="./settings.js">
</script>
-<style>
+<style lang="scss">
.setting-item {
margin: 1em 1em 1.4em;
+ textarea {
+ width: 100%;
+ height: 100px;
+ }
}
.setting-list {
list-style-type: none;
}
</style>
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 87fff8792d..bb026fe1f6 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -1,78 +1,90 @@
import Attachment from '../attachment/attachment.vue'
import FavoriteButton from '../favorite_button/favorite_button.vue'
import RetweetButton from '../retweet_button/retweet_button.vue'
import DeleteButton from '../delete_button/delete_button.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue'
import UserCardContent from '../user_card_content/user_card_content.vue'
+import { filter } from 'lodash'
const Status = {
props: [
'statusoid',
'expandable',
'inConversation',
'focused'
],
data: () => ({
replying: false,
expanded: false,
unmuted: false,
userExpanded: false
}),
computed: {
+ muteWords () {
+ return this.$store.state.config.muteWords
+ },
hideAttachments () {
return (this.$store.state.config.hideAttachments && !this.inConversation) ||
(this.$store.state.config.hideAttachmentsInConv && this.inConversation)
},
retweet () { return !!this.statusoid.retweeted_status },
retweeter () { return this.statusoid.user.name },
status () {
if (this.retweet) {
return this.statusoid.retweeted_status
} else {
return this.statusoid
}
},
loggedIn () {
return !!this.$store.state.users.currentUser
},
- muted () { return !this.unmuted && this.status.user.muted },
+ muteWordHits () {
+ const statusText = this.status.text.toLowerCase()
+ const hits = filter(this.muteWords, (muteWord) => {
+ return statusText.includes(muteWord.toLowerCase())
+ })
+
+ return hits
+ },
+ muted () { return !this.unmuted && (this.status.user.muted || this.muteWordHits.length > 0) },
isReply () { return !!this.status.in_reply_to_status_id },
borderColor () {
return {
borderBottomColor: this.$store.state.config.colors['base02']
}
}
},
components: {
Attachment,
FavoriteButton,
RetweetButton,
DeleteButton,
PostStatusForm,
UserCardContent
},
methods: {
linkClicked ({target}) {
if (target.tagName === 'SPAN') {
target = target.parentNode
}
if (target.tagName === 'A') {
window.open(target.href, '_blank')
}
},
toggleReplying () {
this.replying = !this.replying
},
toggleExpanded () {
this.$emit('toggleExpanded')
},
toggleMute () {
this.unmuted = !this.unmuted
},
toggleUserExpanded () {
this.userExpanded = !this.userExpanded
}
}
}
export default Status
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 993ce79691..642f114320 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,194 +1,199 @@
<template>
<div class="status-el base00-background base03-border" v-if="!status.deleted" v-bind:class="[{ 'base01-background': focused }, { 'status-conversation': inConversation }]" >
<template v-if="muted">
<div class="media status container muted">
<small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
+ <small class="muteWords">{{muteWordHits.join(', ')}}</small>
<a href="#" class="unmute" @click.prevent="toggleMute"><i class="icon-eye-off"></i></a>
</div>
</template>
<template v-if="!muted">
<div v-if="retweet" class="media container retweet-info">
<div class="media-left">
<i class='fa icon-retweet retweeted'></i>
</div>
<div class="media-body">
Retweeted by {{retweeter}}
</div>
</div>
<div class="media status container">
<div class="media-left">
<a :href="status.user.statusnet_profile_url">
<img @click.prevent="toggleUserExpanded" class='avatar' :src="status.user.profile_image_url_original">
</a>
</div>
<div class="media-body">
<div class="base05 base05=border usercard" v-if="userExpanded">
<user-card-content :user="status.user"></user-card-content>
</div>
<div class="user-content">
<h4 class="media-heading">
{{status.user.name}}
<small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
<small v-if="status.in_reply_to_screen_name"> &gt;
<router-link :to="{ name: 'user-profile', params: { id: status.in_reply_to_user_id } }">
{{status.in_reply_to_screen_name}}
</router-link>
</small>
<template v-if="isReply">
<small>
<router-link :to="{ name: 'conversation', params: { id: status.in_reply_to_status_id } }">
<i class="icon-reply"></i>
</router-link>
</small>
</template>
-
<small>
<router-link :to="{ name: 'conversation', params: { id: status.id } }">
<timeago :since="status.created_at" :auto-update="60"></timeago>
</router-link>
</small>
<template v-if="expandable">
-
<small>
<a href="#" @click.prevent="toggleExpanded" ><i class="icon-plus-squared"></i></a>
</small>
- <small v-if="status.user.muted">
+ <small v-if="unmuted">
<a href="#" @click.prevent="toggleMute" ><i class="icon-eye-off"></i></a>
</small>
</template>
<small v-if="!status.is_local" class="source_url">
<a :href="status.external_url" target="_blank" ><i class="icon-binoculars"></i></a>
</small>
</h4>
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
<div v-if='status.attachments' class='attachments'>
<attachment v-if="!hideAttachments" :status-id="status.id" :nsfw="status.nsfw" :attachment="attachment" v-for="attachment in status.attachments">
</attachment>
</div>
</div>
<div v-if="loggedIn">
<div class='status-actions'>
<div>
<a href="#" v-on:click.prevent="toggleReplying">
<i class="fa icon-reply" :class="{'icon-reply-active': replying}"></i>
</a>
</div>
<retweet-button :status=status></retweet-button>
<favorite-button :status=status></favorite-button>
<delete-button :status=status></delete-button>
</div>
</div>
</div>
</div>
<div class="status base00-background container" v-if="replying">
<div class="reply-left"/>
<post-status-form class="reply-body" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" v-on:posted="toggleReplying"/>
</div>
</template>
</div>
</template>
<script src="./status.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
.status-el {
hyphens: auto;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
border-left-width: 0px;
.user-content {
min-height: 52px;
padding-top: 1px;
}
.source_url {
float: right;
}
.greentext {
color: green;
}
a {
display: inline-block;
word-break: break-all;
}
.status-content {
margin: 3px 15px 4px 0;
}
p {
margin: 0;
margin-top: 0.2em;
margin-bottom: 0.5em;
}
}
.status-conversation {
border-left-style: solid;
}
.status-actions {
padding-top: 5px;
}
.icon-reply:hover {
color: $blue;
}
.icon-reply-active {
color: $blue;
}
.status .avatar {
width: 48px;
}
.status.compact .avatar {
width: 32px;
}
.status {
padding: 0.65em 0.7em 0.8em 0.8em;
border-bottom: 1px solid;
border-bottom-color: inherit;
border-left: 4px rgba(255, 48, 16, 0.65);
border-left-style: inherit;
}
.muted {
padding: 0.1em 0.7em 0.1em 0.8em;
button {
margin-left: auto;
}
+
+ .muteWords {
+ margin-left: 10px;
+ }
}
a.unmute {
display: block;
margin-left: auto;
}
.usercard {
border-style: solid;
border-width: 1px;
border-radius: 10px;
margin-bottom: 1em;
margin-top: 0.2em;
}
.reply-left {
flex: 0;
min-width: 48px;
}
.reply-body {
flex: 1;
}
</style>
diff --git a/src/main.js b/src/main.js
index ab0fd6c08b..969ca8dc0c 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,89 +1,90 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import App from './App.vue'
import PublicTimeline from './components/public_timeline/public_timeline.vue'
import PublicAndExternalTimeline from './components/public_and_external_timeline/public_and_external_timeline.vue'
import FriendsTimeline from './components/friends_timeline/friends_timeline.vue'
import ConversationPage from './components/conversation-page/conversation-page.vue'
import Mentions from './components/mentions/mentions.vue'
import UserProfile from './components/user_profile/user_profile.vue'
import Settings from './components/settings/settings.vue'
import statusesModule from './modules/statuses.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
import VueTimeago from 'vue-timeago'
import createPersistedState from './lib/persisted_state.js'
Vue.use(Vuex)
Vue.use(VueRouter)
Vue.use(VueTimeago, {
locale: 'en-US',
locales: {
'en-US': require('vue-timeago/locales/en-US.json')
}
})
const persistedStateOptions = {
paths: [
'config.hideAttachments',
'config.hideAttachmentsInConv',
'config.hideNsfw',
+ 'config.muteWords',
'statuses.notifications',
'users.users'
]
}
const store = new Vuex.Store({
modules: {
statuses: statusesModule,
users: usersModule,
api: apiModule,
config: configModule
},
plugins: [createPersistedState(persistedStateOptions)],
strict: process.env.NODE_ENV !== 'production'
})
const routes = [
{ name: 'root', path: '/', redirect: '/main/all' },
{ path: '/main/all', component: PublicAndExternalTimeline },
{ path: '/main/public', component: PublicTimeline },
{ path: '/main/friends', component: FriendsTimeline },
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
{ name: 'user-profile', path: '/users/:id', component: UserProfile },
{ name: 'mentions', path: '/:username/mentions', component: Mentions },
{ name: 'settings', path: '/settings', component: Settings }
]
const router = new VueRouter({
mode: 'history',
routes,
scrollBehavior: (to, from, savedPosition) => {
if (to.matched.some(m => m.meta.dontScroll)) {
return false
}
return savedPosition || { x: 0, y: 0 }
}
})
/* eslint-disable no-new */
new Vue({
router,
store,
el: '#app',
render: h => h(App)
})
window.fetch('/static/config.json')
.then((res) => res.json())
.then(({name, theme, background, logo}) => {
store.dispatch('setOption', { name: 'name', value: name })
store.dispatch('setOption', { name: 'theme', value: theme })
store.dispatch('setOption', { name: 'background', value: background })
store.dispatch('setOption', { name: 'logo', value: logo })
})
diff --git a/src/modules/config.js b/src/modules/config.js
index 05b4ab3bbe..f59dc6f0d7 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -1,37 +1,38 @@
import { set } from 'vue'
import StyleSetter from '../services/style_setter/style_setter.js'
const defaultState = {
name: 'Pleroma FE',
colors: {},
hideAttachments: false,
hideAttachmentsInConv: false,
- hideNsfw: true
+ hideNsfw: true,
+ muteWords: []
}
const config = {
state: defaultState,
mutations: {
setOption (state, { name, value }) {
set(state, name, value)
}
},
actions: {
setPageTitle ({state}, option = '') {
document.title = `${option} ${state.name}`
},
setOption ({ commit, dispatch }, { name, value }) {
commit('setOption', {name, value})
switch (name) {
case 'name':
dispatch('setPageTitle')
break
case 'theme':
const fullPath = `/static/css/${value}`
StyleSetter.setStyle(fullPath, commit)
}
}
}
}
export default config

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 2, 8:59 AM (2 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1804024
Default Alt Text
(16 KB)

Event Timeline