fix #35: the actions don't update the status
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				/ depoly (push) Successful in 1m18s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	/ depoly (push) Successful in 1m18s
				
			This commit is contained in:
		
							parent
							
								
									17e738e21a
								
							
						
					
					
						commit
						29eaf1a02b
					
				
					 5 changed files with 114 additions and 96 deletions
				
			
		| 
						 | 
				
			
			@ -18,9 +18,9 @@ type Timeline<T extends mastodon.DefaultPaginationParams> = {
 | 
			
		|||
 | 
			
		||||
type TimelineParamsOf<T> = T extends Timeline<infer P> ? P : never;
 | 
			
		||||
 | 
			
		||||
function createControlsForLookup(
 | 
			
		||||
  lookup: ReactiveMap<string, TreeNode<mastodon.v1.Status>>,
 | 
			
		||||
) {
 | 
			
		||||
export type ThreadNode = TreeNode<mastodon.v1.Status>;
 | 
			
		||||
 | 
			
		||||
function createControlsForLookup(lookup: ReactiveMap<string, ThreadNode>) {
 | 
			
		||||
  return {
 | 
			
		||||
    get(id: string) {
 | 
			
		||||
      return lookup.get(id);
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ function createControlsForLookup(
 | 
			
		|||
    set(id: string, value: mastodon.v1.Status) {
 | 
			
		||||
      const node = untrack(() => lookup.get(id));
 | 
			
		||||
      if (!node) return;
 | 
			
		||||
      lookup.set(id, {...node, value});
 | 
			
		||||
      lookup.set(id, { ...node, value });
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ function createControlsForLookup(
 | 
			
		|||
export function createTimelineControlsForArray(
 | 
			
		||||
  status: () => mastodon.v1.Status[] | undefined,
 | 
			
		||||
): TimelineControls {
 | 
			
		||||
  const lookup = new ReactiveMap<string, TreeNode<mastodon.v1.Status>>();
 | 
			
		||||
  const lookup = new ReactiveMap<string, ThreadNode>();
 | 
			
		||||
 | 
			
		||||
  const [threads, setThreads] = createStore([] as mastodon.v1.Status["id"][]);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,22 +55,24 @@ export function createTimelineControlsForArray(
 | 
			
		|||
    });
 | 
			
		||||
    if (!nls) return;
 | 
			
		||||
 | 
			
		||||
    setThreads([]);
 | 
			
		||||
    lookup.clear();
 | 
			
		||||
    batch(() => {
 | 
			
		||||
      setThreads([]);
 | 
			
		||||
      lookup.clear();
 | 
			
		||||
 | 
			
		||||
    const existence = [] as boolean[];
 | 
			
		||||
      for (const status of nls) {
 | 
			
		||||
        lookup.set(status.id, {
 | 
			
		||||
          value: status,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    for (const [idx, status] of nls.entries()) {
 | 
			
		||||
      existence[idx] = !!untrack(() => lookup.get(status.id));
 | 
			
		||||
      lookup.set(status.id, {
 | 
			
		||||
        value: status,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    untrack(() => {
 | 
			
		||||
      for (const status of nls) {
 | 
			
		||||
        const node = lookup.get(status.id)!;
 | 
			
		||||
        const parent = status.inReplyToId
 | 
			
		||||
          ? lookup.get(status.inReplyToId)
 | 
			
		||||
          : undefined;
 | 
			
		||||
 | 
			
		||||
    for (const status of nls) {
 | 
			
		||||
      const node = untrack(() => lookup.get(status.id))!;
 | 
			
		||||
      if (status.inReplyToId) {
 | 
			
		||||
        const parent = lookup.get(status.inReplyToId);
 | 
			
		||||
        if (parent) {
 | 
			
		||||
          const children = parent.children ?? [];
 | 
			
		||||
          if (!children.find((x) => x.value.id == status.id)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -80,12 +82,13 @@ export function createTimelineControlsForArray(
 | 
			
		|||
          node.parent = parent;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const newThreads = nls
 | 
			
		||||
      .filter((x, i) => !existence[i])
 | 
			
		||||
      .map((x) => x.id)
 | 
			
		||||
      .filter((id) => (lookup.get(id)!.children?.length ?? 0) === 0);
 | 
			
		||||
    const newThreads = untrack(() =>
 | 
			
		||||
      nls
 | 
			
		||||
        .map((x) => x.id)
 | 
			
		||||
        .filter((id) => (lookup.get(id)!.children?.length ?? 0) === 0),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    setThreads(newThreads);
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -269,32 +272,34 @@ export function createTimeline<
 | 
			
		|||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (chk.rebuilt) {
 | 
			
		||||
      lookup.clear();
 | 
			
		||||
      setThreads([]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const existence = [] as boolean[];
 | 
			
		||||
 | 
			
		||||
    for (const [idx, status] of chk.chunk.entries()) {
 | 
			
		||||
      existence[idx] = !!untrack(() => lookup.get(status.id));
 | 
			
		||||
      lookup.set(status.id, {
 | 
			
		||||
        value: status,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    batch(() => {
 | 
			
		||||
      if (chk.rebuilt) {
 | 
			
		||||
        lookup.clear();
 | 
			
		||||
        setThreads([]);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (const [idx, status] of chk.chunk.entries()) {
 | 
			
		||||
        existence[idx] = !!untrack(() => lookup.get(status.id));
 | 
			
		||||
        lookup.set(status.id, {
 | 
			
		||||
          value: status,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    for (const status of chk.chunk) {
 | 
			
		||||
      const node = untrack(() => lookup.get(status.id))!;
 | 
			
		||||
      if (status.inReplyToId) {
 | 
			
		||||
        const parent = lookup.get(status.inReplyToId);
 | 
			
		||||
        if (parent) {
 | 
			
		||||
          const children = parent.children ?? [];
 | 
			
		||||
          if (!children.find((x) => x.value.id == status.id)) {
 | 
			
		||||
            children.push(node);
 | 
			
		||||
          }
 | 
			
		||||
          parent.children = children;
 | 
			
		||||
          node.parent = parent;
 | 
			
		||||
      const parent = untrack(() =>
 | 
			
		||||
        status.inReplyToId ? lookup.get(status.inReplyToId) : undefined,
 | 
			
		||||
      );
 | 
			
		||||
      if (parent) {
 | 
			
		||||
        const children = parent.children ?? [];
 | 
			
		||||
        if (!children.find((x) => x.value.id == status.id)) {
 | 
			
		||||
          children.push(node);
 | 
			
		||||
        }
 | 
			
		||||
        parent.children = children;
 | 
			
		||||
        node.parent = parent;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,9 +58,10 @@ const TootBottomSheet: Component = (props) => {
 | 
			
		|||
    },
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const toot = () => catchError(remoteToot, (error) => {
 | 
			
		||||
    console.error(error)
 | 
			
		||||
  }) ?? getCache(acctText(), params.id);
 | 
			
		||||
  const toot = () =>
 | 
			
		||||
    catchError(remoteToot, (error) => {
 | 
			
		||||
      console.error(error);
 | 
			
		||||
    }) ?? getCache(acctText(), params.id);
 | 
			
		||||
 | 
			
		||||
  createEffect((lastTootId?: string) => {
 | 
			
		||||
    const tootId = toot()?.id;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,8 @@ import RegularToot, {
 | 
			
		|||
  findRootToot,
 | 
			
		||||
} from "./RegularToot";
 | 
			
		||||
import cardStyle from "../material/cards.module.css";
 | 
			
		||||
import type { ReactiveMap } from "@solid-primitives/map";
 | 
			
		||||
import type { ThreadNode } from "../masto/timelines";
 | 
			
		||||
 | 
			
		||||
function positionTootInThread(index: number, threadLength: number) {
 | 
			
		||||
  if (index === 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +36,7 @@ const TootList: Component<{
 | 
			
		|||
  ref?: Ref<HTMLDivElement>;
 | 
			
		||||
  id?: string;
 | 
			
		||||
  threads: readonly string[];
 | 
			
		||||
  onUnknownThread: (id: string) => { value: mastodon.v1.Status }[] | undefined;
 | 
			
		||||
  onUnknownThread: (id: string) => ThreadNode[] | undefined;
 | 
			
		||||
  onChangeToot: (id: string, value: mastodon.v1.Status) => void;
 | 
			
		||||
}> = (props) => {
 | 
			
		||||
  const session = useDefaultSession();
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +73,7 @@ const TootList: Component<{
 | 
			
		|||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const result = reblogged
 | 
			
		||||
    /* const result = reblogged
 | 
			
		||||
      ? await client.v1.statuses.$select(status.id).unreblog()
 | 
			
		||||
      : (await client.v1.statuses.$select(status.id).reblog()).reblog!;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -82,13 +84,15 @@ const TootList: Component<{
 | 
			
		|||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      props.onChangeToot(status.id, result);
 | 
			
		||||
    }
 | 
			
		||||
    } */
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const toggleFavourite = async (status: mastodon.v1.Status) => {
 | 
			
		||||
    const client = session()?.client;
 | 
			
		||||
    if (!client) return;
 | 
			
		||||
    const ovalue = status.favourited;
 | 
			
		||||
    props.onChangeToot(status.id, { ...status, favourited: !ovalue });
 | 
			
		||||
 | 
			
		||||
    const result = ovalue
 | 
			
		||||
      ? await client.v1.statuses.$select(status.id).unfavourite()
 | 
			
		||||
      : await client.v1.statuses.$select(status.id).favourite();
 | 
			
		||||
| 
						 | 
				
			
			@ -170,11 +174,12 @@ const TootList: Component<{
 | 
			
		|||
  const checkIsExpended = (status: mastodon.v1.Status) =>
 | 
			
		||||
    checkIsExpendedId(status.id);
 | 
			
		||||
 | 
			
		||||
  const getPath = (itemId: string) => {
 | 
			
		||||
    return props
 | 
			
		||||
      .onUnknownThread(itemId)!
 | 
			
		||||
      .reverse()
 | 
			
		||||
      .map((x) => x.value);
 | 
			
		||||
  const reply = (
 | 
			
		||||
    status: mastodon.v1.Status,
 | 
			
		||||
    event: { currentTarget: HTMLElement },
 | 
			
		||||
  ) => {
 | 
			
		||||
    const element = findRootToot(event.currentTarget);
 | 
			
		||||
    openFullScreenToot(status, element, true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
| 
						 | 
				
			
			@ -184,38 +189,46 @@ const TootList: Component<{
 | 
			
		|||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <div ref={props.ref} id={props.id} class="toot-list">
 | 
			
		||||
        <For each={props.threads}>
 | 
			
		||||
          {(itemId) => {
 | 
			
		||||
            const toots = createMemo(() => getPath(itemId));
 | 
			
		||||
        <Index each={props.threads}>
 | 
			
		||||
          {(threadId, threadIdx) => {
 | 
			
		||||
            const thread = createMemo(() =>
 | 
			
		||||
              props.onUnknownThread(threadId())?.reverse(),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const threadLength = () => thread()?.length ?? 0;
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
              <Index each={toots()}>
 | 
			
		||||
                {(status, index) => (
 | 
			
		||||
                  <RegularToot
 | 
			
		||||
                    data-status-id={status().id}
 | 
			
		||||
                    data-thread-sort={index}
 | 
			
		||||
                    status={status()}
 | 
			
		||||
                    thread={
 | 
			
		||||
                      toots().length > 1
 | 
			
		||||
                        ? positionTootInThread(index, toots().length)
 | 
			
		||||
                        : undefined
 | 
			
		||||
                    }
 | 
			
		||||
                    class={cardStyle.card}
 | 
			
		||||
                    evaluated={checkIsExpended(status())}
 | 
			
		||||
                    actionable={checkIsExpended(status())}
 | 
			
		||||
                    onBookmark={onBookmark}
 | 
			
		||||
                    onRetoot={toggleBoost}
 | 
			
		||||
                    onFavourite={toggleFavourite}
 | 
			
		||||
                    onReply={(status, event) => {
 | 
			
		||||
                      const element = findRootToot(event.currentTarget);
 | 
			
		||||
                      openFullScreenToot(status, element, true);
 | 
			
		||||
                    }}
 | 
			
		||||
                    onClick={[onItemClick, status()]}
 | 
			
		||||
                  />
 | 
			
		||||
                )}
 | 
			
		||||
              <Index each={thread()}>
 | 
			
		||||
                {(threadNode, index) => {
 | 
			
		||||
                  const status = () => threadNode().value;
 | 
			
		||||
 | 
			
		||||
                  return (
 | 
			
		||||
                    <RegularToot
 | 
			
		||||
                      data-status-id={status().id}
 | 
			
		||||
                      data-thread={threadIdx}
 | 
			
		||||
                      data-thread-len={threadLength()}
 | 
			
		||||
                      data-thread-sort={index}
 | 
			
		||||
                      status={status()}
 | 
			
		||||
                      thread={
 | 
			
		||||
                        threadLength() > 1
 | 
			
		||||
                          ? positionTootInThread(index, threadLength())
 | 
			
		||||
                          : undefined
 | 
			
		||||
                      }
 | 
			
		||||
                      class={cardStyle.card}
 | 
			
		||||
                      evaluated={checkIsExpended(status())}
 | 
			
		||||
                      actionable={checkIsExpended(status())}
 | 
			
		||||
                      onBookmark={onBookmark}
 | 
			
		||||
                      onRetoot={toggleBoost}
 | 
			
		||||
                      onFavourite={toggleFavourite}
 | 
			
		||||
                      onReply={reply}
 | 
			
		||||
                      onClick={[onItemClick, status()]}
 | 
			
		||||
                    />
 | 
			
		||||
                  );
 | 
			
		||||
                }}
 | 
			
		||||
              </Index>
 | 
			
		||||
            );
 | 
			
		||||
          }}
 | 
			
		||||
        </For>
 | 
			
		||||
        </Index>
 | 
			
		||||
      </div>
 | 
			
		||||
    </ErrorBoundary>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,14 @@
 | 
			
		|||
import {
 | 
			
		||||
  Component,
 | 
			
		||||
  For,
 | 
			
		||||
  createSignal,
 | 
			
		||||
  Match,
 | 
			
		||||
  Switch as JsSwitch,
 | 
			
		||||
  ErrorBoundary,
 | 
			
		||||
  createSelector,
 | 
			
		||||
} from "solid-js";
 | 
			
		||||
import { type mastodon } from "masto";
 | 
			
		||||
import { Button } from "@suid/material";
 | 
			
		||||
import { createTimelineSnapshot } from "../masto/timelines.js";
 | 
			
		||||
import { vibrate } from "../platform/hardware.js";
 | 
			
		||||
import PullDownToRefresh from "./PullDownToRefresh.jsx";
 | 
			
		||||
import Thread from "./Thread.jsx";
 | 
			
		||||
import TootList from "./TootList.jsx";
 | 
			
		||||
 | 
			
		||||
const TrendTimelinePanel: Component<{
 | 
			
		||||
| 
						 | 
				
			
			@ -25,11 +21,10 @@ const TrendTimelinePanel: Component<{
 | 
			
		|||
  ) => void;
 | 
			
		||||
}> = (props) => {
 | 
			
		||||
  const [scrollLinked, setScrollLinked] = createSignal<HTMLElement>();
 | 
			
		||||
  const [timeline, snapshot, { refetch: refetchTimeline }] =
 | 
			
		||||
    createTimelineSnapshot(
 | 
			
		||||
      () => props.client.v1.trends.statuses,
 | 
			
		||||
      () => ({ limit: 120 }),
 | 
			
		||||
    );
 | 
			
		||||
  const [tl, snapshot, { refetch: refetchTimeline }] = createTimelineSnapshot(
 | 
			
		||||
    () => props.client.v1.trends.statuses,
 | 
			
		||||
    () => ({ limit: 120 }),
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ErrorBoundary
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +45,9 @@ const TrendTimelinePanel: Component<{
 | 
			
		|||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <TootList
 | 
			
		||||
          threads={timeline.list}
 | 
			
		||||
          onUnknownThread={timeline.getPath}
 | 
			
		||||
          onChangeToot={timeline.set}
 | 
			
		||||
          threads={tl.list}
 | 
			
		||||
          onUnknownThread={tl.getPath}
 | 
			
		||||
          onChangeToot={tl.set}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,8 +5,12 @@
 | 
			
		|||
  margin-block: 0;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  contain: content;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
 | 
			
		||||
  &:not(.expanded) {
 | 
			
		||||
    user-select: none;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  &.toot {
 | 
			
		||||
    /* fix composition ordering: I think the css module processor should aware the overriding and behaves, but no */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue