mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	chore: mobile export image style
This commit is contained in:
		@@ -3,7 +3,7 @@ import Locale from "../locales";
 | 
			
		||||
import styles from "./exporter.module.scss";
 | 
			
		||||
import { List, ListItem, Modal, showToast } from "./ui-lib";
 | 
			
		||||
import { IconButton } from "./button";
 | 
			
		||||
import { copyToClipboard, downloadAs } from "../utils";
 | 
			
		||||
import { copyToClipboard, downloadAs, useMobileScreen } from "../utils";
 | 
			
		||||
 | 
			
		||||
import CopyIcon from "../icons/copy.svg";
 | 
			
		||||
import LoadingIcon from "../icons/three-dots.svg";
 | 
			
		||||
@@ -222,16 +222,19 @@ export function MessageExporter() {
 | 
			
		||||
export function PreviewActions(props: {
 | 
			
		||||
  download: () => void;
 | 
			
		||||
  copy: () => void;
 | 
			
		||||
  showCopy?: boolean;
 | 
			
		||||
}) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles["preview-actions"]}>
 | 
			
		||||
      <IconButton
 | 
			
		||||
        text={Locale.Export.Copy}
 | 
			
		||||
        bordered
 | 
			
		||||
        shadow
 | 
			
		||||
        icon={<CopyIcon />}
 | 
			
		||||
        onClick={props.copy}
 | 
			
		||||
      ></IconButton>
 | 
			
		||||
      {props.showCopy && (
 | 
			
		||||
        <IconButton
 | 
			
		||||
          text={Locale.Export.Copy}
 | 
			
		||||
          bordered
 | 
			
		||||
          shadow
 | 
			
		||||
          icon={<CopyIcon />}
 | 
			
		||||
          onClick={props.copy}
 | 
			
		||||
        ></IconButton>
 | 
			
		||||
      )}
 | 
			
		||||
      <IconButton
 | 
			
		||||
        text={Locale.Export.Download}
 | 
			
		||||
        bordered
 | 
			
		||||
@@ -282,23 +285,34 @@ export function ImagePreviewer(props: {
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const isMobile = useMobileScreen();
 | 
			
		||||
 | 
			
		||||
  const download = () => {
 | 
			
		||||
    const dom = previewRef.current;
 | 
			
		||||
    if (!dom) return;
 | 
			
		||||
    toPng(dom)
 | 
			
		||||
      .then((blob) => {
 | 
			
		||||
        if (!blob) return;
 | 
			
		||||
        const link = document.createElement("a");
 | 
			
		||||
        link.download = `${props.topic}.png`;
 | 
			
		||||
        link.href = blob;
 | 
			
		||||
        link.click();
 | 
			
		||||
 | 
			
		||||
        if (isMobile) {
 | 
			
		||||
          const image = new Image();
 | 
			
		||||
          image.src = blob;
 | 
			
		||||
          const win = window.open("");
 | 
			
		||||
          win?.document.write(image.outerHTML);
 | 
			
		||||
        } else {
 | 
			
		||||
          const link = document.createElement("a");
 | 
			
		||||
          link.download = `${props.topic}.png`;
 | 
			
		||||
          link.href = blob;
 | 
			
		||||
          link.click();
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch((e) => console.log("[Export Image] ", e));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles["image-previewer"]}>
 | 
			
		||||
      <PreviewActions copy={copy} download={download} />
 | 
			
		||||
      <PreviewActions copy={copy} download={download} showCopy={!isMobile} />
 | 
			
		||||
      <div
 | 
			
		||||
        className={`${styles["preview-body"]} ${styles["default-theme"]}`}
 | 
			
		||||
        ref={previewRef}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,31 @@
 | 
			
		||||
    .search-bar {
 | 
			
		||||
      max-width: unset;
 | 
			
		||||
      flex-grow: 1;
 | 
			
		||||
      margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .filter-item:not(:last-child) {
 | 
			
		||||
      margin-right: 10px;
 | 
			
		||||
    .actions {
 | 
			
		||||
      display: flex;
 | 
			
		||||
 | 
			
		||||
      button:not(:last-child) {
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media screen and (max-width: 600px) {
 | 
			
		||||
      flex-direction: column;
 | 
			
		||||
 | 
			
		||||
      .search-bar {
 | 
			
		||||
        margin-right: 0;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .actions {
 | 
			
		||||
        margin-top: 20px;
 | 
			
		||||
 | 
			
		||||
        button {
 | 
			
		||||
          flex-grow: 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -140,33 +140,35 @@ export function MessageSelector(props: {
 | 
			
		||||
          }}
 | 
			
		||||
        ></input>
 | 
			
		||||
 | 
			
		||||
        <IconButton
 | 
			
		||||
          text={Locale.Select.All}
 | 
			
		||||
          bordered
 | 
			
		||||
          className={styles["filter-item"]}
 | 
			
		||||
          onClick={selectAll}
 | 
			
		||||
        />
 | 
			
		||||
        <IconButton
 | 
			
		||||
          text={Locale.Select.Latest}
 | 
			
		||||
          bordered
 | 
			
		||||
          className={styles["filter-item"]}
 | 
			
		||||
          onClick={() =>
 | 
			
		||||
            props.updateSelection((selection) => {
 | 
			
		||||
              selection.clear();
 | 
			
		||||
              messages
 | 
			
		||||
                .slice(messageCount - 10)
 | 
			
		||||
                .forEach((m) => selection.add(m.id!));
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        />
 | 
			
		||||
        <IconButton
 | 
			
		||||
          text={Locale.Select.Clear}
 | 
			
		||||
          bordered
 | 
			
		||||
          className={styles["filter-item"]}
 | 
			
		||||
          onClick={() =>
 | 
			
		||||
            props.updateSelection((selection) => selection.clear())
 | 
			
		||||
          }
 | 
			
		||||
        />
 | 
			
		||||
        <div className={styles["actions"]}>
 | 
			
		||||
          <IconButton
 | 
			
		||||
            text={Locale.Select.All}
 | 
			
		||||
            bordered
 | 
			
		||||
            className={styles["filter-item"]}
 | 
			
		||||
            onClick={selectAll}
 | 
			
		||||
          />
 | 
			
		||||
          <IconButton
 | 
			
		||||
            text={Locale.Select.Latest}
 | 
			
		||||
            bordered
 | 
			
		||||
            className={styles["filter-item"]}
 | 
			
		||||
            onClick={() =>
 | 
			
		||||
              props.updateSelection((selection) => {
 | 
			
		||||
                selection.clear();
 | 
			
		||||
                messages
 | 
			
		||||
                  .slice(messageCount - 10)
 | 
			
		||||
                  .forEach((m) => selection.add(m.id!));
 | 
			
		||||
              })
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
          <IconButton
 | 
			
		||||
            text={Locale.Select.Clear}
 | 
			
		||||
            bordered
 | 
			
		||||
            className={styles["filter-item"]}
 | 
			
		||||
            onClick={() =>
 | 
			
		||||
              props.updateSelection((selection) => selection.clear())
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div className={styles["messages"]}>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user