* Fix some mobile layout issues

* More hacks to fix scrolling a bit
This commit is contained in:
styppo 2023-01-01 20:53:16 +00:00
parent 342764c458
commit bef9fad696
No known key found for this signature in database
GPG Key ID: 3AAA685C50724C28
7 changed files with 100 additions and 73 deletions

View File

@ -44,10 +44,12 @@
</div> </div>
</div> </div>
<ProfilePopup v-if="$store.getters.isSignedIn" /> <div style="position: sticky; bottom: 0">
<div v-else class="sign-in" @click="signIn"> <ProfilePopup v-if="$store.getters.isSignedIn" />
<q-icon class="icon" name="login" size="sm" /> <div v-else class="sign-in" @click="signIn">
<div class="label">Log in</div> <q-icon class="icon" name="login" size="sm" />
<div class="label">Log in</div>
</div>
</div> </div>
<div <div
@ -116,11 +118,11 @@ menu {
margin: 0; margin: 0;
padding-inline-start: 0; padding-inline-start: 0;
.menu { .menu {
height: 100%;
&-nav { &-nav {
position: relative; position: relative;
padding: 0 1rem; padding: 0 1rem;
} }
height: 100%;
&-logo { &-logo {
margin: 1rem 0; margin: 1rem 0;
svg, img { svg, img {

View File

@ -14,9 +14,9 @@
<div class="addon"> <div class="addon">
<slot /> <slot />
</div> </div>
<div class="logo"> <router-link class="logo" to="/">
<Logo /> <Logo />
</div> </router-link>
</div> </div>
</template> </template>
@ -107,6 +107,7 @@ export default defineComponent({
@media screen and (max-width: $phone) { @media screen and (max-width: $phone) {
.page-header { .page-header {
padding: .4rem 1rem;
.logo { .logo {
display: block; display: block;
position: absolute; position: absolute;

View File

@ -127,8 +127,8 @@ export default {
}, },
}, },
methods: { methods: {
formatPostDate(ts) { formatPostDate(timestamp) {
const date = new Date(ts) const date = new Date(timestamp)
const month = this.$t(MONTHS[date.getMonth()]) const month = this.$t(MONTHS[date.getMonth()])
const sameYear = date.getFullYear() === (new Date().getFullYear()) const sameYear = date.getFullYear() === (new Date().getFullYear())
@ -136,8 +136,8 @@ export default {
return `${date.getDate()} ${month}${year}` return `${date.getDate()} ${month}${year}`
}, },
formatPostTime(ts) { formatPostTime(timestamp) {
const date = new Date(ts) const date = new Date(timestamp)
return `${date.getHours()}:${date.getMinutes()}` return `${date.getHours()}:${date.getMinutes()}`
} }
} }

View File

@ -18,16 +18,14 @@
<p> <p>
<BaseUserName :pubkey="post.author" @click.stop /> <BaseUserName :pubkey="post.author" @click.stop />
<span>&#183;</span> <span>&#183;</span>
<span class="created-at">{{ moment(post.createdAt).fromNow() }}</span> <span class="created-at">{{ formatPostDate(post.createdAt) }}</span>
</p> </p>
<p v-if="ancestor" class="in-reply-to"> <p v-if="ancestor" class="in-reply-to">
Replying to <a @click.stop="toEvent(ancestor)">{{ shorten(ancestor) }}</a> Replying to <a @click.stop="toEvent(ancestor)">{{ shorten(ancestor) }}</a>
</p> </p>
</div> </div>
<div class="post-content-body"> <div class="post-content-body">
<p> <BaseMarkdown :content="post.content" />
<BaseMarkdown :content="post.content" />
</p>
</div> </div>
<div v-if="actions" class="post-content-actions"> <div v-if="actions" class="post-content-actions">
<div class="action-item comment" @click.stop="$store.dispatch('createPost', {replyTo})"> <div class="action-item comment" @click.stop="$store.dispatch('createPost', {replyTo})">
@ -135,7 +133,23 @@ export default {
}, },
}, },
methods: { methods: {
moment, formatPostDate(timestamp) {
return this.$q.screen.lt.md
? this.shortDateFromNow(timestamp)
: moment(timestamp).fromNow()
},
shortDateFromNow(timestamp) {
const now = Date.now()
const diff = Math.round(Math.max(now - timestamp, 0) / 1000)
const formatDiff = (div, offset) => Math.max(Math.floor((diff + offset) / div), 1)
if (diff < 45) return `${formatDiff(1, 0)}s`
if (diff < 60 * 45) return `${formatDiff(60, 15)}m`
if (diff < 60 * 60 * 22) return `${formatDiff(60 * 60, 60 * 15)}h`
if (diff < 60 * 60 * 24 * 26) return `${formatDiff(60 * 60 * 24, 60 * 60 * 2)}d`
if (diff < 60 * 60 * 24 * 30 * 320) return `${formatDiff(60 * 60 * 24 * 30, 60 * 60 * 24 * 4)}mo`
return `${formatDiff(60 * 60 * 24 * 30 * 365, 60 * 60 * 24 * 45)}y`
}
}, },
} }
</script> </script>
@ -175,10 +189,13 @@ export default {
} }
&-content { &-content {
margin-left: 12px; margin-left: 12px;
padding: 1rem 0; padding: 1rem 0 .4rem;
flex-grow: 1; flex-grow: 1;
max-width: 570px; max-width: 570px;
&-header { &-header {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.in-reply-to { .in-reply-to {
color: $color-dark-gray; color: $color-dark-gray;
a { a {
@ -212,25 +229,7 @@ export default {
} }
&-body { &-body {
color: #fff; color: #fff;
&-images { margin-bottom: 1rem;
&-wrapper {
border-radius: 10px;
overflow: hidden;
border: $border-light;
display: flex;
.post-content-image-item {
cursor: zoom-in;
& + .post-content-image-item {
border-left: $border-light;
}
flex-grow: 1;
img {
vertical-align: middle;
width: 100%;
}
}
}
}
} }
&-actions { &-actions {
display: flex; display: flex;
@ -287,18 +286,11 @@ export default {
} }
} }
} }
@media screen and (max-width: $phone) { @media screen and (max-width: $phone) {
.post{ .post {
&-content { &-content {
&-header { &-header {
span{
display: none;
}
.created-at {
display: block;
color: rgba($color: $color-dark-gray, $alpha: 0.5);
margin: 5px 0;
}
.nip05 { .nip05 {
display: unset; display: unset;
color: $color-dark-gray; color: $color-dark-gray;

View File

@ -10,7 +10,7 @@
<div class="layout-flow"> <div class="layout-flow">
<q-page-container ref="pageContainer"> <q-page-container ref="pageContainer">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<keep-alive :include="['Feed', 'Messages', 'Notifications']"> <keep-alive :include="cachedPages">
<component :is="Component" :key="$route.path" @scroll-to-rect="scrollToRect" /> <component :is="Component" :key="$route.path" @scroll-to-rect="scrollToRect" />
</keep-alive> </keep-alive>
</router-view> </router-view>
@ -31,6 +31,7 @@
> >
<BaseIcon icon="hamburger" /> <BaseIcon icon="hamburger" />
</div> </div>
<div v-if="mobileMenuOpen" class="mobile-menu-backdrop fixed-full" v-close-popup></div>
</div> </div>
<SignInDialog /> <SignInDialog />
@ -66,14 +67,19 @@ export default defineComponent({
setup () { setup () {
const $q = useQuasar() const $q = useQuasar()
// const cachedPages = ref(['feed', 'notifications', 'messages']) $q.screen.setSizes({
// FIXME Needs to be in sync with assets/variables.scss
sm: 414,
md: 755,
lg: 1113,
xl: 1310,
})
return $q return $q
}, },
data() { data() {
return { return {
cachedPages: ['Feed', 'Notifications', 'Messages'], cachedPages: ['Feed', 'Notifications', 'Messages', 'Inbox', 'Settings', 'DevTools'],
middlePagePos: {}, middlePagePos: {},
broadcastChannel: new BroadcastChannel('hamstr'), broadcastChannel: new BroadcastChannel('hamstr'),
activeWindow: false, activeWindow: false,
@ -118,8 +124,8 @@ export default defineComponent({
// setup scrolling // setup scrolling
// document.querySelector('#left-drawer').addEventListener('wheel', this.redirectScroll) // document.querySelector('#left-drawer').addEventListener('wheel', this.redirectScroll)
this.$router.beforeEach((to, from) => { this.preserveScrollPos(to, from) }) // this.$router.beforeEach((to, from) => { this.preserveScrollPos(to, from) })
this.$router.afterEach((to, from) => { this.restoreScrollPos(to, from) }) // this.$router.afterEach((to, from) => { this.restoreScrollPos(to, from) })
// destroy streams before unloading window // destroy streams before unloading window
window.onbeforeunload = async () => { window.onbeforeunload = async () => {
@ -147,7 +153,7 @@ export default defineComponent({
}, },
scrollToRect(rect) { scrollToRect(rect) {
let offset = Math.max(rect.top, 0) - 78 let offset = Math.max(rect.top, 0)
setVerticalScrollPosition(this.scrollingContainer, offset, 0) setVerticalScrollPosition(this.scrollingContainer, offset, 0)
}, },
@ -355,6 +361,12 @@ export default defineComponent({
fill: #fff; fill: #fff;
} }
} }
.mobile-menu-backdrop {
z-index: 750;
pointer-events: all;
outline: 0;
background: rgba(0, 0, 0, 0.4);
}
} }
} }
</style> </style>

View File

@ -1,24 +1,28 @@
<template> <template>
<q-page> <q-page>
<PageHeader> <div class="page-header-container">
<div class="addon-menu"> <PageHeader>
<div class="addon-menu-icon"> <div class="addon-menu">
<q-icon name="more_vert" size="sm" /> <div class="addon-menu-icon">
</div> <q-icon name="more_vert" size="sm" />
<q-menu target=".addon-menu-icon" anchor="top left" self="top right" class="addon-menu-popup"> </div>
<div> <q-menu target=".addon-menu-icon" anchor="top left" self="top right" class="addon-menu-popup">
<div v-for="tabName in availableTabs" :key="tabName" class="popup-header" @click="tab = tabName" v-close-popup> <div>
<p>{{ tabName }}</p> <div v-for="tabName in availableTabs" :key="tabName" class="popup-header" @click="tab = tabName" v-close-popup>
<div class="more" v-if="tab === tabName"> <p>{{ tabName }}</p>
<BaseIcon icon="tick" /> <div class="more" v-if="tab === tabName">
<BaseIcon icon="tick" />
</div>
</div> </div>
</div> </div>
</div> </q-menu>
</q-menu> </div>
</div> </PageHeader>
</PageHeader> </div>
<PostEditor class="post-editor" v-if="$store.getters.isSignedIn" /> <div class="post-editor" v-if="$store.getters.isSignedIn">
<PostEditor />
</div>
<div class="feed-tabs"> <div class="feed-tabs">
<q-tabs <q-tabs
@ -42,7 +46,7 @@
</div> </div>
<div class="feed"> <div class="feed">
<div class="load-more-container"> <div class="load-more-container" :class="{'more-available': unreadFeed[tab].length}">
<BaseButtonLoadMore <BaseButtonLoadMore
v-if="unreadFeed[tab].length" v-if="unreadFeed[tab].length"
:loading="loadingUnread" :loading="loadingUnread"
@ -342,9 +346,21 @@ export default defineComponent({
} }
@media screen and (max-width: $phone) { @media screen and (max-width: $phone) {
.page-header-container {
border-bottom: $border-dark;
}
.post-editor { .post-editor {
display: none; display: none;
} }
.feed {
.load-more-container {
border: 0;
min-height: 0;
&.more-available {
border-bottom: $border-dark;
}
}
}
} }
</style> </style>

View File

@ -23,8 +23,8 @@
:connector="ancestorsCompiled.length > 0" :connector="ancestorsCompiled.length > 0"
@add-event="processChildEvent" @add-event="processChildEvent"
/> />
<div v-else> <div v-else style="padding-left: 1.5rem">
{{ $t('event') }} {{ $route.params.eventId }} <q-spinner size="sm" style="margin-right: .5rem"/> Loading...
</div> </div>
</q-item> </q-item>
@ -106,6 +106,8 @@ export default defineComponent({
this.scrollToMainEvent() this.scrollToMainEvent()
}) })
this.resizeObserver.observe(this.$refs.page.$el) this.resizeObserver.observe(this.$refs.page.$el)
setTimeout(() => this.resizeObserver.disconnect(), 2000)
}, },
beforeUnmount() { beforeUnmount() {
@ -200,12 +202,14 @@ export default defineComponent({
const el = this.$refs.main?.$el const el = this.$refs.main?.$el
if (!el) return if (!el) return
const offset = Math.max(el.offsetTop - 78, 0) // TODO Clean up
const offset = this.$q.screen.xs ? 61 : 78
const position = Math.max(el.offsetTop - offset, 0)
if (this.scrollTimeout) { if (this.scrollTimeout) {
clearTimeout(this.scrollTimeout) clearTimeout(this.scrollTimeout)
} }
this.scrollTimeout = setTimeout(() => window.scrollTo(0, offset), 100) this.scrollTimeout = setTimeout(() => window.scrollTo(0, position), 100)
}, },
addEventAncestors(event) { addEventAncestors(event) {