Merge remote-tracking branch 'upstream/develop' into neckbeard
This commit is contained in:
commit
215ea3e790
|
@ -0,0 +1 @@
|
|||
Display quotes count on posts and add quotes list page
|
|
@ -0,0 +1 @@
|
|||
Option to only show scrobbles that are recent enough
|
|
@ -0,0 +1 @@
|
|||
Add caching system for themes3
|
|
@ -32,6 +32,7 @@
|
|||
"click-outside-vue3": "4.0.1",
|
||||
"cropperjs": "1.5.13",
|
||||
"escape-html": "1.0.3",
|
||||
"hash-sum": "^2.0.0",
|
||||
"js-cookie": "3.0.5",
|
||||
"localforage": "1.10.0",
|
||||
"parse-link-header": "2.0.0",
|
||||
|
|
|
@ -14,7 +14,7 @@ import { windowWidth, windowHeight } 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'
|
||||
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
||||
import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import { applyTheme, applyConfig, tryLoadCache } from '../services/style_setter/style_setter.js'
|
||||
import FaviconService from '../services/favicon_service/favicon_service.js'
|
||||
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
|
||||
|
||||
|
@ -353,21 +353,25 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
|
||||
await setConfig({ store })
|
||||
|
||||
const { customTheme, customThemeSource } = store.state.config
|
||||
const { customTheme, customThemeSource, forceThemeRecompilation } = store.state.config
|
||||
const { theme } = store.state.instance
|
||||
const customThemePresent = customThemeSource || customTheme
|
||||
|
||||
if (customThemePresent) {
|
||||
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
|
||||
applyTheme(customThemeSource)
|
||||
} else {
|
||||
applyTheme(customTheme)
|
||||
}
|
||||
if (!forceThemeRecompilation && tryLoadCache()) {
|
||||
store.commit('setThemeApplied')
|
||||
} else if (theme) {
|
||||
// do nothing, it will load asynchronously
|
||||
} else {
|
||||
console.error('Failed to load any theme!')
|
||||
if (customThemePresent) {
|
||||
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
|
||||
applyTheme(customThemeSource)
|
||||
} else {
|
||||
applyTheme(customTheme)
|
||||
}
|
||||
store.commit('setThemeApplied')
|
||||
} else if (theme) {
|
||||
// do nothing, it will load asynchronously
|
||||
} else {
|
||||
console.error('Failed to load any theme!')
|
||||
}
|
||||
}
|
||||
|
||||
applyConfig(store.state.config)
|
||||
|
|
|
@ -25,6 +25,7 @@ import ListsTimeline from 'components/lists_timeline/lists_timeline.vue'
|
|||
import ListsEdit from 'components/lists_edit/lists_edit.vue'
|
||||
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
|
||||
import AnnouncementsPage from 'components/announcements_page/announcements_page.vue'
|
||||
import QuotesTimeline from '../components/quotes_timeline/quotes_timeline.vue'
|
||||
|
||||
export default (store) => {
|
||||
const validateAuthenticatedRoute = (to, from, next) => {
|
||||
|
@ -51,6 +52,7 @@ export default (store) => {
|
|||
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
||||
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
||||
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
|
||||
{ name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline },
|
||||
{
|
||||
name: 'remote-user-profile-acct',
|
||||
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import Timeline from '../timeline/timeline.vue'
|
||||
|
||||
const QuotesTimeline = {
|
||||
created () {
|
||||
this.$store.commit('clearTimeline', { timeline: 'quotes' })
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId })
|
||||
},
|
||||
components: {
|
||||
Timeline
|
||||
},
|
||||
computed: {
|
||||
statusId () { return this.$route.params.id },
|
||||
timeline () { return this.$store.state.statuses.timelines.quotes }
|
||||
},
|
||||
watch: {
|
||||
statusId () {
|
||||
this.$store.commit('clearTimeline', { timeline: 'quotes' })
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId })
|
||||
}
|
||||
},
|
||||
unmounted () {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'quotes')
|
||||
}
|
||||
}
|
||||
|
||||
export default QuotesTimeline
|
|
@ -0,0 +1,10 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.quotes')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'quotes'"
|
||||
:status-id="statusId"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src='./quotes_timeline.js'></script>
|
|
@ -17,6 +17,10 @@ export default {
|
|||
units: {
|
||||
type: Array,
|
||||
default: () => allCssUnits
|
||||
},
|
||||
unitSet: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -30,6 +34,10 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getUnitString (value) {
|
||||
if (this.unitSet === 'none') return value
|
||||
return this.$t(['settings', 'units', this.unitSet, value].join('.'))
|
||||
},
|
||||
updateValue (e) {
|
||||
this.configSink(this.path, parseInt(e.target.value) + this.stateUnit)
|
||||
},
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<span
|
||||
v-if="matchesExpertLevel"
|
||||
class="SizeSetting"
|
||||
class="UnitSetting"
|
||||
>
|
||||
<label
|
||||
:for="path"
|
||||
|
@ -23,7 +23,7 @@
|
|||
:id="path"
|
||||
:model-value="stateUnit"
|
||||
:disabled="disabled"
|
||||
class="css-unit-input"
|
||||
class="unit-input unstyled"
|
||||
@change="updateUnit"
|
||||
>
|
||||
<option
|
||||
|
@ -31,7 +31,7 @@
|
|||
:key="option"
|
||||
:value="option"
|
||||
>
|
||||
{{ option }}
|
||||
{{ getUnitString(option) }}
|
||||
</option>
|
||||
</Select>
|
||||
{{ ' ' }}
|
||||
|
@ -42,20 +42,19 @@
|
|||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./size_setting.js"></script>
|
||||
<script src="./unit_setting.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
.SizeSetting {
|
||||
.UnitSetting {
|
||||
.number-input {
|
||||
max-width: 6.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.css-unit-input,
|
||||
.css-unit-input select {
|
||||
margin-left: 0.5em;
|
||||
width: 4em;
|
||||
max-width: 4em;
|
||||
.unit-input,
|
||||
.unit-input select {
|
||||
min-width: 4em;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
|
@ -31,10 +31,6 @@
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
select {
|
||||
min-width: 10em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
|
|
@ -31,10 +31,6 @@
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
select {
|
||||
min-width: 10em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { filter, trim, debounce } from 'lodash'
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
@ -19,6 +20,7 @@ const FilteringTab = {
|
|||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
UnitSetting,
|
||||
IntegerSetting
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -119,6 +119,17 @@
|
|||
{{ $t('settings.hide_scrobbles') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<UnitSetting
|
||||
key="hideScrobblesAfter"
|
||||
path="hideScrobblesAfter"
|
||||
:units="['m', 'h', 'd']"
|
||||
unitSet="time"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_scrobbles_after') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -3,7 +3,7 @@ import ChoiceSetting from '../helpers/choice_setting.vue'
|
|||
import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import FloatSetting from '../helpers/float_setting.vue'
|
||||
import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue'
|
||||
import UnitSetting, { defaultHorizontalUnits } from '../helpers/unit_setting.vue'
|
||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
@ -64,7 +64,7 @@ const GeneralTab = {
|
|||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
FloatSetting,
|
||||
SizeSetting,
|
||||
UnitSetting,
|
||||
InterfaceLanguageSwitcher,
|
||||
ScopeSelector,
|
||||
ProfileSettingIndicator
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
<li v-if="expertLevel > 0">
|
||||
{{ $t('settings.column_sizes') }}
|
||||
<div class="column-settings">
|
||||
<SizeSetting
|
||||
<UnitSetting
|
||||
v-for="column in columns"
|
||||
:key="column"
|
||||
:path="column + 'ColumnWidth'"
|
||||
|
@ -142,7 +142,7 @@
|
|||
expert="1"
|
||||
>
|
||||
{{ $t('settings.column_sizes_' + column) }}
|
||||
</SizeSetting>
|
||||
</UnitSetting>
|
||||
</div>
|
||||
</li>
|
||||
<li class="select-multiple">
|
||||
|
@ -200,6 +200,14 @@
|
|||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.post_look_feel') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="forceThemeRecompilation"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.force_theme_recompilation_debug') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="conversationDisplay"
|
||||
|
|
|
@ -373,6 +373,9 @@ const Status = {
|
|||
hidePostStats () {
|
||||
return this.mergedConfig.hidePostStats
|
||||
},
|
||||
shouldDisplayFavsAndRepeats () {
|
||||
return !this.hidePostStats && this.isFocused && (this.combinedFavsAndRepeatsUsers.length > 0 || this.statusFromGlobalRepository.quotes_count)
|
||||
},
|
||||
muteBotStatuses () {
|
||||
return this.mergedConfig.muteBotStatuses
|
||||
},
|
||||
|
@ -422,7 +425,27 @@ const Status = {
|
|||
return this.quotedStatus && this.displayQuote
|
||||
},
|
||||
scrobblePresent () {
|
||||
return !this.mergedConfig.hideScrobbles && this.status.user.latestScrobble && this.status.user.latestScrobble.artist
|
||||
if (this.mergedConfig.hideScrobbles) return false
|
||||
if (!this.status.user.latestScrobble) return false
|
||||
const value = this.mergedConfig.hideScrobblesAfter.match(/\d+/gs)[0]
|
||||
const unit = this.mergedConfig.hideScrobblesAfter.match(/\D+/gs)[0]
|
||||
let multiplier = 60 * 1000 // minutes is smallest unit
|
||||
switch (unit) {
|
||||
case 'm':
|
||||
break
|
||||
case 'h':
|
||||
multiplier *= 60 // hour
|
||||
break
|
||||
case 'd':
|
||||
multiplier *= 60 // hour
|
||||
multiplier *= 24 // day
|
||||
break
|
||||
}
|
||||
const maxAge = Number(value) * multiplier
|
||||
const createdAt = Date.parse(this.status.user.latestScrobble.created_at)
|
||||
const age = Date.now() - createdAt
|
||||
if (age > maxAge) return false
|
||||
return this.status.user.latestScrobble.artist
|
||||
},
|
||||
scrobble () {
|
||||
return this.status.user.latestScrobble
|
||||
|
|
|
@ -374,6 +374,7 @@
|
|||
font-weight: bolder;
|
||||
font-size: 1.1em;
|
||||
line-height: 1em;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
&:hover .stat-title {
|
||||
|
|
|
@ -484,7 +484,7 @@
|
|||
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="!hidePostStats && isFocused && combinedFavsAndRepeatsUsers.length > 0"
|
||||
v-if="shouldDisplayFavsAndRepeats"
|
||||
class="favs-repeated-users"
|
||||
>
|
||||
<div class="stats">
|
||||
|
@ -512,6 +512,19 @@
|
|||
</div>
|
||||
</div>
|
||||
</UserListPopover>
|
||||
<router-link
|
||||
v-if="statusFromGlobalRepository.quotes_count > 0"
|
||||
:to="{ name: 'quotes', params: { id: status.id } }"
|
||||
>
|
||||
<div
|
||||
class="stat-count"
|
||||
>
|
||||
<a class="stat-title">{{ $t('status.quotes') }}</a>
|
||||
<div class="stat-number">
|
||||
{{ statusFromGlobalRepository.quotes_count }}
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
<div class="avatar-row">
|
||||
<AvatarList :users="combinedFavsAndRepeatsUsers" />
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ const Timeline = {
|
|||
'title',
|
||||
'userId',
|
||||
'listId',
|
||||
'statusId',
|
||||
'tag',
|
||||
'embedded',
|
||||
'count',
|
||||
|
@ -121,6 +122,7 @@ const Timeline = {
|
|||
showImmediately,
|
||||
userId: this.userId,
|
||||
listId: this.listId,
|
||||
statusId: this.statusId,
|
||||
tag: this.tag
|
||||
})
|
||||
},
|
||||
|
@ -183,6 +185,7 @@ const Timeline = {
|
|||
showImmediately: true,
|
||||
userId: this.userId,
|
||||
listId: this.listId,
|
||||
statusId: this.statusId,
|
||||
tag: this.tag
|
||||
}).then(({ statuses }) => {
|
||||
if (statuses && statuses.length === 0) {
|
||||
|
|
|
@ -19,7 +19,8 @@ export const timelineNames = () => {
|
|||
bookmarks: 'nav.bookmarks',
|
||||
dms: 'nav.dms',
|
||||
'public-timeline': 'nav.public_tl',
|
||||
'public-external-timeline': 'nav.twkn'
|
||||
'public-external-timeline': 'nav.twkn',
|
||||
quotes: 'nav.quotes'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -678,7 +678,7 @@
|
|||
"autohide_floating_post_button": "Automaticky skrýt tlačítko nového příspěvku (mobilní zařízení)",
|
||||
"minimal_scopes_mode": "Minimalizovat možnosti rozsahu příspěvků",
|
||||
"conversation_display": "Styl zobrazení konverzací",
|
||||
"conversation_display_tree": "Stromový styl",
|
||||
"conversation_display_tree": "Stromové zobrazení",
|
||||
"conversation_display_tree_quick": "Stromový styl",
|
||||
"show_scrollbars": "Zobrazit posuvníky bočních sloupců",
|
||||
"third_column_mode": "Pokud je volné místo, zobrazit třetí sloupec obsahující",
|
||||
|
@ -863,7 +863,7 @@
|
|||
"favorites": "Oblíbené",
|
||||
"follow": "Sledovat",
|
||||
"follow_sent": "Požadavek odeslán!",
|
||||
"follow_progress": "Odeslílám požadavek…",
|
||||
"follow_progress": "Odesílám požadavek…",
|
||||
"follow_unfollow": "Přestat sledovat",
|
||||
"followees": "Sledovaní",
|
||||
"followers": "Sledující",
|
||||
|
|
|
@ -189,7 +189,8 @@
|
|||
"mobile_notifications": "Open notifications (there are unread ones)",
|
||||
"mobile_notifications_close": "Close notifications",
|
||||
"mobile_notifications_mark_as_seen": "Mark all as seen",
|
||||
"announcements": "Announcements"
|
||||
"announcements": "Announcements",
|
||||
"quotes": "Quotes"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Unknown status, searching for it…",
|
||||
|
@ -394,6 +395,14 @@
|
|||
"desc": "To enable two-factor authentication, enter the code from your two-factor app:"
|
||||
}
|
||||
},
|
||||
"units": {
|
||||
"time": {
|
||||
"m": "minutes",
|
||||
"s": "seconds",
|
||||
"h": "hours",
|
||||
"d": "days"
|
||||
}
|
||||
},
|
||||
"lists_navigation": "Show lists in navigation",
|
||||
"allow_following_move": "Allow auto-follow when following account moves",
|
||||
"attachmentRadius": "Attachments",
|
||||
|
@ -501,6 +510,7 @@
|
|||
"mute_bot_posts": "Mute bot posts",
|
||||
"hide_actor_type_indication": "Hide actor type (bots, groups, etc.) indication in posts",
|
||||
"hide_scrobbles": "Hide scrobbles",
|
||||
"hide_scrobbles_after": "Hide scrobbles older than",
|
||||
"mute_sensitive_posts": "Mute sensitive posts",
|
||||
"hide_all_muted_posts": "Hide muted posts",
|
||||
"max_thumbnails": "Maximum amount of thumbnails per post (empty = no limit)",
|
||||
|
@ -635,6 +645,7 @@
|
|||
"subject_line_email": "Like email: \"re: subject\"",
|
||||
"subject_line_mastodon": "Like mastodon: copy as is",
|
||||
"subject_line_noop": "Do not copy",
|
||||
"force_theme_recompilation_debug": "Disable theme cahe, force recompile on each boot (DEBUG)",
|
||||
"conversation_display": "Conversation display style",
|
||||
"conversation_display_tree": "Tree-style",
|
||||
"conversation_display_tree_quick": "Tree view",
|
||||
|
@ -1047,6 +1058,7 @@
|
|||
"status": {
|
||||
"favorites": "Favorites",
|
||||
"repeats": "Repeats",
|
||||
"quotes": "Quotes",
|
||||
"repeat_confirm": "Do you really want to repeat this status?",
|
||||
"repeat_confirm_title": "Repeat confirmation",
|
||||
"repeat_confirm_accept_button": "Repeat",
|
||||
|
|
|
@ -202,12 +202,13 @@ const api = {
|
|||
timeline = 'friends',
|
||||
tag = false,
|
||||
userId = false,
|
||||
listId = false
|
||||
listId = false,
|
||||
statusId = false
|
||||
}) {
|
||||
if (store.state.fetchers[timeline]) return
|
||||
|
||||
const fetcher = store.state.backendInteractor.startFetchingTimeline({
|
||||
timeline, store, userId, listId, tag
|
||||
timeline, store, userId, listId, statusId, tag
|
||||
})
|
||||
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@ export const defaultState = {
|
|||
theme: undefined,
|
||||
customTheme: undefined,
|
||||
customThemeSource: undefined,
|
||||
forceThemeRecompilation: false,
|
||||
hideISP: false,
|
||||
hideInstanceWallpaper: false,
|
||||
hideShoutbox: false,
|
||||
|
@ -42,6 +43,7 @@ export const defaultState = {
|
|||
hideAttachments: false,
|
||||
hideAttachmentsInConv: false,
|
||||
hideScrobbles: false,
|
||||
hideScrobblesAfter: '2d',
|
||||
maxThumbnails: 512,
|
||||
hideNsfw: true,
|
||||
preloadImage: true,
|
||||
|
|
|
@ -378,7 +378,8 @@ const instance = {
|
|||
commit('setInstanceOption', { name: 'themeData', value: themeData })
|
||||
// No need to apply theme if there's user theme already
|
||||
const { customTheme } = rootState.config
|
||||
if (customTheme) return
|
||||
const { themeApplied } = rootState.interface
|
||||
if (customTheme || themeApplied) return
|
||||
|
||||
// New theme presets don't have 'theme' property, they use 'source'
|
||||
const themeSource = themeData.source
|
||||
|
|
|
@ -108,6 +108,7 @@ const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements'
|
|||
const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
||||
const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}`
|
||||
const PLEROMA_SCROBBLES_URL = id => `/api/v1/pleroma/accounts/${id}/scrobbles`
|
||||
const PLEROMA_STATUS_QUOTES_URL = id => `/api/v1/pleroma/statuses/${id}/quotes`
|
||||
const PLEROMA_USER_FAVORITES_TIMELINE_URL = id => `/api/v1/pleroma/accounts/${id}/favourites`
|
||||
|
||||
const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
|
||||
|
@ -685,6 +686,7 @@ const fetchTimeline = ({
|
|||
until = false,
|
||||
userId = false,
|
||||
listId = false,
|
||||
statusId = false,
|
||||
tag = false,
|
||||
withMuted = false,
|
||||
replyVisibility = 'all',
|
||||
|
@ -702,7 +704,8 @@ const fetchTimeline = ({
|
|||
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
||||
publicFavorites: PLEROMA_USER_FAVORITES_TIMELINE_URL,
|
||||
tag: MASTODON_TAG_TIMELINE_URL,
|
||||
bookmarks: MASTODON_BOOKMARK_TIMELINE_URL
|
||||
bookmarks: MASTODON_BOOKMARK_TIMELINE_URL,
|
||||
quotes: PLEROMA_STATUS_QUOTES_URL
|
||||
}
|
||||
const isNotifications = timeline === 'notifications'
|
||||
const params = []
|
||||
|
@ -721,6 +724,10 @@ const fetchTimeline = ({
|
|||
url = url(listId)
|
||||
}
|
||||
|
||||
if (timeline === 'quotes') {
|
||||
url = url(statusId)
|
||||
}
|
||||
|
||||
if (minId) {
|
||||
params.push(['min_id', minId])
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import followRequestFetcher from '../../services/follow_request_fetcher/follow_r
|
|||
import listsFetcher from '../../services/lists_fetcher/lists_fetcher.service.js'
|
||||
|
||||
const backendInteractorService = credentials => ({
|
||||
startFetchingTimeline ({ timeline, store, userId = false, listId = false, tag }) {
|
||||
return timelineFetcher.startFetching({ timeline, store, credentials, userId, listId, tag })
|
||||
startFetchingTimeline ({ timeline, store, userId = false, listId = false, statusId = false, tag }) {
|
||||
return timelineFetcher.startFetching({ timeline, store, credentials, userId, listId, statusId, tag })
|
||||
},
|
||||
|
||||
fetchTimeline (args) {
|
||||
|
|
|
@ -331,6 +331,7 @@ export const parseStatus = (data) => {
|
|||
output.quote_id = pleroma.quote_id ? pleroma.quote_id : (output.quote ? output.quote.id : undefined)
|
||||
output.quote_url = pleroma.quote_url
|
||||
output.quote_visible = pleroma.quote_visible
|
||||
output.quotes_count = pleroma.quotes_count
|
||||
} else {
|
||||
output.text = data.content
|
||||
output.summary = data.spoiler_text
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { hex2rgb } from '../color_convert/color_convert.js'
|
||||
import { generatePreset } from '../theme_data/theme_data.service.js'
|
||||
import { init } from '../theme_data/theme_data_3.service.js'
|
||||
import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js'
|
||||
import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js'
|
||||
import { getCssRules } from '../theme_data/css_utils.js'
|
||||
import { defaultState } from '../../modules/config.js'
|
||||
|
@ -87,9 +87,37 @@ export const generateTheme = async (input, callbacks) => {
|
|||
return { lazyProcessFunc: processChunk }
|
||||
}
|
||||
|
||||
export const applyTheme = async (input) => {
|
||||
export const tryLoadCache = () => {
|
||||
const json = localStorage.getItem('pleroma-fe-theme-cache')
|
||||
if (!json) return null
|
||||
let cache
|
||||
try {
|
||||
cache = JSON.parse(json)
|
||||
} catch (e) {
|
||||
console.error('Failed to decode theme cache:', e)
|
||||
return false
|
||||
}
|
||||
if (cache.engineChecksum === getEngineChecksum()) {
|
||||
const styleSheet = new CSSStyleSheet()
|
||||
const lazyStyleSheet = new CSSStyleSheet()
|
||||
|
||||
cache.data[0].forEach(rule => styleSheet.insertRule(rule, 'index-max'))
|
||||
cache.data[1].forEach(rule => lazyStyleSheet.insertRule(rule, 'index-max'))
|
||||
|
||||
document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
|
||||
|
||||
return true
|
||||
} else {
|
||||
console.warn('Engine checksum doesn\'t match, cache not usable, clearing')
|
||||
localStorage.removeItem('pleroma-fe-theme-cache')
|
||||
}
|
||||
}
|
||||
|
||||
export const applyTheme = async (input, onFinish = (data) => {}) => {
|
||||
const styleSheet = new CSSStyleSheet()
|
||||
const styleArray = []
|
||||
const lazyStyleSheet = new CSSStyleSheet()
|
||||
const lazyStyleArray = []
|
||||
|
||||
const { lazyProcessFunc } = await generateTheme(
|
||||
input,
|
||||
|
@ -97,8 +125,10 @@ export const applyTheme = async (input) => {
|
|||
onNewRule (rule, isLazy) {
|
||||
if (isLazy) {
|
||||
lazyStyleSheet.insertRule(rule, 'index-max')
|
||||
lazyStyleArray.push(rule)
|
||||
} else {
|
||||
styleSheet.insertRule(rule, 'index-max')
|
||||
styleArray.push(rule)
|
||||
}
|
||||
},
|
||||
onEagerFinished () {
|
||||
|
@ -106,6 +136,9 @@ export const applyTheme = async (input) => {
|
|||
},
|
||||
onLazyFinished () {
|
||||
document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
|
||||
const cache = { engineChecksum: getEngineChecksum(), data: [styleArray, lazyStyleArray] }
|
||||
onFinish(cache)
|
||||
localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { convert, brightness } from 'chromatism'
|
||||
import sum from 'hash-sum'
|
||||
import { flattenDeep } from 'lodash'
|
||||
import {
|
||||
alphaBlend,
|
||||
|
@ -142,8 +143,12 @@ componentsContext.keys().forEach(key => {
|
|||
components[component.name] = component
|
||||
})
|
||||
|
||||
const engineChecksum = sum(components)
|
||||
|
||||
const ruleToSelector = genericRuleToSelector(components)
|
||||
|
||||
export const getEngineChecksum = () => engineChecksum
|
||||
|
||||
export const init = (extraRuleset, ultimateBackgroundColor) => {
|
||||
const staticVars = {}
|
||||
const stacked = {}
|
||||
|
@ -463,6 +468,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|||
return {
|
||||
lazy: result.filter(x => typeof x === 'function'),
|
||||
eager: result.filter(x => typeof x !== 'function'),
|
||||
staticVars
|
||||
staticVars,
|
||||
engineChecksum
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ const fetchAndUpdate = ({
|
|||
showImmediately = false,
|
||||
userId = false,
|
||||
listId = false,
|
||||
statusId = false,
|
||||
tag = false,
|
||||
until,
|
||||
since
|
||||
|
@ -47,6 +48,7 @@ const fetchAndUpdate = ({
|
|||
|
||||
args.userId = userId
|
||||
args.listId = listId
|
||||
args.statusId = statusId
|
||||
args.tag = tag
|
||||
args.withMuted = !hideMutedPosts
|
||||
if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) {
|
||||
|
@ -78,15 +80,15 @@ const fetchAndUpdate = ({
|
|||
})
|
||||
}
|
||||
|
||||
const startFetching = ({ timeline = 'friends', credentials, store, userId = false, listId = false, tag = false }) => {
|
||||
const startFetching = ({ timeline = 'friends', credentials, store, userId = false, listId = false, statusId = false, tag = false }) => {
|
||||
const rootState = store.rootState || store.state
|
||||
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
||||
const showImmediately = timelineData.visibleStatuses.length === 0
|
||||
timelineData.userId = userId
|
||||
timelineData.listId = listId
|
||||
fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, listId, tag })
|
||||
fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, listId, statusId, tag })
|
||||
const boundFetchAndUpdate = () =>
|
||||
fetchAndUpdate({ timeline, credentials, store, userId, listId, tag })
|
||||
fetchAndUpdate({ timeline, credentials, store, userId, listId, statusId, tag })
|
||||
return promiseInterval(boundFetchAndUpdate, 10000)
|
||||
}
|
||||
const timelineFetcher = {
|
||||
|
|
Loading…
Reference in New Issue