ReplyEditor: added only UI
This commit is contained in:
		
							parent
							
								
									6463da68ae
								
							
						
					
					
						commit
						6c98f1e78d
					
				
					 6 changed files with 363 additions and 73 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
.bottomSheet {
 | 
			
		||||
  composes: surface from "material.module.css";
 | 
			
		||||
  composes: surface from "./material.module.css";
 | 
			
		||||
  composes: cardGutSkip from "./cards.module.css";
 | 
			
		||||
  composes: cardNoPad from "./cards.module.css";
 | 
			
		||||
  border: none;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
| 
						 | 
				
			
			@ -47,4 +49,10 @@
 | 
			
		|||
      opacity: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.bottom {
 | 
			
		||||
    top: unset;
 | 
			
		||||
    transform: translateX(-50%);
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,9 +13,12 @@ import {
 | 
			
		|||
} from "solid-js";
 | 
			
		||||
import styles from "./BottomSheet.module.css";
 | 
			
		||||
import { useHeroSignal } from "../platform/anim";
 | 
			
		||||
import { makeEventListener } from "@solid-primitives/event-listener";
 | 
			
		||||
 | 
			
		||||
export type BottomSheetProps = {
 | 
			
		||||
  open?: boolean;
 | 
			
		||||
  bottomUp?: boolean;
 | 
			
		||||
  onClose?(reason: "backdrop"): void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const HERO = Symbol("BottomSheet Hero Symbol");
 | 
			
		||||
| 
						 | 
				
			
			@ -123,8 +126,28 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
 | 
			
		|||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  onMount(() => {
 | 
			
		||||
    makeEventListener(element, "click", (event) => {
 | 
			
		||||
      const rect = element.getBoundingClientRect();
 | 
			
		||||
      const isInDialog =
 | 
			
		||||
        rect.top <= event.clientY &&
 | 
			
		||||
        event.clientY <= rect.top + rect.height &&
 | 
			
		||||
        rect.left <= event.clientX &&
 | 
			
		||||
        event.clientX <= rect.left + rect.width;
 | 
			
		||||
      if (!isInDialog) {
 | 
			
		||||
        props.onClose?.("backdrop");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <dialog class={styles.bottomSheet} ref={element!}>
 | 
			
		||||
    <dialog
 | 
			
		||||
      classList={{
 | 
			
		||||
        [styles.bottomSheet]: true,
 | 
			
		||||
        [styles.bottom]: props.bottomUp,
 | 
			
		||||
      }}
 | 
			
		||||
      ref={element!}
 | 
			
		||||
    >
 | 
			
		||||
      {ochildren() ?? cache()}
 | 
			
		||||
    </dialog>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import { css } from "solid-styled";
 | 
			
		|||
interface ScaffoldProps {
 | 
			
		||||
  topbar?: JSX.Element;
 | 
			
		||||
  fab?: JSX.Element;
 | 
			
		||||
  bottom?: JSX.Element;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Scaffold: ParentComponent<ScaffoldProps> = (props) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +37,16 @@ const Scaffold: ParentComponent<ScaffoldProps> = (props) => {
 | 
			
		|||
      right: 40px;
 | 
			
		||||
      z-index: var(--tutu-zidx-nav, auto);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .bottom-dock {
 | 
			
		||||
      position: sticky;
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      right: 0;
 | 
			
		||||
      z-index: var(--tutu-zidx-nav, auto);
 | 
			
		||||
      padding-bottom: var(--safe-area-inset-bottom, 0);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  `;
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +59,9 @@ const Scaffold: ParentComponent<ScaffoldProps> = (props) => {
 | 
			
		|||
        <div class="fab-dock">{props.fab}</div>
 | 
			
		||||
      </Show>
 | 
			
		||||
      <div class="scaffold-content">{props.children}</div>
 | 
			
		||||
      <Show when={props.bottom}>
 | 
			
		||||
        <div class="bottom-dock">{props.bottom}</div>
 | 
			
		||||
      </Show>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										236
									
								
								src/timelines/ReplyEditor.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/timelines/ReplyEditor.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,236 @@
 | 
			
		|||
import {
 | 
			
		||||
  createSignal,
 | 
			
		||||
  createUniqueId,
 | 
			
		||||
  onMount,
 | 
			
		||||
  type Component,
 | 
			
		||||
  type Setter,
 | 
			
		||||
} from "solid-js";
 | 
			
		||||
import Scaffold from "../material/Scaffold";
 | 
			
		||||
import {
 | 
			
		||||
  Avatar,
 | 
			
		||||
  Button,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  List,
 | 
			
		||||
  ListItemButton,
 | 
			
		||||
  ListItemIcon,
 | 
			
		||||
  ListItemSecondaryAction,
 | 
			
		||||
  ListItemText,
 | 
			
		||||
  Radio,
 | 
			
		||||
  Switch,
 | 
			
		||||
  Divider,
 | 
			
		||||
} from "@suid/material";
 | 
			
		||||
import {
 | 
			
		||||
  ArrowDropDown,
 | 
			
		||||
  Public as PublicIcon,
 | 
			
		||||
  Send,
 | 
			
		||||
  People as PeopleIcon,
 | 
			
		||||
  ThreeP as ThreePIcon,
 | 
			
		||||
  ListAlt as ListAltIcon,
 | 
			
		||||
} from "@suid/icons-material";
 | 
			
		||||
import type { Account } from "../accounts/stores";
 | 
			
		||||
import tootComposers from "./TootComposer.module.css";
 | 
			
		||||
import { makeEventListener } from "@solid-primitives/event-listener";
 | 
			
		||||
import BottomSheet from "../material/BottomSheet";
 | 
			
		||||
 | 
			
		||||
type TootVisibility = "public" | "unlisted" | "private" | "direct";
 | 
			
		||||
 | 
			
		||||
const TootVisibilityPickerDialog: Component<{
 | 
			
		||||
  open?: boolean;
 | 
			
		||||
  onClose: () => void;
 | 
			
		||||
  visibility: TootVisibility;
 | 
			
		||||
  onVisibilityChange: (value: TootVisibility) => void;
 | 
			
		||||
}> = (props) => {
 | 
			
		||||
  type Kind = "public" | "private" | "direct";
 | 
			
		||||
 | 
			
		||||
  const kind = () =>
 | 
			
		||||
    props.visibility === "public" || props.visibility === "unlisted"
 | 
			
		||||
      ? "public"
 | 
			
		||||
      : props.visibility;
 | 
			
		||||
 | 
			
		||||
  const setKind = (nv: Kind) => {
 | 
			
		||||
    if (nv == "public") {
 | 
			
		||||
      props.onVisibilityChange(discoverable() ? "public" : "unlisted");
 | 
			
		||||
    } else {
 | 
			
		||||
      props.onVisibilityChange(nv);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const discoverable = () => {
 | 
			
		||||
    return props.visibility === "public";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const setDiscoverable = (setter: (v: boolean) => boolean) => {
 | 
			
		||||
    const nval = setter(discoverable())
 | 
			
		||||
    props.onVisibilityChange(nval ? "public" : "unlisted"); // trigger change
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <BottomSheet open={props.open} onClose={props.onClose} bottomUp>
 | 
			
		||||
      <Scaffold
 | 
			
		||||
        bottom={
 | 
			
		||||
          <div
 | 
			
		||||
            style={{
 | 
			
		||||
              "border-top": "1px solid #ddd",
 | 
			
		||||
              background: "var(--tutu-color-surface)",
 | 
			
		||||
              padding: "8px 16px",
 | 
			
		||||
              width: "100%",
 | 
			
		||||
              "text-align": "end",
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Button onClick={props.onClose}>Confirm</Button>
 | 
			
		||||
          </div>
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <List>
 | 
			
		||||
          <ListItemButton onClick={[setKind, "public"]}>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <PublicIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText
 | 
			
		||||
              primary="Public"
 | 
			
		||||
              secondary="Everyone can see this toot"
 | 
			
		||||
            ></ListItemText>
 | 
			
		||||
            <ListItemSecondaryAction>
 | 
			
		||||
              <Radio checked={kind() == "public"}></Radio>
 | 
			
		||||
            </ListItemSecondaryAction>
 | 
			
		||||
          </ListItemButton>
 | 
			
		||||
 | 
			
		||||
          <ListItemButton
 | 
			
		||||
            sx={{ paddingLeft: "40px" }}
 | 
			
		||||
            disabled={kind() !== "public"}
 | 
			
		||||
            onClick={() => setDiscoverable((x) => !x)}
 | 
			
		||||
          >
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <ListAltIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText
 | 
			
		||||
              primary="Discoverable"
 | 
			
		||||
              secondary="The others can discover it on the exploration."
 | 
			
		||||
            ></ListItemText>
 | 
			
		||||
            <ListItemSecondaryAction>
 | 
			
		||||
              <Switch
 | 
			
		||||
                checked={discoverable()}
 | 
			
		||||
                disabled={kind() !== "public"}
 | 
			
		||||
              ></Switch>
 | 
			
		||||
            </ListItemSecondaryAction>
 | 
			
		||||
          </ListItemButton>
 | 
			
		||||
 | 
			
		||||
          <Divider />
 | 
			
		||||
 | 
			
		||||
          <ListItemButton onClick={[setKind, "private"]}>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <PeopleIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText
 | 
			
		||||
              primary="Only Followers"
 | 
			
		||||
              secondary="Visibile for followers only"
 | 
			
		||||
            ></ListItemText>
 | 
			
		||||
            <ListItemSecondaryAction>
 | 
			
		||||
              <Radio checked={kind() == "private"}></Radio>
 | 
			
		||||
            </ListItemSecondaryAction>
 | 
			
		||||
          </ListItemButton>
 | 
			
		||||
 | 
			
		||||
          <Divider />
 | 
			
		||||
 | 
			
		||||
          <ListItemButton onClick={[setKind, "direct"]}>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <ThreePIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText
 | 
			
		||||
              primary="Only Mentions"
 | 
			
		||||
              secondary="Visible for mentioned users only"
 | 
			
		||||
            ></ListItemText>
 | 
			
		||||
            <ListItemSecondaryAction>
 | 
			
		||||
              <Radio checked={kind() == "direct"}></Radio>
 | 
			
		||||
            </ListItemSecondaryAction>
 | 
			
		||||
          </ListItemButton>
 | 
			
		||||
        </List>
 | 
			
		||||
      </Scaffold>
 | 
			
		||||
    </BottomSheet>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ReplyEditor: Component<{
 | 
			
		||||
  profile: Account;
 | 
			
		||||
  replyToDisplayName: string;
 | 
			
		||||
}> = (props) => {
 | 
			
		||||
  let inputRef: HTMLTextAreaElement;
 | 
			
		||||
  const buttonId = createUniqueId();
 | 
			
		||||
  const menuId = createUniqueId();
 | 
			
		||||
 | 
			
		||||
  const [typing, setTyping] = createSignal(false);
 | 
			
		||||
  const [visibility, setVisibility] = createSignal<TootVisibility>("public");
 | 
			
		||||
  const [permPicker, setPermPicker] = createSignal(false);
 | 
			
		||||
 | 
			
		||||
  onMount(() => {
 | 
			
		||||
    makeEventListener(inputRef, "focus", () => setTyping(true));
 | 
			
		||||
    makeEventListener(inputRef, "blur", () => setTyping(false));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const containerStyle = () =>
 | 
			
		||||
    typing()
 | 
			
		||||
      ? {
 | 
			
		||||
          position: "sticky" as const,
 | 
			
		||||
          top: "var(--scaffold-topbar-height, 0)",
 | 
			
		||||
          bottom: "var(--safe-area-inset-bottom, 0)",
 | 
			
		||||
          "z-index": 1,
 | 
			
		||||
        }
 | 
			
		||||
      : undefined;
 | 
			
		||||
 | 
			
		||||
  const visibilityText = () => {
 | 
			
		||||
    switch (visibility()) {
 | 
			
		||||
      case "public":
 | 
			
		||||
        return "Discoverable";
 | 
			
		||||
      case "unlisted":
 | 
			
		||||
        return "Public";
 | 
			
		||||
      case "private":
 | 
			
		||||
        return "Only Followers";
 | 
			
		||||
      case "direct":
 | 
			
		||||
        return "Only Mentions";
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      class={tootComposers.composer}
 | 
			
		||||
      style={containerStyle()}
 | 
			
		||||
      onClick={() => setTyping(true)}
 | 
			
		||||
    >
 | 
			
		||||
      <div class={tootComposers.replyInput}>
 | 
			
		||||
        <Avatar src={props.profile.inf?.avatar} />
 | 
			
		||||
        <textarea
 | 
			
		||||
          ref={inputRef!}
 | 
			
		||||
          placeholder={`Reply to ${props.replyToDisplayName}...`}
 | 
			
		||||
          style={{ width: "100%", border: "none" }}
 | 
			
		||||
        ></textarea>
 | 
			
		||||
        <IconButton>
 | 
			
		||||
          <Send />
 | 
			
		||||
        </IconButton>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div
 | 
			
		||||
        style={{
 | 
			
		||||
          display: "flex",
 | 
			
		||||
          "justify-content": "flex-end",
 | 
			
		||||
          "margin-top": "8px",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Button onClick={[setPermPicker, true]} id={buttonId}>
 | 
			
		||||
          {visibilityText()}
 | 
			
		||||
          <ArrowDropDown />
 | 
			
		||||
        </Button>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <TootVisibilityPickerDialog
 | 
			
		||||
        open={permPicker()}
 | 
			
		||||
        onClose={() => setPermPicker(false)}
 | 
			
		||||
        visibility={visibility()}
 | 
			
		||||
        onVisibilityChange={setVisibility}
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ReplyEditor;
 | 
			
		||||
| 
						 | 
				
			
			@ -8,12 +8,16 @@ import {
 | 
			
		|||
  type Component,
 | 
			
		||||
} from "solid-js";
 | 
			
		||||
import Scaffold from "../material/Scaffold";
 | 
			
		||||
import { AppBar, Avatar, CircularProgress, IconButton, Toolbar } from "@suid/material";
 | 
			
		||||
import {
 | 
			
		||||
  AppBar,
 | 
			
		||||
  CircularProgress,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  Toolbar,
 | 
			
		||||
} from "@suid/material";
 | 
			
		||||
import { Title } from "../material/typography";
 | 
			
		||||
import {
 | 
			
		||||
  ArrowBack as BackIcon,
 | 
			
		||||
  Close as CloseIcon,
 | 
			
		||||
  Send,
 | 
			
		||||
} from "@suid/icons-material";
 | 
			
		||||
import { createUnauthorizedClient, useSessions } from "../masto/clients";
 | 
			
		||||
import { resolveCustomEmoji } from "../masto/toot";
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +26,8 @@ import type { mastodon } from "masto";
 | 
			
		|||
import cards from "../material/cards.module.css";
 | 
			
		||||
import { css } from "solid-styled";
 | 
			
		||||
import { vibrate } from "../platform/hardware";
 | 
			
		||||
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
 | 
			
		||||
import ReplyEditor from "./ReplyEditor";
 | 
			
		||||
 | 
			
		||||
let cachedEntry: [string, mastodon.v1.Status] | undefined;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,11 +41,14 @@ function getCache(acct: string, id: string) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const TootBottomSheet: Component = (props) => {
 | 
			
		||||
  const params = useParams<{ acct: string; id: string }>();
 | 
			
		||||
  const location = useLocation<{ tootBottomSheetPushedCount?: number }>();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const allSession = useSessions();
 | 
			
		||||
  const time = createTimeSource();
 | 
			
		||||
  const acctText = () => decodeURIComponent(params.acct);
 | 
			
		||||
  const session = () => {
 | 
			
		||||
    const [inputUsername, inputSite] = acctText().split("@", 2);
 | 
			
		||||
| 
						 | 
				
			
			@ -164,12 +173,6 @@ const TootBottomSheet: Component = (props) => {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  css`
 | 
			
		||||
    .bottom-dock {
 | 
			
		||||
      position: sticky;
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      z-index: var(--tutu-zidx-nav);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .name :global(img) {
 | 
			
		||||
      max-height: 1em;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -203,74 +206,69 @@ const TootBottomSheet: Component = (props) => {
 | 
			
		|||
        </AppBar>
 | 
			
		||||
      }
 | 
			
		||||
    >
 | 
			
		||||
      <For each={ancestors()}>
 | 
			
		||||
        {(item) => (
 | 
			
		||||
          <RegularToot
 | 
			
		||||
            id={`toot-${item.id}`}
 | 
			
		||||
            class={cards.card}
 | 
			
		||||
            status={item}
 | 
			
		||||
            actionable={false}
 | 
			
		||||
            onClick={[switchContext, item]}
 | 
			
		||||
          ></RegularToot>
 | 
			
		||||
        )}
 | 
			
		||||
      </For>
 | 
			
		||||
      <TimeSourceProvider value={time}>
 | 
			
		||||
        <For each={ancestors()}>
 | 
			
		||||
          {(item) => (
 | 
			
		||||
            <RegularToot
 | 
			
		||||
              id={`toot-${item.id}`}
 | 
			
		||||
              class={cards.card}
 | 
			
		||||
              status={item}
 | 
			
		||||
              actionable={false}
 | 
			
		||||
              onClick={[switchContext, item]}
 | 
			
		||||
            ></RegularToot>
 | 
			
		||||
          )}
 | 
			
		||||
        </For>
 | 
			
		||||
 | 
			
		||||
      <article>
 | 
			
		||||
        <Show when={toot()}>
 | 
			
		||||
          <RegularToot
 | 
			
		||||
            id={`toot-${toot()!.id}`}
 | 
			
		||||
            class={cards.card}
 | 
			
		||||
            style={{
 | 
			
		||||
              "scroll-margin-top": "calc(var(--scaffold-topbar-height) + 20px)",
 | 
			
		||||
            }}
 | 
			
		||||
            status={toot()!}
 | 
			
		||||
            actionable={!!actSession()}
 | 
			
		||||
            evaluated={true}
 | 
			
		||||
            onBookmark={onBookmark}
 | 
			
		||||
            onRetoot={onBoost}
 | 
			
		||||
            onFavourite={onFav}
 | 
			
		||||
          ></RegularToot>
 | 
			
		||||
        </Show>
 | 
			
		||||
      </article>
 | 
			
		||||
        <article>
 | 
			
		||||
          <Show when={toot()}>
 | 
			
		||||
            <RegularToot
 | 
			
		||||
              id={`toot-${toot()!.id}`}
 | 
			
		||||
              class={cards.card}
 | 
			
		||||
              style={{
 | 
			
		||||
                "scroll-margin-top":
 | 
			
		||||
                  "calc(var(--scaffold-topbar-height) + 20px)",
 | 
			
		||||
              }}
 | 
			
		||||
              status={toot()!}
 | 
			
		||||
              actionable={!!actSession()}
 | 
			
		||||
              evaluated={true}
 | 
			
		||||
              onBookmark={onBookmark}
 | 
			
		||||
              onRetoot={onBoost}
 | 
			
		||||
              onFavourite={onFav}
 | 
			
		||||
            ></RegularToot>
 | 
			
		||||
          </Show>
 | 
			
		||||
        </article>
 | 
			
		||||
 | 
			
		||||
      <Show when={tootContext.loading}>
 | 
			
		||||
        <div
 | 
			
		||||
          style={{
 | 
			
		||||
            display: "flex",
 | 
			
		||||
            "justify-content": "center",
 | 
			
		||||
            "margin-block": "12px",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <CircularProgress style="width: 1.5em; height: 1.5em;" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </Show>
 | 
			
		||||
 | 
			
		||||
      <For each={descendants()}>
 | 
			
		||||
        {(item) => (
 | 
			
		||||
          <RegularToot
 | 
			
		||||
            id={`toot-${item.id}`}
 | 
			
		||||
            class={cards.card}
 | 
			
		||||
            status={item}
 | 
			
		||||
            actionable={false}
 | 
			
		||||
            onClick={[switchContext, item]}
 | 
			
		||||
          ></RegularToot>
 | 
			
		||||
        )}
 | 
			
		||||
      </For>
 | 
			
		||||
 | 
			
		||||
      <div class="bottom-dock">
 | 
			
		||||
        <Show when={profile()}>
 | 
			
		||||
          <div style="display: flex; gap: 8px; background: var(--tutu-color-surface); padding: 8px 8px calc(var(--safe-area-inset-bottom, 0px) + 8px);">
 | 
			
		||||
            <Avatar src={profile()!.inf?.avatar} />
 | 
			
		||||
            <textarea
 | 
			
		||||
              placeholder={`Reply to ${toot()?.account?.displayName ?? ""}...`}
 | 
			
		||||
              style={{ width: "100%", border: "none" }}
 | 
			
		||||
            ></textarea>
 | 
			
		||||
            <IconButton>
 | 
			
		||||
              <Send />
 | 
			
		||||
            </IconButton>
 | 
			
		||||
          <ReplyEditor
 | 
			
		||||
            profile={profile()!}
 | 
			
		||||
            replyToDisplayName={toot()?.account?.displayName || ""}
 | 
			
		||||
          />
 | 
			
		||||
        </Show>
 | 
			
		||||
 | 
			
		||||
        <Show when={tootContext.loading}>
 | 
			
		||||
          <div
 | 
			
		||||
            style={{
 | 
			
		||||
              display: "flex",
 | 
			
		||||
              "justify-content": "center",
 | 
			
		||||
              "margin-block": "12px",
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <CircularProgress style="width: 1.5em; height: 1.5em;" />
 | 
			
		||||
          </div>
 | 
			
		||||
        </Show>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
        <For each={descendants()}>
 | 
			
		||||
          {(item) => (
 | 
			
		||||
            <RegularToot
 | 
			
		||||
              id={`toot-${item.id}`}
 | 
			
		||||
              class={cards.card}
 | 
			
		||||
              status={item}
 | 
			
		||||
              actionable={false}
 | 
			
		||||
              onClick={[switchContext, item]}
 | 
			
		||||
            ></RegularToot>
 | 
			
		||||
          )}
 | 
			
		||||
        </For>
 | 
			
		||||
      </TimeSourceProvider>
 | 
			
		||||
    </Scaffold>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								src/timelines/TootComposer.module.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/timelines/TootComposer.module.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
 | 
			
		||||
.composer {
 | 
			
		||||
  composes: card from "../material/cards.module.css";
 | 
			
		||||
  --card-gut: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.replyInput {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: flex-start;
 | 
			
		||||
  gap: 8px;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue