A bug with collapsing when scrolling in Web App for Telegram Bot
Asked Answered
P

7

9

I want to build my mobile web application written in flutter in Telegram. At the same time, there is a problem: when scrolling down, the application collapses, as if I were collapsing the bottom sheet.

Web App; Telegram Web App; Video demonstration of the problem

I managed to work around this problem for iOS devices by adding the following script to the code:

document.addEventListener("touchmove", function (event) {
    event.preventDefault();
},
    {
        passive: false
    });

But on Android, the problem persists. I tried all the variations of working with touchmove, overflow, relative and other ways to disable scrolling on the page. The problem on Android has not been solved by any method that I have found.

Preform answered 5/8, 2023 at 16:29 Comment(1)
Does this answer your question? Telegram Web App collapse on bottom swipeMisdirection
O
2

Telegram released an official solution for that

window.Telegram.WebApp.disableVerticalSwipes()

disableVerticalSwipes() Bot API 7.7+ A method that disables vertical swipes to close or minimize the Mini App. This method is useful if your Mini App uses swipe gestures that may conflict with the gestures for minimizing and closing the app.

https://core.telegram.org/bots/webapps

Requires Bot API 7.7+, you may check current version in bot @theme_inspector_bot

Ostwald answered 16/7 at 18:1 Comment(1)
I just tried this method, it's perfect!Remanence
C
1

Add this to the body section in the css to disable any touch events:

body {
    touch-action: none !important;
}
Cowling answered 25/8, 2023 at 10:32 Comment(5)
Not op here, I've tried this and it only works sometimes. I can scroll but after a while it randomly locks in place. @Zak, do you know why?Vorfeld
I had issues when I used scrollable modals, the scrolling in modals confuses the scrolling in main page, so I decided to stop using modals for longer contents, my pages works just fineCowling
I'm not using scrollable modules. I am using addeventlistener("touchstart") and "touchmove" though. Is there anything I should avoid or additional code I need to use your answer?Vorfeld
I have the code hosted on GitHub at github.com/botfi-app/botfi-wallet , kindly check it out to learn how i did itCowling
I ended up removing the modals and making them standalone pagesCowling
B
1

The issue you're encountering with collapsing when scrolling or swiping down in the Telegram Web App is a common problem. Having faced a similar issue myself, I've discovered a solution that effectively resolves it.

Visual Comparsion:

Before diving into the solution, let's visually observe the problem. Below are two GIFs illustrating the issue before and after implementing the solution.

enter image description here enter image description here
Bug
Swiping down the Telegram Mini App
causes it to collapse instead of scrolling*
Fixed
Swiping down the Telegram Mini App
now correctly scrolls through the content after the issue is fixed!*

Now that we've seen the issue in action, let's proceed to implement the solution. I'll provide a step-by-step guide to resolve the problem.

Step-by-Step Solution:

1. Ensure the Document is Scrollable:

Firstly, it's essential to ensure that the document is scrollable. We achieve this by checking if the document's scroll height is greater than the viewport height. If not, we adjust the document's height accordingly.

// Ensure the document is scrollable
function ensureDocumentIsScrollable() {
  const isScrollable =
    document.documentElement.scrollHeight > window.innerHeight;
  // Check if the document is scrollable
  if (!isScrollable) {
    /*
    Set the document's height to 100 % of
    the viewport height plus one extra pixel
    to make it scrollable.
    */
    document.documentElement.style.setProperty(
      "height",
      "calc(100vh + 1px)",
      "important"
    );
  }
}

// Call ensureDocumentIsScrollable function when the entire page has loaded.
window.addEventListener("load", ensureDocumentIsScrollable);

2. Prevent window.scrollY from Becoming Zero:

Next, we prevent window.scrollY from becoming zero when swiping down on the scrollable element. This prevents unexpected collapses when swiping down.

// Prevent windwo.scrollY from becoming zero
function preventCollapse(event) {
  if (window.scrollY === 0) {
    window.scrollTo(0, 1);
  }
}

// Attach the above function to the touchstart event handler of the scrollable element
const scrollableElement = document.querySelector(".scrollable-element");
scrollableElement.addEventListener("touchstart", preventCollapse);

Now that we've outlined the steps, let's integrate the solution into your code. Below is the full code snippet. I've removed unnecessary code and styles to understand the code better.

<html>
  <head>
    <style>
      .scrollable-element {
        overflow-y: scroll;
        height: 32rem;
        font-size: 6.25rem;
        border: 1px solid;
      }
    </style>
  </head>
  <body>
    <div class="scrollable-element">
      <div>Item 1</div>
      <div>Item 2</div>
      <div>Item 3</div>
      <div>Item 4</div>
      <div>Item 5</div>
      <div>Item 6</div>
      <div>Item 7</div>
      <div>Item 8</div>
      <div>Item 9</div>
      <div>Item 10</div>
    </div>
    <script>
      function ensureDocumentIsScrollable() {
        const isScrollable =
          document.documentElement.scrollHeight > window.innerHeight;
        if (!isScrollable) {
          document.documentElement.style.setProperty(
            "height",
            "calc(100vh + 1px)",
            "important"
          );
        }
      }
      function preventCollapse() {
        if (window.scrollY === 0) {
          window.scrollTo(0, 1);
        }
      }

      const scrollableElement = document.querySelector(".scrollable-element");
      scrollableElement.addEventListener("touchstart", preventCollapse);

      window.addEventListener("load", ensureDocumentIsScrollable);
    </script>
  </body>
</html>

By implementing these adjustments, your Telegram Mini App will no longer suffer from unexpected collapses when scrolling for swiping down.

I encourage you to deploy the provided code for a Telegram Web App and observe the results firsthand.

Additionally, you can experience the fix in action on a Telegram Mini App I've applied this solution to: @theme_inspector_bot

Barbusse answered 28/5 at 13:31 Comment(0)
B
0

There are 3 steps: add style, add scroll and add delay

  1. Add this style under head tag in index.html:

    <style>
        body {
          height: 1000vh !important;
          position: static !important;
          overscroll-behavior: none;
        }
    
        flutter-view {
          position: fixed !important;
          max-width: 100vw !important;
          max-height: 100vh !important;
        }
    
        /* Safari-specific CSS */
        @supports (-webkit-touch-callout: inherit) {
          body {
            height: 1000vh !important;
          }
        }
      </style>
    
    1. Add scrolling script under body tag in index.html:
 <script>window.scroll(0, window.document.body.scrollHeight);</script>
  1. (Optional) Sometimes Telegram not loading fast so add some delay in main.dart:

     void main() async {
       await Future.delayed(const Duration(milliseconds: 500));
     }
    

You can follow similar github issue here

Binomial answered 24/6 at 20:39 Comment(0)
J
0

I also faced this problem, in my case the problem was even harder, I needed to insert several containers that scroll on the page, I found a way to avoid this for one container, however when I applied it separately, the containers did not scroll at all, so I made this hook:

import React, { useEffect } from "react";


export const useClassnamedStickyScroll = (...classes: string[]) => {
  useEffect(() => {
    const overflow = 100;
    document.body.style.overflowY = "hidden";
    document.documentElement.style.overflowY = "hidden";
    document.body.style.marginTop = `${overflow}px`;
    document.body.style.height = window.innerHeight + overflow + "px";
    document.body.style.paddingBottom = `${overflow}px`;
    window.scrollTo(0, overflow);
    let ts: number | undefined;
    const onTouchStart = (e: TouchEvent) => {
      ts = e.touches[0].clientY;
    };
    const onTouchMove = (e: TouchEvent) => {
      const elems = classes.map((s) => document.querySelector(`.${s}`));
      if (!elems.includes(null)) {
        const allScrolls = elems
          .map((s) => s as HTMLDivElement)
          .map((s) => s.scrollTop);
        const isScrooled = allScrolls.every((s) => s <= 0);
        const te = e.changedTouches[0].clientY;
        const targetNotInCollection = !elems.some(
          (elem) => elem === e.target || elem?.contains(e.target as Node)
        );
        if (isScrooled && ts! < te && targetNotInCollection) {
          e.preventDefault();
        }
      } else {
        e.preventDefault();
      }
    };
    document.documentElement.addEventListener("touchstart", onTouchStart, {
      passive: false,
    });
    document.documentElement.addEventListener("touchmove", onTouchMove, {
      passive: false,
    });
    return () => {
      document.body.style.overflowY = "hidden";
      document.documentElement.style.overflowY = "hidden";
      document.body.style.marginTop = `${0}px`;
      document.body.style.height = window.innerHeight + 0 + "px";
      document.body.style.paddingBottom = `${0}px`;
      document.documentElement.removeEventListener("touchstart", onTouchStart);
      document.documentElement.removeEventListener("touchmove", onTouchMove);
    };
  }, []);
};

it is important that your containers are div nodes, hope this helps you! You need to pass as args ur containers classnames. Like

useClassnamedStickyScroll(
    "streamer__subscribers-users",
    "streamer__prizes-body"
  );
Jefe answered 24/7 at 14:16 Comment(0)
D
0

For Next Js users use this hook, and import it in _app.tsx:

import { useEffect, useCallback } from "react";

const SCROLL_LOCK_POSITION = 1000;

export function useScrollLock() {
  const handleScroll = useCallback(() => {
    if (window.scrollY < SCROLL_LOCK_POSITION) {
      window.requestAnimationFrame(() => {
        window.scrollTo(0, SCROLL_LOCK_POSITION);
      });
    }
  }, []);

  useEffect(() => {
    // Set initial scroll position
    window.scrollTo(0, SCROLL_LOCK_POSITION);

    // Add scroll event listener
    window.addEventListener("scroll", handleScroll);

    // Clean up
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [handleScroll]);

  return null;
}

and also write this css inside _document.tsx:

* {
        scrollbar-width: none !important;
        -ms-overflow-style: none !important;
      }
      
      *::-webkit-scrollbar {
        display: none !important;
        width: 0 !important;
        height: 0 !important;
      }

      body {
        height: 1000vh !important;
        position: static !important;
        overscroll-behavior: none;
      }

      body, #__next {
        -ms-overflow-style: none;  /* IE and Edge */
        scrollbar-width: none;  /* Firefox */
      }

      body::-webkit-scrollbar, #__next::-webkit-scrollbar {
        display: none;
      }

      #__next {
        position: fixed !important;
        max-width: 100vw !important;
        max-height: 100vh !important;
      }

      @supports (-webkit-touch-callout: inherit) {
        body {
          height: 1000vh !important;
        }
      }
Discretional answered 24/7 at 14:20 Comment(0)
J
0

It's easy! Just past code to index.html

<script>
document.addEventListener('DOMContentLoaded', function(){
    Telegram.WebApp.disableVerticalSwipes()
});
Jointure answered 10/8 at 14:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.