Skip to content

Commit 5b7fe9f

Browse files
committed
webui: add pull to refresh
1 parent 57fe983 commit 5b7fe9f

4 files changed

Lines changed: 116 additions & 0 deletions

File tree

webui/page/exclude.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { listPackages, getPackagesInfo, exec } from 'kernelsu-alt';
22
import { modDir, persistDir } from '../index.js';
33
import { getString } from '../language.js';
4+
import { setupPullToRefresh } from '../pull-to-refresh.js';
45
import fallbackIcon from '../icon.png';
56

67
let allApps = [];
@@ -311,6 +312,11 @@ function initExcludePage() {
311312
refreshAppList();
312313
};
313314

315+
setupPullToRefresh(document.querySelector('#exclude-page .page-content'), async () => {
316+
appItemMap.clear();
317+
await refreshAppList();
318+
});
319+
314320
// init render
315321
refreshAppList();
316322
}

webui/page/kpm.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { exec, spawn, toast } from 'kernelsu-alt';
22
import { modDir, persistDir, initInfo, MAX_CHUNK_SIZE, linkRedirect } from '../index.js';
33
import { getString } from '../language.js';
4+
import { setupPullToRefresh } from '../pull-to-refresh.js';
45

56
let allKpms = [];
67
let searchQuery = '';
@@ -425,6 +426,11 @@ export function initKPMPage() {
425426
});
426427

427428
document.getElementById('load').onclick = () => uploadAndLoadModule();
429+
430+
setupPullToRefresh(document.querySelector('#kpm-page .page-content'), async () => {
431+
kpmItemMap.clear();
432+
await refreshKpmList();
433+
});
428434
}
429435

430436
export { loadModule, refreshKpmList, handleFileUpload, uploadFile }

webui/pull-to-refresh.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
export function setupPullToRefresh(element, onRefresh) {
2+
let startY = 0;
3+
let currentY = 0;
4+
let pulling = false;
5+
let refreshing = false;
6+
const threshold = 130;
7+
const maxPull = 170;
8+
9+
const container = document.createElement('div');
10+
container.className = 'ptr-indicator';
11+
container.innerHTML = `
12+
<div class="ptr-indicator-wrapper">
13+
<md-circular-progress value="0" class="ptr-progress"></md-circular-progress>
14+
</div>
15+
`;
16+
17+
element.parentElement.insertBefore(container, element);
18+
19+
const progress = container.querySelector('.ptr-progress');
20+
21+
element.addEventListener('touchstart', (e) => {
22+
if (element.scrollTop <= 0 && !refreshing) {
23+
startY = e.touches[0].pageY;
24+
pulling = true;
25+
container.style.transition = 'none';
26+
}
27+
}, { passive: true });
28+
29+
element.addEventListener('touchmove', (e) => {
30+
if (!pulling) return;
31+
currentY = e.touches[0].pageY;
32+
const diff = currentY - startY;
33+
34+
if (diff > 0) {
35+
if (e.cancelable) e.preventDefault();
36+
37+
const pullDistance = Math.min(diff * 0.5, maxPull);
38+
const scale = Math.min(diff / 50, 1);
39+
container.style.transform = `translateY(${pullDistance}px) scale(${scale})`;
40+
container.style.opacity = Math.min(diff / 50, 1);
41+
const progressValue = Math.max(0, Math.min((diff - 50) / (threshold - 50), 1));
42+
progress.value = progressValue;
43+
} else {
44+
pulling = false;
45+
container.style.opacity = '0';
46+
container.style.transform = `translateY(0) scale(0)`;
47+
}
48+
}, { passive: false });
49+
50+
element.addEventListener('touchend', async () => {
51+
if (!pulling) return;
52+
pulling = false;
53+
const diff = currentY - startY;
54+
55+
if (diff > threshold) {
56+
refreshing = true;
57+
progress.indeterminate = true;
58+
container.style.transition = 'all 0.3s ease';
59+
container.style.transform = `translateY(${threshold * 0.5}px) scale(1)`;
60+
container.style.opacity = '1';
61+
62+
try {
63+
await onRefresh();
64+
} finally {
65+
refreshing = false;
66+
progress.indeterminate = false;
67+
progress.value = 0;
68+
container.style.transform = `translateY(0) scale(0)`;
69+
container.style.opacity = '0';
70+
}
71+
} else {
72+
container.style.transition = 'all 0.3s ease';
73+
container.style.transform = `translateY(0) scale(0)`;
74+
container.style.opacity = '0';
75+
}
76+
});
77+
}

webui/styles.css

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,33 @@ body {
162162
padding: 16px 0;
163163
box-sizing: border-box;
164164
scroll-behavior: smooth;
165+
overscroll-behavior-y: contain;
166+
}
167+
168+
.ptr-indicator {
169+
position: absolute;
170+
top: -48px;
171+
left: 0;
172+
right: 0;
173+
height: 48px;
174+
display: flex;
175+
align-items: center;
176+
justify-content: center;
177+
z-index: 10;
178+
pointer-events: none;
179+
opacity: 0;
180+
}
181+
182+
.ptr-indicator-wrapper {
183+
display: flex;
184+
align-items: center;
185+
justify-content: center;
186+
height: 48px;
187+
width: 48px;
188+
background-color: var(--md-sys-color-surface-container-high);
189+
border-radius: 50%;
190+
191+
--md-circular-progress-size: 36px;
165192
}
166193

167194
.page.active {

0 commit comments

Comments
 (0)