Skip to content

Commit dd23a58

Browse files
committed
✨feat: enable native bounce effect for Android WebView while preserving pull-to-refresh
- Add enableWebViewBounceEffect() method to MainActivity.kt to enable OVER_SCROLL_ALWAYS - Modify ScrollArea component to allow native bounce for small pulls (<80px) - Only prevent default behavior for intentional pull-to-refresh gestures - Maintain pull-to-refresh functionality with smart threshold detection
1 parent 68b4a1e commit dd23a58

File tree

3 files changed

+28
-26
lines changed

3 files changed

+28
-26
lines changed

app/src-tauri/gen/android/app/src/main/java/com/blinko/app/MainActivity.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class MainActivity : TauriActivity() {
1919
// Apply saved theme before super.onCreate to prevent flash
2020
blinko.applyStartupTheme(this)
2121
super.onCreate(savedInstanceState)
22+
enableWebViewBounceEffect()
2223
handleShortcutIntent()
2324
handleShareIntent()
2425
}
@@ -33,6 +34,20 @@ class MainActivity : TauriActivity() {
3334
handleShareIntent()
3435
}
3536

37+
private fun enableWebViewBounceEffect() {
38+
// Use a small delay to ensure WebView is initialized
39+
window.decorView.postDelayed({
40+
try {
41+
findWebView(window.decorView)?.let { webView ->
42+
// Enable bounce/overscroll effect
43+
webView.overScrollMode = View.OVER_SCROLL_ALWAYS
44+
Log.i("BlinkoApp", "WebView bounce effect enabled")
45+
}
46+
} catch (e: Exception) {
47+
Log.e("BlinkoApp", "Failed to enable WebView bounce effect: ${e.message}")
48+
}
49+
}, 500L) // Short delay to ensure WebView is ready
50+
}
3651

3752
private fun handleShortcutIntent() {
3853
if (hasInjectedShortcut) return

app/src/components/BlinkoSettings/AboutSetting.tsx

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -156,23 +156,6 @@ export const AboutSetting = observer(() => {
156156
</div>
157157
</div>
158158

159-
{/* Version info explanation */}
160-
{isInTauri() && (
161-
<div className="mb-6 p-4 bg-secondbackground rounded-lg">
162-
<div className="flex items-start gap-3">
163-
<Icon icon="mdi:information" className="text-blue-600 mt-0.5" width="20" height="20" />
164-
<div className="flex-1">
165-
<h4 className="font-medium text-blue-800 mb-1">
166-
{t('version-info-title')}
167-
</h4>
168-
<p className="text-sm text-blue-700">
169-
{t('version-info-description')}
170-
</p>
171-
</div>
172-
</div>
173-
</div>
174-
)}
175-
176159
<div className="space-y-4">
177160
<h3 className="font-medium text-gray-500 mb-2">{t('community')}</h3>
178161
<Item
@@ -227,7 +210,7 @@ export const AboutSetting = observer(() => {
227210
color="warning"
228211
variant="flat"
229212
startContent={<Icon icon="mdi:cached" width="16" />}
230-
onClick={clearBrowserCache}
213+
onPress={clearBrowserCache}
231214
>
232215
{t('clear-cache')}
233216
</Button>

app/src/components/Common/ScrollArea/index.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export const ScrollArea = observer(forwardRef<ScrollAreaHandles, IProps>(({
8181
return;
8282
}
8383

84-
// Check if at top position
85-
if (scrollElement.scrollTop > 0) return;
84+
// Check if at top position - allow small tolerance for bounce effect
85+
if (scrollElement.scrollTop > 5) return;
8686

8787
const clientY = e instanceof TouchEvent ? e.touches[0].clientY : e.clientY;
8888
startYRef.current = clientY;
@@ -109,11 +109,16 @@ export const ScrollArea = observer(forwardRef<ScrollAreaHandles, IProps>(({
109109
const deltaY = clientY - startYRef.current;
110110

111111
if (deltaY > 0) {
112-
// Only prevent default if we're at the top and pulling down
112+
// Only prevent default if we're at the top and pulling down WITH SUFFICIENT FORCE
113113
if (scrollElement.scrollTop === 0) {
114-
e.preventDefault();
115-
const distance = Math.min(deltaY * 0.5, maxPullDownDistance);
116-
setPullDistance(distance);
114+
// Only prevent default and trigger refresh UI when pull distance is significant
115+
// This allows small bounces to work naturally while still enabling pull-to-refresh
116+
if (deltaY > 80) { // Require at least 30px pull before interfering
117+
e.preventDefault();
118+
const distance = Math.min(deltaY * 0.5, maxPullDownDistance);
119+
setPullDistance(distance);
120+
}
121+
// For smaller pulls, let native bounce effect work
117122
}
118123
}
119124
};
@@ -225,8 +230,7 @@ export const ScrollArea = observer(forwardRef<ScrollAreaHandles, IProps>(({
225230
{showRefreshIndicator && (
226231
<div
227232
className={`flex items-center justify-center transition-all duration-150 ${isDragging ? '' : 'duration-300'
228-
} ${isReadyToRefresh ? 'text-primary' : 'text-gray-500'} ${isReadyToRefresh ? 'bg-primary/5' : 'bg-gray-50/80'
229-
}`}
233+
} ${isReadyToRefresh ? 'text-primary' : 'text-gray-500'} `}
230234
style={{
231235
height: `${pullDistance}px`,
232236
marginTop: `-${pullDistance}px`,

0 commit comments

Comments
 (0)