Adrien Denat coyotte508 HF staff commited on
Commit
77c7055
1 Parent(s): 5be0e20

Fix scroll to bottom (#78)

Browse files

* fix snap scroll to bottom not updating properly to bottom when switching conversation

* scroll to bottom when a user sends a new message ( fixes #75)

* update comment to be detailing the scroll offset

Co-authored-by: Eliott C. <coyotte508@gmail.com>

* use tick() instead of afterUpdate

---------

Co-authored-by: Eliott C. <coyotte508@gmail.com>

src/lib/actions/snapScrollToBottom.ts CHANGED
@@ -1,6 +1,9 @@
1
  import { navigating } from "$app/stores";
 
2
  import { get } from "svelte/store";
3
 
 
 
4
  /**
5
  * @param node element to snap scroll to bottom
6
  * @param dependency pass in a dependency to update scroll on changes.
@@ -15,24 +18,25 @@ export const snapScrollToBottom = (node: HTMLElement, dependency: any) => {
15
  isDetached = true;
16
  }
17
 
18
- // if user scrolled back to bottom, we reattach
19
- if (node.scrollTop === node.scrollHeight - node.clientHeight) {
20
  isDetached = false;
21
  }
22
 
23
  prevScrollValue = node.scrollTop;
24
  };
25
 
26
- const updateScroll = (_options: { force?: boolean } = {}) => {
27
  const defaultOptions = { force: false };
28
  const options = { ...defaultOptions, ..._options };
29
  const { force } = options;
30
 
31
  if (!force && isDetached && !get(navigating)) return;
32
 
33
- node.scroll({
34
- top: node.scrollHeight,
35
- });
 
36
  };
37
 
38
  node.addEventListener("scroll", handleScroll);
 
1
  import { navigating } from "$app/stores";
2
+ import { tick } from "svelte";
3
  import { get } from "svelte/store";
4
 
5
+ const detachedOffset = 10;
6
+
7
  /**
8
  * @param node element to snap scroll to bottom
9
  * @param dependency pass in a dependency to update scroll on changes.
 
18
  isDetached = true;
19
  }
20
 
21
+ // if user scrolled back to within 10px of bottom, we reattach
22
+ if (node.scrollTop - (node.scrollHeight - node.clientHeight) >= -detachedOffset) {
23
  isDetached = false;
24
  }
25
 
26
  prevScrollValue = node.scrollTop;
27
  };
28
 
29
+ const updateScroll = async (_options: { force?: boolean } = {}) => {
30
  const defaultOptions = { force: false };
31
  const options = { ...defaultOptions, ..._options };
32
  const { force } = options;
33
 
34
  if (!force && isDetached && !get(navigating)) return;
35
 
36
+ // wait for next tick to ensure that the DOM is updated
37
+ await tick();
38
+
39
+ node.scrollTo({ top: node.scrollHeight });
40
  };
41
 
42
  node.addEventListener("scroll", handleScroll);
src/lib/components/chat/ChatMessages.svelte CHANGED
@@ -2,6 +2,8 @@
2
  import type { Message } from "$lib/types/Message";
3
  import { snapScrollToBottom } from "$lib/actions/snapScrollToBottom";
4
  import ScrollToBottomBtn from "$lib/components/ScrollToBottomBtn.svelte";
 
 
5
  import ChatIntroduction from "./ChatIntroduction.svelte";
6
  import ChatMessage from "./ChatMessage.svelte";
7
 
@@ -10,6 +12,16 @@
10
  export let pending: boolean;
11
 
12
  let chatContainer: HTMLElement;
 
 
 
 
 
 
 
 
 
 
13
  </script>
14
 
15
  <div class="overflow-y-auto h-full" use:snapScrollToBottom={messages} bind:this={chatContainer}>
 
2
  import type { Message } from "$lib/types/Message";
3
  import { snapScrollToBottom } from "$lib/actions/snapScrollToBottom";
4
  import ScrollToBottomBtn from "$lib/components/ScrollToBottomBtn.svelte";
5
+ import { tick } from "svelte";
6
+
7
  import ChatIntroduction from "./ChatIntroduction.svelte";
8
  import ChatMessage from "./ChatMessage.svelte";
9
 
 
12
  export let pending: boolean;
13
 
14
  let chatContainer: HTMLElement;
15
+
16
+ async function scrollToBottom() {
17
+ await tick();
18
+ chatContainer.scrollTop = chatContainer.scrollHeight;
19
+ }
20
+
21
+ // If last message is from user, scroll to bottom
22
+ $: if (messages.at(-1)?.from === "user") {
23
+ scrollToBottom();
24
+ }
25
  </script>
26
 
27
  <div class="overflow-y-auto h-full" use:snapScrollToBottom={messages} bind:this={chatContainer}>