Ryanhub - file viewer
filename: views/layout/footer.php
branch: main
back to repo
        </main>
    </div>
    <footer class="site-footer">
        <div class="page-shell" style="text-align: center;">
            <div>Theres no time like 7pm dinner time.</div>
        </div>
    </footer>

    <div class="image-modal-backdrop" data-image-modal>
        <img src="" alt="">
    </div>

    <div class="upload-interactions-modal-backdrop" data-upload-interactions-modal style="align-items:center; justify-content:center;">
        <div style="width: min(860px, 100%); max-height: 86vh; overflow:auto; border-radius: 18px; background: var(--bg-elevated); border: 1px solid rgba(255,255,255,0.18); box-shadow: 0 30px 100px rgba(0,0,0,0.55); padding: 18px 18px; position: relative;">
            <div data-upload-interactions-body>
                <!-- Server-rendered HTML goes here -->
            </div>
            <button
                type="button"
                class="pill"
                data-close-upload-interactions
                style="position: absolute; top: 12px; right: 12px; z-index: 1; font-size: 11px; padding: 6px 10px;"
                aria-label="Close comments modal"
            >
                X
            </button>
        </div>
    </div>

    <script>
        (function () {
            if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                return;
            }
            document.addEventListener('DOMContentLoaded', function () {
                var initialCards = document.querySelectorAll('[data-animate-initial]');
                var laterCards = document.querySelectorAll('[data-animate]');

                initialCards.forEach(function (el) {
                    el.classList.add('fade-in-1');
                });

                laterCards.forEach(function (el) {
                    el.classList.add('fade-in-2');
                });

                var modal = document.querySelector('[data-image-modal]');
                var modalImg = modal ? modal.querySelector('img') : null;
                if (!modal || !modalImg) return;

                function openModal(src) {
                    modalImg.src = src;
                    modal.classList.add('is-open');
                }

                function closeModal() {
                    modal.classList.remove('is-open');
                    modalImg.src = '';
                }

                document.addEventListener('click', function (event) {
                    var trigger = event.target.closest('[data-image-full]');
                    if (trigger && trigger.getAttribute('data-image-full')) {
                        event.preventDefault();
                        openModal(trigger.getAttribute('data-image-full'));
                    } else if (modal.classList.contains('is-open') && (event.target === modal || !modal.contains(event.target))) {
                        closeModal();
                    }
                });

                document.addEventListener('keyup', function (event) {
                    if (event.key === 'Escape' && modal.classList.contains('is-open')) {
                        closeModal();
                    }
                });
            });
        })();
    </script>

    <style>
        .upload-interactions-modal-backdrop {
            position: fixed;
            inset: 0;
            background: rgba(0, 0, 0, 0.72);
            display: none;
            z-index: 4200;
            padding: 24px;
        }
        .upload-interactions-modal-backdrop.is-open {
            display: flex;
        }
    </style>

    <script>
        (function () {
            document.addEventListener('DOMContentLoaded', function () {
                var backdrop = document.querySelector('[data-upload-interactions-modal]');
                var body = backdrop ? backdrop.querySelector('[data-upload-interactions-body]') : null;
                if (!backdrop || !body) return;

                // No header/footer inside the modal; closing is handled via backdrop click and Escape.

                function openModal() {
                    backdrop.classList.add('is-open');
                }
                function closeModal() {
                    backdrop.classList.remove('is-open');
                }

                    var closeBtn = backdrop.querySelector('[data-close-upload-interactions]');
                    if (closeBtn) {
                        closeBtn.addEventListener('click', function (e) {
                            e.preventDefault();
                            closeModal();
                        });
                    }

                function setBodyHtml(html) {
                    body.innerHTML = html;
                    // Re-bind forms inside the modal to refresh modal body via fetch.
                    var commentForms = body.querySelectorAll('form.upload-comment-form');
                    commentForms.forEach(function (form) {
                        form.addEventListener('submit', function (e) {
                            e.preventDefault();
                            submitFormForBody(form);
                        });
                    });
                }

                function submitFormForBody(form) {
                    var formData = new FormData(form);
                    var action = form.getAttribute('action') || '';
                    fetch(action, {
                        method: 'POST',
                        body: formData,
                        headers: { 'X-Requested-With': 'XMLHttpRequest' }
                    }).then(function (r) { return r.text(); })
                        .then(function (html) { setBodyHtml(html); })
                        .catch(function () { /* leave existing content */ });
                }

                function loadInteractions(uploadId, opts) {
                    opts = opts || {};
                    openModal();
                    body.innerHTML = '<div class="muted" style="padding: 14px 0;">Loading…</div>';
                    var urlBase = '<?= addslashes(url('api/upload-interactions')) ?>';
                    var fullUrl = urlBase + '?upload_id=' + encodeURIComponent(uploadId);
                    var method = opts.reaction ? 'POST' : 'GET';
                    if (method === 'GET') {
                        fetch(fullUrl, {
                            method: 'GET',
                            headers: { 'X-Requested-With': 'XMLHttpRequest' }
                        }).then(function (r) { return r.text(); })
                            .then(function (html) {
                                setBodyHtml(html);
                                if (opts.openComments) {
                                    scrollToComments();
                                }
                            })
                            .catch(function () {
                                body.innerHTML = '<div style="color:#9b2c2c; font-size:13px;">Failed to load discussion.</div>';
                            });
                        return;
                    }

                    // POST reaction, then reload modal body via server-rendered HTML.
                    var fd = new FormData();
                    fd.append('upload_id', uploadId);
                    fd.append('reaction', opts.reaction);
                    fetch(urlBase, {
                        method: 'POST',
                        body: fd,
                        headers: { 'X-Requested-With': 'XMLHttpRequest' }
                    }).then(function (r) { return r.text(); })
                        .then(function (html) {
                            setBodyHtml(html);
                            if (opts.openComments) {
                                scrollToComments();
                            }
                        })
                        .catch(function () {
                            body.innerHTML = '<div style="color:#9b2c2c; font-size:13px;">Failed to load discussion.</div>';
                        });
                }

                document.addEventListener('click', function (event) {
                    var trigger = event.target.closest('[data-open-upload-interactions]');
                    if (trigger) {
                        var uploadId = trigger.getAttribute('data-upload-id') || '';
                        if (!uploadId) return;
                        var reaction = trigger.getAttribute('data-reaction');
                        var openComments = trigger.getAttribute('data-open-comments');

                        // If this is a reaction click from the main page, do async update and do NOT open modal.
                        if (reaction) {
                            event.preventDefault();
                            var urlBase = '<?= addslashes(url('api/upload-interactions')) ?>';
                            var fd = new FormData();
                            fd.append('upload_id', uploadId);
                            fd.append('reaction', reaction);
                            fetch(urlBase, {
                                method: 'POST',
                                body: fd,
                                headers: { 'X-Requested-With': 'XMLHttpRequest' }
                            }).then(function (r) { return r.json(); })
                                .then(function (data) {
                                    // Update only the clicked card's like/dislike button labels.
                                    var card = trigger.closest('figure');
                                    if (!card) return;
                                    var likeBtn = card.querySelector('[data-reaction="like"]');
                                    var dislikeBtn = card.querySelector('[data-reaction="dislike"]');
                                    if (likeBtn) likeBtn.textContent = '▲ ' + (data.like_count || 0);
                                    if (dislikeBtn) dislikeBtn.textContent = '▼ ' + (data.dislike_count || 0);
                                })
                                .catch(function () { /* ignore */ });
                            return;
                        }

                        loadInteractions(uploadId, {
                            reaction: reaction ? reaction : null,
                            openComments: Boolean(openComments)
                        });
                    } else if (backdrop.classList.contains('is-open') && (event.target === backdrop)) {
                        closeModal();
                    }
                });

                function scrollToComments() {
                    // Scroll inside the modal container to the comments block.
                    var commentsHeading = body.querySelector('.upload-interactions-comments-heading');
                    if (!commentsHeading) return;
                    commentsHeading.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }

                document.addEventListener('keyup', function (event) {
                    if (event.key === 'Escape' && backdrop.classList.contains('is-open')) {
                        closeModal();
                    }
                });
            });
        })();
    </script>
</body>
</html>