Merge pull request #517 from eitchtee/dev

feat(frontend): add pull to refresh for iOS PWA
This commit is contained in:
Herculino Trotta
2026-02-15 11:36:59 -03:00
committed by GitHub
6 changed files with 129 additions and 1 deletions

View File

@@ -9,9 +9,10 @@
{% include 'includes/scripts/hyperscript/sounds.html' %}
{% include 'includes/scripts/hyperscript/swal.html' %}
{% include 'includes/scripts/hyperscript/autosize.html' %}
{% include 'includes/scripts/pull_to_refresh_i18n.html' %}
<script defer>
let tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (!tz) {
tz = "UTC"
}

View File

@@ -0,0 +1,6 @@
{% load i18n %}
<span id="ptr-i18n"
class="hidden"
data-pull="{% translate 'Pull down to refresh' %}"
data-release="{% translate 'Release to refresh' %}"
data-refreshing="{% translate 'Refreshing' %}"></span>

View File

@@ -28,6 +28,7 @@
"hyperscript.org": "^0.9.14",
"mathjs": "^15.1.0",
"postcss": "^8.5.6",
"pulltorefreshjs": "^0.1.22",
"sass": "^1.94.0",
"sweetalert2": "^11.26.3",
"tailwindcss": "^4.1.17",
@@ -2309,6 +2310,12 @@
"version": "4.2.0",
"license": "MIT"
},
"node_modules/pulltorefreshjs": {
"version": "0.1.22",
"resolved": "https://registry.npmjs.org/pulltorefreshjs/-/pulltorefreshjs-0.1.22.tgz",
"integrity": "sha512-haxNVEHnS4NCQA7NeG7TSV69z4uqy/N7nfPRuc4dPWe8H6ygUrMjdNeohE+6v0lVVX/ukSjbLYwPUGUYtFKfvQ==",
"license": "MIT"
},
"node_modules/readdirp": {
"version": "4.1.2",
"license": "MIT",

View File

@@ -35,6 +35,7 @@
"hyperscript.org": "^0.9.14",
"mathjs": "^15.1.0",
"postcss": "^8.5.6",
"pulltorefreshjs": "^0.1.22",
"sass": "^1.94.0",
"sweetalert2": "^11.26.3",
"tailwindcss": "^4.1.17",

View File

@@ -0,0 +1,112 @@
import PullToRefresh from 'pulltorefreshjs';
const isOverlayOpen = () => !!document.querySelector('.offcanvas.show, .swal2-container');
const isIosPwa = () => {
const ua = window.navigator.userAgent.toLowerCase();
const isIos = /iphone|ipad|ipod/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
const isStandalone = window.navigator.standalone === true || window.matchMedia('(display-mode: standalone)').matches;
return true || isIos && isStandalone;
};
const ptrMarkup = `
<div class="__PREFIX__box">
<div class="__PREFIX__content">
<div class="__PREFIX__icon"></div>
<div class="__PREFIX__text"></div>
</div>
</div>
`;
const ptrStyles = `
.__PREFIX__ptr {
box-shadow: inset 0 -3px 5px rgba(0, 0, 0, 0.12);
pointer-events: none;
font-size: 0.85em;
font-weight: bold;
top: 0;
height: 0;
transition: height 0.3s, min-height 0.3s;
text-align: center;
width: 100%;
overflow: hidden;
display: flex;
align-items: flex-end;
align-content: stretch;
}
.__PREFIX__box {
padding: 10px;
flex-basis: 100%;
}
.__PREFIX__pull {
transition: none;
}
.__PREFIX__text {
margin-top: .33em;
color: var(--color-base-content);
}
.__PREFIX__icon {
color: var(--color-base-content);
transition: transform .3s;
}
/*
When at the top of the page, disable vertical overscroll so passive touch
listeners can take over.
*/
.__PREFIX__top {
touch-action: pan-x pan-down pinch-zoom;
}
.__PREFIX__release .__PREFIX__icon {
transform: rotate(180deg);
}
`;
const getPtrStrings = () => {
const ptrStringsEl = document.querySelector('#ptr-i18n');
return {
pull: ptrStringsEl?.dataset.pull,
release: ptrStringsEl?.dataset.release,
refreshing: ptrStringsEl?.dataset.refreshing
};
};
const initPullToRefresh = () => {
const ptrStrings = getPtrStrings();
PullToRefresh.destroyAll();
let ptr = PullToRefresh.init({
mainElement: 'body',
triggerElement: '#content',
getMarkup() {
return ptrMarkup;
},
getStyles() {
return ptrStyles;
},
instructionsPullToRefresh: ptrStrings.pull || 'Pull down to refresh',
instructionsReleaseToRefresh: ptrStrings.release || 'Release to refresh',
instructionsRefreshing: ptrStrings.refreshing || 'Refreshing',
shouldPullToRefresh() {
return isIosPwa() && !isOverlayOpen() && window.scrollY === 0;
},
onRefresh() {
window.location.reload();
}
});
};
if (isIosPwa()) {
initPullToRefresh();
document.body.addEventListener('htmx:afterSwap', (event) => {
if (event.detail.target === document.body) {
initPullToRefresh();
}
});
}

View File

@@ -10,3 +10,4 @@ import './js/sweetalert2.js';
import './js/style.js';
import './js/_utils.js';
import './js/hide_amounts.js';
import './js/pulltorefresh.js';