dev #27
@@ -64,6 +64,19 @@ class XenForoGuard implements Guard
|
||||
if ($this->hasUser())
|
||||
return $this->user;
|
||||
|
||||
$user = $this->getFromSession();
|
||||
if( $user )
|
||||
return $user;
|
||||
|
||||
$user = $this->getFromCookie();
|
||||
if( $user )
|
||||
return $user;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getFromSession(): ?XenForoUser
|
||||
{
|
||||
$sessionId = $this->request->cookie('xf_session');
|
||||
if(!$sessionId)
|
||||
return null;
|
||||
@@ -92,6 +105,64 @@ class XenForoGuard implements Guard
|
||||
return $this->user = new XenForoUser($xfUser);
|
||||
}
|
||||
|
||||
private function isCorrectCookieKey(string $key, $record): bool
|
||||
{
|
||||
$known = $record->remember_key;
|
||||
if( !$known )
|
||||
return false;
|
||||
|
||||
$check = hash('sha256', $key, true);
|
||||
return hash_equals($known, $check);
|
||||
}
|
||||
|
||||
private function getFromCookie(): ?XenForoUser
|
||||
{
|
||||
$cookie = $this->request->cookie('xf_user');
|
||||
if(!$cookie)
|
||||
return null;
|
||||
|
||||
$parts = explode(',', $cookie);
|
||||
if( count( $parts ) !== 2 )
|
||||
return null;
|
||||
|
||||
[$userId, $key] = $parts;
|
||||
$userId = (int) $userId;
|
||||
|
||||
if( !$userId || !$key )
|
||||
return null;
|
||||
|
||||
$remembers = \DB::connection('xenforo')
|
||||
->table('user_remember')
|
||||
->where('user_id', $userId)
|
||||
->get();
|
||||
|
||||
if( !$remembers )
|
||||
return null;
|
||||
|
||||
$valid = false;
|
||||
|
||||
foreach( $remembers as $remember )
|
||||
{
|
||||
if( $this->isCorrectCookieKey($key, $remember) && $remember->expiry_date >= time() ){
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !$valid )
|
||||
return null;
|
||||
|
||||
$xfUser = \DB::connection('xenforo')
|
||||
->table('user')
|
||||
->where('user_id', $userId)
|
||||
->first();
|
||||
|
||||
if(!$xfUser)
|
||||
return null;
|
||||
|
||||
return $this->user = new XenForoUser($xfUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unused.
|
||||
*
|
||||
|
||||
@@ -125,4 +125,18 @@ class EntryHelpers {
|
||||
session(["downloaded_file_{$entryFile->file_uuid}" => 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function stripBbCode(?string $text): ?string
|
||||
{
|
||||
if ($text === null) return null;
|
||||
$text = preg_replace('/\[quote\b[^\]]*\](.*?)\[\/quote\]/is', '', $text);
|
||||
return preg_replace('/\[\/?\w+[^\]]*\]/i', '', $text);
|
||||
}
|
||||
|
||||
public static function stripMarkdown(?string $text): ?string
|
||||
{
|
||||
if ($text === null) return null;
|
||||
$html = Str::markdown($text);
|
||||
return html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Helpers\EntryHelpers;
|
||||
use App\Models\Entry;
|
||||
use App\Models\EntryReview;
|
||||
use App\Models\News;
|
||||
@@ -65,7 +66,7 @@ class ActivityService
|
||||
'user_id' => $entry->user_id,
|
||||
'badge' => EntryCard::ENTRY_TYPES_BADGE[$entry->type],
|
||||
'badge_class' => $entry->type,
|
||||
'excerpt' => $entry->description ? \Str::limit(strip_tags($entry->description), 80) : null,
|
||||
'excerpt' => $entry->description ? \Str::limit(EntryHelpers::stripMarkdown(strip_tags($entry->description)), 80) : null,
|
||||
'meta' => $entry->getRealPlatform()?->name
|
||||
];
|
||||
}
|
||||
@@ -82,7 +83,7 @@ class ActivityService
|
||||
'user_id' => $news->user_id,
|
||||
'badge' => 'News',
|
||||
'badge_class' => 'news',
|
||||
'excerpt' => $news->description ? \Str::limit(strip_tags($news->description), 80) : null,
|
||||
'excerpt' => $news->description ? \Str::limit(EntryHelpers::stripMarkdown(strip_tags($news->description)), 80) : null,
|
||||
'meta' => $news->category?->name
|
||||
];
|
||||
}
|
||||
@@ -99,7 +100,7 @@ class ActivityService
|
||||
'user_id' => $message->user_id,
|
||||
'badge' => 'Post',
|
||||
'badge_class' => 'message',
|
||||
'excerpt' => $message->message ? \Str::limit(strip_tags($message->message), 80) : null,
|
||||
'excerpt' => $message->message ? \Str::limit(EntryHelpers::stripBbCode(strip_tags($message->message)), 80) : null,
|
||||
'meta' => null
|
||||
];
|
||||
|
||||
@@ -117,7 +118,7 @@ class ActivityService
|
||||
'user_id' => $thread->user_id,
|
||||
'badge' => 'Thread',
|
||||
'badge_class' => 'thread',
|
||||
'excerpt' => $thread->message ? \Str::limit(strip_tags($thread->message), 80) : null,
|
||||
'excerpt' => $thread->message ? \Str::limit(EntryHelpers::stripBbCode(strip_tags($thread->message)), 80) : null,
|
||||
'meta' => null
|
||||
];
|
||||
|
||||
@@ -152,7 +153,7 @@ class ActivityService
|
||||
'user_id' => $review->user_id,
|
||||
'badge' => 'Review',
|
||||
'badge_class' => 'review',
|
||||
'excerpt' => $review->description ? \Str::limit(strip_tags($review->description), 80) : null,
|
||||
'excerpt' => $review->description ? \Str::limit(EntryHelpers::stripMarkdown(strip_tags($review->description)), 80) : null,
|
||||
'meta' => $review->entry()->exists() ? ( $review->entry->complete_title ?? $review->entry->title ) : null,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
flex-direction: column;
|
||||
transition: transform 0.2s, border-color 0.2s;
|
||||
cursor: pointer;
|
||||
@@ -72,6 +73,7 @@
|
||||
.entry-card-info {
|
||||
padding: 15px;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -82,13 +84,16 @@
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 5px;
|
||||
line-height: 1.3;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.entry-card-author {
|
||||
color: var(--rhpz-orange);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 10px;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.entry-card-meta {
|
||||
@@ -148,6 +153,9 @@
|
||||
|
||||
.entry-card-meta {
|
||||
font-size: 0.75rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,45 @@
|
||||
background-color: rgba(129, 199, 132, 0.1);
|
||||
}
|
||||
|
||||
.back-to-top {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.back-to-top {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
position: fixed;
|
||||
right: 14px;
|
||||
bottom: 14px;
|
||||
z-index: 1000;
|
||||
padding: 10px 14px;
|
||||
border-radius: 999px;
|
||||
background-color: var(--bg2);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(10px);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.back-to-top.visible {
|
||||
display: inline-flex;
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.back-to-top span {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
/* BLOCK */
|
||||
|
||||
.block {
|
||||
|
||||
@@ -227,8 +227,10 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
|
||||
.btn {
|
||||
min-width: 36px;
|
||||
@@ -255,18 +257,20 @@
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.database-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.database-wrapper {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.database-filters {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2,1fr);
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.database-results {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.database-filter-group:last-child {
|
||||
@@ -286,13 +290,32 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-bar .filter-bar-search {
|
||||
max-width: none;
|
||||
flex: initial;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-bar .btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.database-wrapper {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.database-filters {
|
||||
width: 100%;
|
||||
width: min(100%, 420px);
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
grid-template-columns: 1fr;
|
||||
order: -1;
|
||||
margin-bottom: 10px;
|
||||
@@ -303,7 +326,7 @@
|
||||
}
|
||||
|
||||
.grid-entries {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
@@ -311,6 +334,20 @@
|
||||
@media (max-width: 600px) {
|
||||
.database-search {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.filter-bar .filter-bar-search {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.filter-bar .btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.database-filters {
|
||||
@@ -318,7 +355,7 @@
|
||||
}
|
||||
|
||||
.grid-entries {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -393,6 +393,37 @@
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gallery-mobile-controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.gallery-mobile-controls {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.gallery-move-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--bg2);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gallery-move-btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.authors-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
@@ -510,13 +541,30 @@
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.upload-item-actions {
|
||||
.upload-item {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.upload-item-info {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.upload-item-actions {
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.upload-item-actions .btn {
|
||||
width: 100%;
|
||||
width: auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
.file-state-icon { width: 18px; height: 18px; }
|
||||
@@ -673,10 +721,18 @@
|
||||
.grid-hashes {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.grid-credits {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.grid-first {
|
||||
display: none;
|
||||
}
|
||||
.hash-first {
|
||||
display: none;
|
||||
}
|
||||
.author-search-selected {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@@ -706,4 +762,20 @@
|
||||
.form-error-text {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.form-type-of-checkboxes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
|
||||
label {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.5);
|
||||
margin-right: 1.33rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,38 @@
|
||||
|
||||
.grid-entries {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6,1fr);
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
.grid-entries {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.grid-entries {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.grid-entries {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.grid-entries {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.grid-entries {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
.activity-tl-card-description {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text2);
|
||||
white-space: nowrap;
|
||||
word-break: break-word;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.3;
|
||||
}
|
||||
@@ -265,13 +265,15 @@
|
||||
@media (max-width: 600px) {
|
||||
.activity-tl-header { flex-direction: column; align-items: flex-start; }
|
||||
.activity-tl-thumb { display: none; }
|
||||
.activity-tl-left { display: none; }
|
||||
.activity-tl-card-description { display: none; }
|
||||
.activity-day-sep { padding-left: 44px; }
|
||||
.activity-tl-left { width: 44px; }
|
||||
|
||||
|
||||
.activity-tl-date {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
.activity-tl-content-title {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@@ -279,7 +281,7 @@
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.activity-timeline {
|
||||
padding-left: 50px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.activity-tl-left {
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
line-height: 1.6;
|
||||
color: var(--text);
|
||||
margin-bottom: 30px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.entry-gallery {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
.menu-header {
|
||||
padding: 10px;
|
||||
@@ -44,9 +45,12 @@
|
||||
}
|
||||
|
||||
.menu-navigation {
|
||||
flex-grow: 1;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
padding: 10px 0;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior: contain;
|
||||
|
||||
.menu-group {
|
||||
margin-bottom: 20px;
|
||||
@@ -96,7 +100,9 @@
|
||||
}
|
||||
|
||||
.menu-user {
|
||||
padding: 15px 20px;
|
||||
flex-shrink: 0;
|
||||
margin-top: auto;
|
||||
padding: 15px 20px calc(15px + env(safe-area-inset-bottom)) 20px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -131,6 +137,7 @@
|
||||
&.username {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
&.user_role {
|
||||
font-size: 0.75rem;
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 60px;
|
||||
height: calc(100vh - 60px);
|
||||
height: calc(100dvh - 60px);
|
||||
max-height: calc(100dvh - 60px);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease-in-out;
|
||||
z-index: 999;
|
||||
|
||||
@@ -131,15 +131,38 @@ export function GalleryManager() {
|
||||
this.dragSrcI = index;
|
||||
},
|
||||
|
||||
moveImageUp(index){
|
||||
if( index <= 0 )
|
||||
return;
|
||||
|
||||
const moved = this.images.splice(index, 1)[0];
|
||||
this.images.splice(index - 1, 0, moved);
|
||||
},
|
||||
|
||||
moveImageDown(index){
|
||||
if( index >= this.images.length - 1 )
|
||||
return;
|
||||
|
||||
const moved = this.images.splice(index, 1)[0];
|
||||
this.images.splice(index + 1, 0, moved);
|
||||
},
|
||||
|
||||
reorderImages(from, to){
|
||||
if( from === null || to === null || from === to )
|
||||
return;
|
||||
|
||||
const moved = this.images.splice(from, 1)[0];
|
||||
this.images.splice(to, 0, moved);
|
||||
this.dragSrcI = to;
|
||||
},
|
||||
|
||||
dragOver(e, index){
|
||||
e.preventDefault();
|
||||
|
||||
if( this.dragSrcI === null || this.dragSrcI === index )
|
||||
return;
|
||||
|
||||
const moved = this.images.splice(this.dragSrcI, 1)[0];
|
||||
this.images.splice(index, 0, moved);
|
||||
this.dragSrcI = index;
|
||||
this.reorderImages(this.dragSrcI, index);
|
||||
},
|
||||
|
||||
dragEnd(){
|
||||
|
||||
@@ -8,6 +8,7 @@ import notifications from "./notifications.js";
|
||||
import conversations from "./conversations.js";
|
||||
import settings from "./settings.js";
|
||||
import { initMobileMenu } from "./mobile-menu.js";
|
||||
import { initMobileBackToTop } from "./mobile-back-to-top.js"
|
||||
|
||||
/**
|
||||
* Get config defined in meta.blade.php
|
||||
@@ -47,3 +48,4 @@ Alpine.store('settings', settings() );
|
||||
|
||||
// Mobile Menu
|
||||
document.addEventListener('DOMContentLoaded', initMobileMenu);
|
||||
document.addEventListener( 'DOMContentLoaded', initMobileBackToTop );
|
||||
|
||||
@@ -72,6 +72,7 @@ export default function hovercard(){
|
||||
Alpine.nextTick(() => {
|
||||
const card = document.querySelector('.hovercard');
|
||||
if (card) window.refreshIcons(card);
|
||||
this.updatePosition(this.anchorEl);
|
||||
});
|
||||
|
||||
} catch( error ){
|
||||
@@ -89,13 +90,26 @@ export default function hovercard(){
|
||||
const RECT = anchorEl.getBoundingClientRect();
|
||||
const SCROLL_X = window.scrollX;
|
||||
const SCROLL_Y = window.scrollY;
|
||||
const VIEWPORT_WIDTH = window.innerWidth;
|
||||
const VIEWPORT_HEIGHT = window.innerHeight;
|
||||
|
||||
let x = RECT.left + SCROLL_X;
|
||||
const CARD = document.querySelector('.hovercard');
|
||||
const WIDTH = CARD?.offsetWidth || 280;
|
||||
const HEIGHT = CARD?.offsetHeight || 320;
|
||||
|
||||
let x = RECT.right + SCROLL_X + 8;
|
||||
let y = RECT.bottom + SCROLL_Y + 8;
|
||||
|
||||
const WIDTH = 280;
|
||||
if( x + WIDTH > window.innerWidth ){
|
||||
x = window.innerWidth - WIDTH - 16;
|
||||
if( x + WIDTH > VIEWPORT_WIDTH - 8 && RECT.left + SCROLL_X - WIDTH - 8 >= 8 ){
|
||||
x = RECT.left + SCROLL_X - WIDTH - 8;
|
||||
} else {
|
||||
x = Math.max(8, Math.min(x, VIEWPORT_WIDTH - WIDTH - 8));
|
||||
}
|
||||
|
||||
if( y + HEIGHT > VIEWPORT_HEIGHT + SCROLL_Y - 8 && RECT.top + SCROLL_Y - HEIGHT - 8 >= 8 ){
|
||||
y = RECT.top + SCROLL_Y - HEIGHT - 8;
|
||||
} else {
|
||||
y = Math.max(8, Math.min(y, VIEWPORT_HEIGHT + SCROLL_Y - HEIGHT - 8));
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
|
||||
18
resources/js/mobile-back-to-top.js
Normal file
18
resources/js/mobile-back-to-top.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export function initMobileBackToTop(){
|
||||
const backToTopButton = document.querySelector('.back-to-top');
|
||||
const content = document.getElementById('content');
|
||||
|
||||
if (!backToTopButton || !content) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toggleBackToTop = () => {
|
||||
const shouldShow = window.innerWidth <= 768 && content.scrollTop > 320;
|
||||
|
||||
backToTopButton.classList.toggle('visible', shouldShow);
|
||||
};
|
||||
|
||||
toggleBackToTop();
|
||||
content.addEventListener('scroll', toggleBackToTop, { passive: true });
|
||||
window.addEventListener('resize', toggleBackToTop, { passive: true });
|
||||
}
|
||||
@@ -15,7 +15,15 @@
|
||||
</div>
|
||||
<div class="form-gallery form-group level" style="flex:4;">
|
||||
<template x-for="(image,i) in images" :key="image.key">
|
||||
<div class="gallery-item" :class="{ 'gallery-item--dragging': dragSrcI === i }" draggable="true" @dragstart="dragStart(i)" @dragover="dragOver($event, i)" @dragend="dragEnd()">
|
||||
<div
|
||||
class="gallery-item"
|
||||
:class="{ 'gallery-item--dragging': dragSrcI === i }"
|
||||
draggable="true"
|
||||
:data-gallery-index="i"
|
||||
@dragstart="dragStart(i)"
|
||||
@dragover="dragOver($event, i)"
|
||||
@dragend="dragEnd()"
|
||||
>
|
||||
<div class="form-image-preview-wrap">
|
||||
<div class="gallery-drag-handle" title="Drag to reorder">
|
||||
<i data-lucide="grip-vertical" size="14"></i>
|
||||
@@ -28,6 +36,14 @@
|
||||
X
|
||||
</button>
|
||||
</div>
|
||||
<div class="gallery-mobile-controls" aria-label="Reorder screenshot">
|
||||
<button type="button" class="gallery-move-btn" @click="moveImageUp(i)" :disabled="i === 0" title="Move up">
|
||||
<i data-lucide="chevron-up" size="14"></i>
|
||||
</button>
|
||||
<button type="button" class="gallery-move-btn" @click="moveImageDown(i)" :disabled="i === images.length - 1" title="Move down">
|
||||
<i data-lucide="chevron-down" size="14"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<x-form-field-title name="Staff/Credits" />
|
||||
<template x-if="credits.length > 0">
|
||||
<div class="form-group grid-credits">
|
||||
<div><x-form-field-title name="Name" /></div>
|
||||
<div><x-form-field-title name="Description" /></div>
|
||||
<div><x-form-field-title name="Actions" /></div>
|
||||
<div class="grid-first"><x-form-field-title name="Name" /></div>
|
||||
<div class="grid-first"><x-form-field-title name="Description" /></div>
|
||||
<div class="grid-first"><x-form-field-title name="Actions" /></div>
|
||||
<template x-for="(credit,i) in credits" :key="i">
|
||||
<div style="display:contents">
|
||||
<div>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
|
||||
<div class="comment-body">
|
||||
{!! $comment['message'] !!}
|
||||
{!! \App\Helpers\EntryHelpers::stripBbCode($comment['message']) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
|
||||
</div>
|
||||
|
||||
<button type="button" class="back-to-top" aria-label="Back to top" onclick="document.getElementById('content')?.scrollTo({ top: 0, behavior: 'smooth' });">
|
||||
<i data-lucide="arrow-up"></i>
|
||||
<span>Top</span>
|
||||
</button>
|
||||
|
||||
@include('components.hovercard')
|
||||
@livewireScripts
|
||||
@stack('scripts')
|
||||
|
||||
@@ -38,7 +38,11 @@
|
||||
</div>
|
||||
<div class="menu-user-info">
|
||||
<span class="username">
|
||||
{{ $VISITOR->username ?? "Guest" }}
|
||||
@if( $VISITOR->guest() )
|
||||
Guest
|
||||
@else
|
||||
<x-xf-username-link :user-id="$VISITOR->user_id" />
|
||||
@endif
|
||||
</span>
|
||||
<span class="user_role">
|
||||
<a href="{{ $VISITOR->guest() ? xfRoute('login') : xfRoute('logout') . '?t=' . xfCsrfToken() }}">
|
||||
|
||||
Reference in New Issue
Block a user