Compare commits
16 commits
d8e19faf5d
...
facf0de397
Author | SHA1 | Date | |
---|---|---|---|
|
facf0de397 | ||
|
1f84b79f4e | ||
|
05577aea54 | ||
|
7550b8cbd2 | ||
|
6c5fc53789 | ||
|
5bbfa5ab25 | ||
|
2abde63afc | ||
|
dc4cbbe830 | ||
|
bfc1091f7f | ||
|
6b37e5baec | ||
|
d7059dbf3d | ||
|
023c1d5ff0 | ||
|
00df9c9c32 | ||
|
8ee5122909 | ||
|
5ad5f4c51e | ||
|
d67180c1e6 |
16 changed files with 477 additions and 263 deletions
1
changelog.d/date-absolute.add
Normal file
1
changelog.d/date-absolute.add
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Support displaying time in absolute format
|
173
index.html
173
index.html
|
@ -4,45 +4,72 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
|
||||||
<link rel="icon" type="image/png" href="/favicon.png">
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
<style id="pleroma-eager-styles" type="text/css"></style>
|
<style id="splashscreen">
|
||||||
<style id="pleroma-lazy-styles" type="text/css"></style>
|
#webpack-hot-middleware-clientOverlay {
|
||||||
<!--server-generated-meta-->
|
z-index: 9999999999999999999999999999;
|
||||||
</head>
|
}
|
||||||
<body style="margin: 0; padding: 0">
|
|
||||||
<noscript>To use Pleroma, please enable JavaScript.</noscript>
|
#splash {
|
||||||
<!-- putting styles here to avoid having to wait for styles to load up -->
|
--scale: 1;
|
||||||
<div id="splash" style="
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
align-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #0f161e;
|
background: #0f161e;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
color: #b9b9ba;
|
color: #b9b9ba;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 999999;
|
z-index: 9999;
|
||||||
"
|
font-size: calc(1vw + 1vh + 1vmin);
|
||||||
>
|
}
|
||||||
<img
|
|
||||||
style="
|
#splash-credit {
|
||||||
width: 30vh;
|
position: absolute;
|
||||||
margin-top: 1vh;
|
font-size: 14px;
|
||||||
margin-bottom: 0.5vh;
|
bottom: 0;
|
||||||
"
|
right: 0;
|
||||||
src="/static/pleromatan_apology_fox.png"
|
}
|
||||||
/>
|
|
||||||
<div
|
#splash-container {
|
||||||
id="throbber"
|
align-items: center;
|
||||||
style='
|
}
|
||||||
--logoChunkSize: 2vh;
|
|
||||||
|
#mascot-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mascot:not(.orz) {
|
||||||
|
margin-bottom: 2em
|
||||||
|
object-position: 2.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mascot-temp.orz {
|
||||||
|
margin-bottom: 2em
|
||||||
|
object-position: 2.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mascot,
|
||||||
|
#mascot-temp {
|
||||||
|
width: calc(10em * var(--scale));
|
||||||
|
height: calc(8em * var(--scale));
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
#throbber {
|
||||||
display: grid;
|
display: grid;
|
||||||
margin-top: 2.5vh;
|
width: calc(5em * 0.5 * var(--scale));
|
||||||
margin-bottom: 0.5vh;
|
height: calc(8em * 0.5 * var(--scale));
|
||||||
width: 30vw;
|
grid-template-rows: repeat(8, 1fr);
|
||||||
grid-template-rows: repeat(8, var(--logoChunkSize));
|
grid-template-columns: repeat(5, 1fr);
|
||||||
grid-template-columns: repeat(5, var(--logoChunkSize));
|
|
||||||
grid-template-areas: "P P . L L"
|
grid-template-areas: "P P . L L"
|
||||||
"P P . L L"
|
"P P . L L"
|
||||||
"P P . L L"
|
"P P . L L"
|
||||||
|
@ -51,57 +78,69 @@
|
||||||
"P P . . ."
|
"P P . . ."
|
||||||
"P P . E E"
|
"P P . E E"
|
||||||
"P P . E E";
|
"P P . E E";
|
||||||
width: auto;
|
}
|
||||||
'
|
|
||||||
>
|
.chunk {
|
||||||
<div
|
|
||||||
style="
|
|
||||||
background-color: #e2b188;
|
background-color: #e2b188;
|
||||||
|
box-shadow: 0.01em 0.01em 0.1em 0 #e2b188;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chunk-P {
|
||||||
grid-area: P;
|
grid-area: P;
|
||||||
border-top-left-radius: calc(var(--logoChunkSize) / 2);
|
border-top-left-radius: calc(var(--logoChunkSize) / 2);
|
||||||
box-shadow: 0.1vh 0.1vh 1vh 0 #e2b188;
|
}
|
||||||
"
|
|
||||||
>
|
#chunk-L {
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #e2b188;
|
|
||||||
grid-area: L;
|
grid-area: L;
|
||||||
border-bottom-right-radius: calc(var(--logoChunkSize) / 2);
|
border-bottom-right-radius: calc(var(--logoChunkSize) / 2);
|
||||||
box-shadow: 0.1vh 0.1vh 1vh 0 #e2b188;
|
}
|
||||||
"
|
|
||||||
>
|
#chunk-E {
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #e2b188;
|
|
||||||
grid-area: E;
|
grid-area: E;
|
||||||
border-bottom-right-radius: calc(var(--logoChunkSize) / 2);
|
border-bottom-right-radius: calc(var(--logoChunkSize) / 2);
|
||||||
box-shadow: 0.1vh 0.1vh 1vh 0 #e2b188;
|
}
|
||||||
"
|
|
||||||
>
|
#status {
|
||||||
</div>
|
line-height: 2;
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id="status"
|
|
||||||
class="css-ok"
|
|
||||||
style="
|
|
||||||
margin-top: 3.5vh;
|
|
||||||
height: 4vh;
|
|
||||||
line-height: 4vh;
|
|
||||||
font-size: 4vh;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
"
|
}
|
||||||
>
|
|
||||||
|
@media (prefers-reduced-motion) {
|
||||||
|
#throbber {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style id="pleroma-eager-styles" type="text/css"></style>
|
||||||
|
<style id="pleroma-lazy-styles" type="text/css"></style>
|
||||||
|
<!--server-generated-meta-->
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0">
|
||||||
|
<noscript>To use Pleroma, please enable JavaScript.</noscript>
|
||||||
|
<!-- putting styles here to avoid having to wait for styles to load up -->
|
||||||
|
<div id="splash">
|
||||||
|
<div id="splash-credit">
|
||||||
|
Art by pipivovott
|
||||||
|
</div>
|
||||||
|
<div id="splash-container">
|
||||||
|
<div id="mascot-container">
|
||||||
|
<div id="throbber">
|
||||||
|
<div class="chunk" id="chunk-P">
|
||||||
|
</div>
|
||||||
|
<div class="chunk" id="chunk-L">
|
||||||
|
</div>
|
||||||
|
<div class="chunk" id="chunk-E">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img id="mascot" src="/static/pleromatan_apology_fox.png">
|
||||||
|
</div>
|
||||||
|
<div id="status" class="css-ok">
|
||||||
<!-- (。>﹏<) -->
|
<!-- (。>﹏<) -->
|
||||||
<span class="initial-text">(。>﹏<)</span>
|
<span class="initial-text">(。>﹏<)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div id="app" class="hidden"></div>
|
<div id="app" class="hidden"></div>
|
||||||
<div id="modal"></div>
|
<div id="modal"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
|
|
79
src/App.scss
79
src/App.scss
|
@ -919,7 +919,6 @@ option {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 2s;
|
transition: opacity 2s;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
z-index: 9999999999999999999999999999;
|
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -938,13 +937,80 @@ option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#mascot-container {
|
||||||
|
perspective: 60em;
|
||||||
|
perspective-origin: 0 -15em;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
}
|
||||||
|
|
||||||
#throbber {
|
#throbber {
|
||||||
animation-duration: 2s;
|
animation-duration: 3s;
|
||||||
animation-name: bounce;
|
animation-name: bounce;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
animation-direction: normal;
|
animation-direction: normal;
|
||||||
transform-origin: bottom center;
|
transform-origin: bottom center;
|
||||||
|
|
||||||
|
--defaultY: 0;
|
||||||
|
|
||||||
|
&.dead {
|
||||||
|
animation-name: dead;
|
||||||
|
animation-duration: 3s;
|
||||||
|
// animation-iteration-count: 1;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
transform: rotateX(90deg) rotateY(0) rotateZ(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dead {
|
||||||
|
0% {
|
||||||
|
transform: rotateX(0) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
5% {
|
||||||
|
transform: rotateX(0) rotateY(0) rotateZ(1deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
10% {
|
||||||
|
transform: rotateX(0) rotateY(0) rotateZ(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
15% {
|
||||||
|
transform: rotateX(0) rotateY(0) rotateZ(3deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
transform: rotateX(0) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
transform: rotateX(0) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
transform: rotateX(10deg) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
35% {
|
||||||
|
transform: rotateX(-10deg) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: rotateX(10deg) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
45% {
|
||||||
|
transform: rotateX(-10deg) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: rotateX(10deg) rotateY(0) rotateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotateX(90deg) rotateY(0) rotateZ(-45deg);
|
||||||
|
transition-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); /* easeInQuint */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes bounce {
|
@keyframes bounce {
|
||||||
0% {
|
0% {
|
||||||
scale: 1 1;
|
scale: 1 1;
|
||||||
|
@ -955,24 +1021,28 @@ option {
|
||||||
10% {
|
10% {
|
||||||
scale: 1.2 0.8;
|
scale: 1.2 0.8;
|
||||||
translate: 0 0;
|
translate: 0 0;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-out;
|
animation-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
30% {
|
30% {
|
||||||
scale: 0.9 1.1;
|
scale: 0.9 1.1;
|
||||||
translate: 0 -40%;
|
translate: 0 -40%;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in;
|
animation-timing-function: ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
40% {
|
40% {
|
||||||
scale: 1.1 0.9;
|
scale: 1.1 0.9;
|
||||||
translate: 0 -50%;
|
translate: 0 -50%;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in;
|
animation-timing-function: ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
45% {
|
45% {
|
||||||
scale: 0.9 1.1;
|
scale: 0.9 1.1;
|
||||||
translate: 0 -45%;
|
translate: 0 -45%;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in;
|
animation-timing-function: ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,30 +1055,35 @@ option {
|
||||||
55% {
|
55% {
|
||||||
scale: 0.985 1.025;
|
scale: 0.985 1.025;
|
||||||
translate: 0 -35%;
|
translate: 0 -35%;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in;
|
animation-timing-function: ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
60% {
|
60% {
|
||||||
scale: 1.0125 0.9985;
|
scale: 1.0125 0.9985;
|
||||||
translate: 0 -30%;
|
translate: 0 -30%;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in;
|
animation-timing-function: ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
80% {
|
80% {
|
||||||
scale: 1.0063 0.9938;
|
scale: 1.0063 0.9938;
|
||||||
translate: 0 -10%;
|
translate: 0 -10%;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in-ou;
|
animation-timing-function: ease-in-ou;
|
||||||
}
|
}
|
||||||
|
|
||||||
90% {
|
90% {
|
||||||
scale: 1.2 0.8;
|
scale: 1.2 0.8;
|
||||||
translate: 0 0;
|
translate: 0 0;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-out;
|
animation-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
scale: 1 1;
|
scale: 1 1;
|
||||||
translate: 0 0;
|
translate: 0 0;
|
||||||
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-out;
|
animation-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -327,11 +327,7 @@ const setConfig = async ({ store }) => {
|
||||||
|
|
||||||
const checkOAuthToken = async ({ store }) => {
|
const checkOAuthToken = async ({ store }) => {
|
||||||
if (store.getters.getUserToken()) {
|
if (store.getters.getUserToken()) {
|
||||||
try {
|
return store.dispatch('loginUser', store.getters.getUserToken())
|
||||||
await store.dispatch('loginUser', store.getters.getUserToken())
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
@ -349,10 +345,15 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
||||||
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
|
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
|
||||||
store.dispatch('setInstanceOption', { name: 'server', value: server })
|
store.dispatch('setInstanceOption', { name: 'server', value: server })
|
||||||
|
|
||||||
|
document.querySelector('#status').textContent = i18n.global.t('splash.settings')
|
||||||
await setConfig({ store })
|
await setConfig({ store })
|
||||||
await store.dispatch('setTheme')
|
|
||||||
|
|
||||||
document.querySelector('#status').textContent = i18n.global.t('splash.theme')
|
document.querySelector('#status').textContent = i18n.global.t('splash.theme')
|
||||||
|
try {
|
||||||
|
await store.dispatch('setTheme').catch((e) => { console.log(e) })
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
|
||||||
applyConfig(store.state.config, i18n.global)
|
applyConfig(store.state.config, i18n.global)
|
||||||
|
|
||||||
// Now we can try getting the server settings and logging in
|
// Now we can try getting the server settings and logging in
|
||||||
|
@ -363,7 +364,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
||||||
getInstancePanel({ store }),
|
getInstancePanel({ store }),
|
||||||
getNodeInfo({ store }),
|
getNodeInfo({ store }),
|
||||||
getInstanceConfig({ store })
|
getInstanceConfig({ store })
|
||||||
])
|
]).catch(e => Promise.reject(e))
|
||||||
|
|
||||||
// Start fetching things that don't need to block the UI
|
// Start fetching things that don't need to block the UI
|
||||||
store.dispatch('fetchMutes')
|
store.dispatch('fetchMutes')
|
||||||
|
|
|
@ -217,6 +217,27 @@
|
||||||
{{ $t('settings.no_rich_text_description') }}
|
{{ $t('settings.no_rich_text_description') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
path="useAbsoluteTimeFormat"
|
||||||
|
expert="1"
|
||||||
|
>
|
||||||
|
{{ $t('settings.absolute_time_format') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
<ul
|
||||||
|
class="setting-list suboptions"
|
||||||
|
v-if="mergedConfig.useAbsoluteTimeFormat"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<IntegerSetting
|
||||||
|
path="absoluteTimeFormatMinAgeDays"
|
||||||
|
:min="0"
|
||||||
|
>
|
||||||
|
{{ $t('settings.absolute_time_format_min_age_days') }}
|
||||||
|
</IntegerSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<h3>{{ $t('settings.attachments') }}</h3>
|
<h3>{{ $t('settings.attachments') }}</h3>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
:datetime="time"
|
:datetime="time"
|
||||||
:title="localeDateString"
|
:title="localeDateString"
|
||||||
>
|
>
|
||||||
{{ relativeTimeString }}
|
{{ relativeOrAbsoluteTimeString }}
|
||||||
</time>
|
</time>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -16,16 +16,28 @@ export default {
|
||||||
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'],
|
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
relativeTimeMs: 0,
|
||||||
relativeTime: { key: 'time.now', num: 0 },
|
relativeTime: { key: 'time.now', num: 0 },
|
||||||
interval: null
|
interval: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
localeDateString () {
|
shouldUseAbsoluteTimeFormat () {
|
||||||
const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale)
|
if (!this.$store.getters.mergedConfig.useAbsoluteTimeFormat) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return this.$store.getters.mergedConfig.absoluteTimeFormatMinAgeDays * DateUtils.DAY <= this.relativeTimeMs
|
||||||
|
},
|
||||||
|
browserLocale () {
|
||||||
|
return localeService.internalToBrowserLocale(this.$i18n.locale)
|
||||||
|
},
|
||||||
|
timeAsDate () {
|
||||||
return typeof this.time === 'string'
|
return typeof this.time === 'string'
|
||||||
? new Date(Date.parse(this.time)).toLocaleString(browserLocale)
|
? new Date(Date.parse(this.time))
|
||||||
: this.time.toLocaleString(browserLocale)
|
: this.time
|
||||||
|
},
|
||||||
|
localeDateString () {
|
||||||
|
return this.timeAsDate.toLocaleString(this.browserLocale)
|
||||||
},
|
},
|
||||||
relativeTimeString () {
|
relativeTimeString () {
|
||||||
const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num])
|
const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num])
|
||||||
|
@ -35,6 +47,40 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeString
|
return timeString
|
||||||
|
},
|
||||||
|
absoluteTimeString () {
|
||||||
|
if (this.longFormat) {
|
||||||
|
return this.localeDateString
|
||||||
|
}
|
||||||
|
const now = new Date()
|
||||||
|
const formatter = (() => {
|
||||||
|
if (DateUtils.isSameDay(this.timeAsDate, now)) {
|
||||||
|
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||||
|
minute: 'numeric',
|
||||||
|
hour: 'numeric'
|
||||||
|
})
|
||||||
|
} else if (DateUtils.isSameMonth(this.timeAsDate, now)) {
|
||||||
|
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||||
|
hour: 'numeric',
|
||||||
|
day: 'numeric'
|
||||||
|
})
|
||||||
|
} else if (DateUtils.isSameYear(this.timeAsDate, now)) {
|
||||||
|
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
return formatter.format(this.timeAsDate)
|
||||||
|
},
|
||||||
|
relativeOrAbsoluteTimeString () {
|
||||||
|
return this.shouldUseAbsoluteTimeFormat ? this.absoluteTimeString : this.relativeTimeString
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -54,6 +100,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
refreshRelativeTimeObject () {
|
refreshRelativeTimeObject () {
|
||||||
const nowThreshold = typeof this.nowThreshold === 'number' ? this.nowThreshold : 1
|
const nowThreshold = typeof this.nowThreshold === 'number' ? this.nowThreshold : 1
|
||||||
|
this.relativeTimeMs = DateUtils.relativeTimeMs(this.time)
|
||||||
this.relativeTime = this.longFormat
|
this.relativeTime = this.longFormat
|
||||||
? DateUtils.relativeTime(this.time, nowThreshold)
|
? DateUtils.relativeTime(this.time, nowThreshold)
|
||||||
: DateUtils.relativeTimeShort(this.time, nowThreshold)
|
: DateUtils.relativeTimeShort(this.time, nowThreshold)
|
||||||
|
|
|
@ -506,6 +506,8 @@
|
||||||
"autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available",
|
"autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available",
|
||||||
"emoji_reactions_on_timeline": "Show emoji reactions on timeline",
|
"emoji_reactions_on_timeline": "Show emoji reactions on timeline",
|
||||||
"emoji_reactions_scale": "Reactions scale factor",
|
"emoji_reactions_scale": "Reactions scale factor",
|
||||||
|
"absolute_time_format": "Use absolute time format",
|
||||||
|
"absolute_time_format_min_age_days": "Only use for time more than this number of days away from now",
|
||||||
"export_theme": "Save preset",
|
"export_theme": "Save preset",
|
||||||
"filtering": "Filtering",
|
"filtering": "Filtering",
|
||||||
"wordfilter": "Wordfilter",
|
"wordfilter": "Wordfilter",
|
||||||
|
@ -870,9 +872,6 @@
|
||||||
"component": "Component",
|
"component": "Component",
|
||||||
"override": "Override",
|
"override": "Override",
|
||||||
"shadow_id": "Shadow #{value}",
|
"shadow_id": "Shadow #{value}",
|
||||||
"offset": "Shadow offset",
|
|
||||||
"light_grid": "Use light checkerboard",
|
|
||||||
"name": "Name",
|
|
||||||
"blur": "Blur",
|
"blur": "Blur",
|
||||||
"spread": "Spread",
|
"spread": "Spread",
|
||||||
"inset": "Inset",
|
"inset": "Inset",
|
||||||
|
@ -880,7 +879,6 @@
|
||||||
"filter_hint": {
|
"filter_hint": {
|
||||||
"always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.",
|
"always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.",
|
||||||
"drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.",
|
"drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.",
|
||||||
"avatar_inset_short": "Separate inset shadow",
|
|
||||||
"avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.",
|
"avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.",
|
||||||
"spread_zero": "Shadows with spread > 0 will appear as if it was set to zero",
|
"spread_zero": "Shadows with spread > 0 will appear as if it was set to zero",
|
||||||
"inset_classic": "Inset shadows will be using {0}"
|
"inset_classic": "Inset shadows will be using {0}"
|
||||||
|
@ -1410,11 +1408,12 @@
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"theme": "Applying theme, please wait warmly...",
|
"theme": "Applying theme, please wait warmly...",
|
||||||
"instance": "Getting instance info...",
|
"instance": "Getting instance info...",
|
||||||
"splines": "Reticulating splines...",
|
"settings": "Applying settings...",
|
||||||
"almost": "Almost there!",
|
"almost": "Reticulating splines...",
|
||||||
"fun_1": "Drink more water!",
|
"fun_1": "Drink more water",
|
||||||
"fun_2": "Take it easy!",
|
"fun_2": "Take it easy!",
|
||||||
"fun_3": "Suya..",
|
"fun_3": "Suya...",
|
||||||
"fun_4": "#cofe"
|
"fun_4": "My Pleroma machine is full power!",
|
||||||
|
"error": "Something went wrong"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/main.js
23
src/main.js
|
@ -48,6 +48,16 @@ const i18n = createI18n({
|
||||||
|
|
||||||
messages.setLanguage(i18n.global, currentLocale)
|
messages.setLanguage(i18n.global, currentLocale)
|
||||||
|
|
||||||
|
const splashError = (i18n, e) => {
|
||||||
|
document.querySelector('#mascot').src = (Math.floor(Math.random() * 2) > 0)
|
||||||
|
? '/static/pleromatan_orz_fox.png'
|
||||||
|
: '/static/pleromatan_orz.png'
|
||||||
|
document.querySelector('#mascot').classList.add('orz')
|
||||||
|
document.querySelector('#throbber').classList.add('dead')
|
||||||
|
document.querySelector('#status').textContent = i18n.global.t('splash.error')
|
||||||
|
console.error('PleromaFE failed to initialize: ', e)
|
||||||
|
}
|
||||||
|
|
||||||
const persistedStateOptions = {
|
const persistedStateOptions = {
|
||||||
paths: [
|
paths: [
|
||||||
'serverSideStorage.cache',
|
'serverSideStorage.cache',
|
||||||
|
@ -58,17 +68,19 @@ const persistedStateOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
let storageError = false
|
try {
|
||||||
|
let storageError
|
||||||
const plugins = [pushNotifications]
|
const plugins = [pushNotifications]
|
||||||
try {
|
try {
|
||||||
const persistedState = await createPersistedState(persistedStateOptions)
|
const persistedState = await createPersistedState(persistedStateOptions)
|
||||||
plugins.push(persistedState)
|
plugins.push(persistedState)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error('Storage error', e)
|
||||||
storageError = true
|
storageError = e
|
||||||
}
|
}
|
||||||
document.querySelector('#status').removeAttribute('class')
|
document.querySelector('#status').removeAttribute('class')
|
||||||
document.querySelector('#status').textContent = i18n.global.t('splash.loading')
|
document.querySelector('#status').textContent = i18n.global.t('splash.loading')
|
||||||
|
document.querySelector('#splash-credit').textContent = i18n.global.t('update.art_by', { linkToArtist: 'pipivovott' })
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
modules: {
|
modules: {
|
||||||
i18n: {
|
i18n: {
|
||||||
|
@ -108,7 +120,10 @@ const persistedStateOptions = {
|
||||||
if (storageError) {
|
if (storageError) {
|
||||||
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
|
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
|
||||||
}
|
}
|
||||||
afterStoreSetup({ store, i18n })
|
return await afterStoreSetup({ store, i18n })
|
||||||
|
} catch (e) {
|
||||||
|
splashError(i18n, e)
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
// These are inlined by webpack's DefinePlugin
|
// These are inlined by webpack's DefinePlugin
|
||||||
|
|
|
@ -180,7 +180,9 @@ export const defaultState = {
|
||||||
autocompleteSelect: undefined, // instance default
|
autocompleteSelect: undefined, // instance default
|
||||||
closingDrawerMarksAsSeen: undefined, // instance default
|
closingDrawerMarksAsSeen: undefined, // instance default
|
||||||
unseenAtTop: undefined, // instance default
|
unseenAtTop: undefined, // instance default
|
||||||
ignoreInactionableSeen: undefined // instance default
|
ignoreInactionableSeen: undefined, // instance default
|
||||||
|
useAbsoluteTimeFormat: undefined, // instance defualt
|
||||||
|
absoluteTimeFormatMinAgeDays: undefined // instance default
|
||||||
}
|
}
|
||||||
|
|
||||||
// caching the instance default properties
|
// caching the instance default properties
|
||||||
|
|
|
@ -119,6 +119,8 @@ const defaultState = {
|
||||||
closingDrawerMarksAsSeen: true,
|
closingDrawerMarksAsSeen: true,
|
||||||
unseenAtTop: false,
|
unseenAtTop: false,
|
||||||
ignoreInactionableSeen: false,
|
ignoreInactionableSeen: false,
|
||||||
|
useAbsoluteTimeFormat: false,
|
||||||
|
absoluteTimeFormatMinAgeDays: 0,
|
||||||
|
|
||||||
// Nasty stuff
|
// Nasty stuff
|
||||||
customEmoji: [],
|
customEmoji: [],
|
||||||
|
|
|
@ -230,27 +230,27 @@ const interfaceMod = {
|
||||||
|
|
||||||
const forceRecompile = forceThemeRecompilation || recompile
|
const forceRecompile = forceThemeRecompilation || recompile
|
||||||
|
|
||||||
let promise = null
|
let result = null
|
||||||
|
|
||||||
if (themeData) {
|
if (themeData) {
|
||||||
promise = Promise.resolve(normalizeThemeData(themeData))
|
result = normalizeThemeData(themeData)
|
||||||
} else if (themeName) {
|
} else if (themeName) {
|
||||||
promise = getPreset(themeName).then(themeData => normalizeThemeData(themeData))
|
result = normalizeThemeData(getPreset(themeName))
|
||||||
|
.then(themeData => normalizeThemeData(themeData))
|
||||||
} else if (userThemeSource || userThemeSnapshot) {
|
} else if (userThemeSource || userThemeSnapshot) {
|
||||||
promise = Promise.resolve(normalizeThemeData({
|
result = normalizeThemeData({
|
||||||
_pleroma_theme_version: 2,
|
_pleroma_theme_version: 2,
|
||||||
theme: userThemeSnapshot,
|
theme: userThemeSnapshot,
|
||||||
source: userThemeSource
|
source: userThemeSource
|
||||||
}))
|
})
|
||||||
} else if (actualThemeName && actualThemeName !== 'custom') {
|
} else if (actualThemeName && actualThemeName !== 'custom') {
|
||||||
promise = getPreset(actualThemeName).then(themeData => {
|
const themeData = actualThemeName
|
||||||
const realThemeData = normalizeThemeData(themeData)
|
const realThemeData = normalizeThemeData(themeData)
|
||||||
if (actualThemeName === instanceThemeName) {
|
if (actualThemeName === instanceThemeName) {
|
||||||
// This sole line is the reason why this whole block is above the recompilation check
|
// This sole line is the reason why this whole block is above the recompilation check
|
||||||
commit('setInstanceOption', { name: 'themeData', value: { theme: realThemeData } })
|
commit('setInstanceOption', { name: 'themeData', value: { theme: realThemeData } })
|
||||||
}
|
}
|
||||||
return realThemeData
|
result = realThemeData
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Cannot load any theme!')
|
throw new Error('Cannot load any theme!')
|
||||||
}
|
}
|
||||||
|
@ -259,11 +259,10 @@ const interfaceMod = {
|
||||||
// cache (tryLoadCache return true if load successful)
|
// cache (tryLoadCache return true if load successful)
|
||||||
if (!forceRecompile && !themeDebug && tryLoadCache()) {
|
if (!forceRecompile && !themeDebug && tryLoadCache()) {
|
||||||
dispatch('setThemeApplied')
|
dispatch('setThemeApplied')
|
||||||
return
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
promise
|
const realThemeData = result
|
||||||
.then(realThemeData => {
|
|
||||||
const theme2ruleset = convertTheme2To3(realThemeData)
|
const theme2ruleset = convertTheme2To3(realThemeData)
|
||||||
|
|
||||||
if (saveData) {
|
if (saveData) {
|
||||||
|
@ -345,9 +344,6 @@ const interfaceMod = {
|
||||||
() => dispatch('setThemeApplied'),
|
() => dispatch('setThemeApplied'),
|
||||||
themeDebug
|
themeDebug
|
||||||
)
|
)
|
||||||
})
|
|
||||||
|
|
||||||
return promise
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,13 @@ export const WEEK = 7 * DAY
|
||||||
export const MONTH = 30 * DAY
|
export const MONTH = 30 * DAY
|
||||||
export const YEAR = 365.25 * DAY
|
export const YEAR = 365.25 * DAY
|
||||||
|
|
||||||
export const relativeTime = (date, nowThreshold = 1) => {
|
export const relativeTimeMs = (date) => {
|
||||||
if (typeof date === 'string') date = Date.parse(date)
|
if (typeof date === 'string') date = Date.parse(date)
|
||||||
|
return Math.abs(Date.now() - date)
|
||||||
|
}
|
||||||
|
export const relativeTime = (date, nowThreshold = 1) => {
|
||||||
const round = Date.now() > date ? Math.floor : Math.ceil
|
const round = Date.now() > date ? Math.floor : Math.ceil
|
||||||
const d = Math.abs(Date.now() - date)
|
const d = relativeTimeMs(date)
|
||||||
const r = { num: round(d / YEAR), key: 'time.unit.years' }
|
const r = { num: round(d / YEAR), key: 'time.unit.years' }
|
||||||
if (d < nowThreshold * SECOND) {
|
if (d < nowThreshold * SECOND) {
|
||||||
r.num = 0
|
r.num = 0
|
||||||
|
@ -57,3 +60,18 @@ export const secondsToUnit = (unit, amount) => {
|
||||||
case 'days': return (1000 * amount) / DAY
|
case 'days': return (1000 * amount) / DAY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isSameYear = (a, b) => {
|
||||||
|
return a.getFullYear() === b.getFullYear()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isSameMonth = (a, b) => {
|
||||||
|
return a.getFullYear() === b.getFullYear() &&
|
||||||
|
a.getMonth() === b.getMonth()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isSameDay = (a, b) => {
|
||||||
|
return a.getFullYear() === b.getFullYear() &&
|
||||||
|
a.getMonth() === b.getMonth() &&
|
||||||
|
a.getDate() === b.getDate()
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ const internalToBackendLocaleMulti = codes => {
|
||||||
|
|
||||||
const getLanguageName = (code) => {
|
const getLanguageName = (code) => {
|
||||||
const specialLanguageNames = {
|
const specialLanguageNames = {
|
||||||
pdc: 'Pennsylvania Dutch',
|
pdc: 'Pennsilfaanisch-Deitsch',
|
||||||
ja_easy: 'やさしいにほんご',
|
ja_easy: 'やさしいにほんご',
|
||||||
'nan-TW': '臺語(閩南語)',
|
'nan-TW': '臺語(閩南語)',
|
||||||
zh: '简体中文',
|
zh: '简体中文',
|
||||||
|
|
|
@ -43,16 +43,16 @@ const adoptStyleSheets = (styles) => {
|
||||||
// is nothing to do here.
|
// is nothing to do here.
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateTheme = async (inputRuleset, callbacks, debug) => {
|
export const generateTheme = (inputRuleset, callbacks, debug) => {
|
||||||
const {
|
const {
|
||||||
onNewRule = (rule, isLazy) => {},
|
onNewRule = (rule, isLazy) => {},
|
||||||
onLazyFinished = () => {},
|
onLazyFinished = () => {},
|
||||||
onEagerFinished = () => {}
|
onEagerFinished = () => {}
|
||||||
} = callbacks
|
} = callbacks
|
||||||
|
|
||||||
// Assuming that "worst case scenario background" is panel background since it's the most likely one
|
|
||||||
const themes3 = init({
|
const themes3 = init({
|
||||||
inputRuleset,
|
inputRuleset,
|
||||||
|
// Assuming that "worst case scenario background" is panel background since it's the most likely one
|
||||||
ultimateBackgroundColor: inputRuleset[0].directives['--bg'].split('|')[1].trim(),
|
ultimateBackgroundColor: inputRuleset[0].directives['--bg'].split('|')[1].trim(),
|
||||||
debug
|
debug
|
||||||
})
|
})
|
||||||
|
@ -146,11 +146,11 @@ export const tryLoadCache = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
|
export const applyTheme = (input, onFinish = (data) => {}, debug) => {
|
||||||
const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
|
const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
|
||||||
const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
|
const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
|
||||||
|
|
||||||
const { lazyProcessFunc } = await generateTheme(
|
const { lazyProcessFunc } = generateTheme(
|
||||||
input,
|
input,
|
||||||
{
|
{
|
||||||
onNewRule (rule, isLazy) {
|
onNewRule (rule, isLazy) {
|
||||||
|
@ -185,8 +185,6 @@ export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
setTimeout(lazyProcessFunc, 0)
|
setTimeout(lazyProcessFunc, 0)
|
||||||
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractStyleConfig = ({
|
const extractStyleConfig = ({
|
||||||
|
|
BIN
static/pleromatan_orz.png
Normal file
BIN
static/pleromatan_orz.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
BIN
static/pleromatan_orz_fox.png
Normal file
BIN
static/pleromatan_orz_fox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
Loading…
Add table
Reference in a new issue