Commit 7d5f6034 authored by Steven's avatar Steven

chore: update compact view

parent f0a521f5
...@@ -161,8 +161,6 @@ message UserSetting { ...@@ -161,8 +161,6 @@ message UserSetting {
string memo_visibility = 4; string memo_visibility = 4;
// The telegram user id of the user. // The telegram user id of the user.
string telegram_user_id = 5; string telegram_user_id = 5;
// The compact view for a memo.
bool compact_view = 6;
} }
message GetUserSettingRequest { message GetUserSettingRequest {
......
...@@ -718,7 +718,6 @@ Used internally for obfuscating the page token. ...@@ -718,7 +718,6 @@ Used internally for obfuscating the page token.
| appearance | [string](#string) | | The preferred appearance of the user. | | appearance | [string](#string) | | The preferred appearance of the user. |
| memo_visibility | [string](#string) | | The default visibility of the memo. | | memo_visibility | [string](#string) | | The default visibility of the memo. |
| telegram_user_id | [string](#string) | | The telegram user id of the user. | | telegram_user_id | [string](#string) | | The telegram user id of the user. |
| compact_view | [bool](#bool) | | The compact view for a memo. |
......
This diff is collapsed.
...@@ -289,7 +289,6 @@ ...@@ -289,7 +289,6 @@
| appearance | [string](#string) | | | | appearance | [string](#string) | | |
| memo_visibility | [string](#string) | | | | memo_visibility | [string](#string) | | |
| telegram_user_id | [string](#string) | | | | telegram_user_id | [string](#string) | | |
| compact_view | [bool](#bool) | | |
...@@ -311,7 +310,6 @@ ...@@ -311,7 +310,6 @@
| USER_SETTING_APPEARANCE | 3 | The appearance of the user. | | USER_SETTING_APPEARANCE | 3 | The appearance of the user. |
| USER_SETTING_MEMO_VISIBILITY | 4 | The visibility of the memo. | | USER_SETTING_MEMO_VISIBILITY | 4 | The visibility of the memo. |
| USER_SETTING_TELEGRAM_USER_ID | 5 | The telegram user id of the user. | | USER_SETTING_TELEGRAM_USER_ID | 5 | The telegram user id of the user. |
| USER_SETTING_COMPACT_VIEW | 6 | The compact view for a memo. |
......
This diff is collapsed.
...@@ -16,8 +16,6 @@ enum UserSettingKey { ...@@ -16,8 +16,6 @@ enum UserSettingKey {
USER_SETTING_MEMO_VISIBILITY = 4; USER_SETTING_MEMO_VISIBILITY = 4;
// The telegram user id of the user. // The telegram user id of the user.
USER_SETTING_TELEGRAM_USER_ID = 5; USER_SETTING_TELEGRAM_USER_ID = 5;
// The compact view for a memo.
USER_SETTING_COMPACT_VIEW = 6;
} }
message UserSetting { message UserSetting {
...@@ -29,7 +27,6 @@ message UserSetting { ...@@ -29,7 +27,6 @@ message UserSetting {
string appearance = 5; string appearance = 5;
string memo_visibility = 6; string memo_visibility = 6;
string telegram_user_id = 7; string telegram_user_id = 7;
bool compact_view = 8;
} }
} }
......
...@@ -1437,9 +1437,6 @@ paths: ...@@ -1437,9 +1437,6 @@ paths:
telegramUserId: telegramUserId:
type: string type: string
description: The telegram user id of the user. description: The telegram user id of the user.
compactView:
type: boolean
description: The compact view for a memo.
tags: tags:
- UserService - UserService
/api/v2/{user.name}: /api/v2/{user.name}:
...@@ -1626,9 +1623,6 @@ definitions: ...@@ -1626,9 +1623,6 @@ definitions:
telegramUserId: telegramUserId:
type: string type: string
description: The telegram user id of the user. description: The telegram user id of the user.
compactView:
type: boolean
description: The compact view for a memo.
apiv2Webhook: apiv2Webhook:
type: object type: object
properties: properties:
......
...@@ -219,7 +219,6 @@ func getDefaultUserSetting() *apiv2pb.UserSetting { ...@@ -219,7 +219,6 @@ func getDefaultUserSetting() *apiv2pb.UserSetting {
Locale: "en", Locale: "en",
Appearance: "system", Appearance: "system",
MemoVisibility: "PRIVATE", MemoVisibility: "PRIVATE",
CompactView: false,
} }
} }
...@@ -245,8 +244,6 @@ func (s *APIV2Service) GetUserSetting(ctx context.Context, _ *apiv2pb.GetUserSet ...@@ -245,8 +244,6 @@ func (s *APIV2Service) GetUserSetting(ctx context.Context, _ *apiv2pb.GetUserSet
userSettingMessage.MemoVisibility = setting.GetMemoVisibility() userSettingMessage.MemoVisibility = setting.GetMemoVisibility()
} else if setting.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID { } else if setting.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
userSettingMessage.TelegramUserId = setting.GetTelegramUserId() userSettingMessage.TelegramUserId = setting.GetTelegramUserId()
} else if setting.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
userSettingMessage.CompactView = setting.GetCompactView()
} }
} }
return &apiv2pb.GetUserSettingResponse{ return &apiv2pb.GetUserSettingResponse{
...@@ -305,16 +302,6 @@ func (s *APIV2Service) UpdateUserSetting(ctx context.Context, request *apiv2pb.U ...@@ -305,16 +302,6 @@ func (s *APIV2Service) UpdateUserSetting(ctx context.Context, request *apiv2pb.U
}); err != nil { }); err != nil {
return nil, status.Errorf(codes.Internal, "failed to upsert user setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to upsert user setting: %v", err)
} }
} else if field == "compact_view" {
if _, err := s.Store.UpsertUserSetting(ctx, &storepb.UserSetting{
UserId: user.ID,
Key: storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW,
Value: &storepb.UserSetting_CompactView{
CompactView: request.Setting.CompactView,
},
}); err != nil {
return nil, status.Errorf(codes.Internal, "failed to upsert user setting: %v", err)
}
} else { } else {
return nil, status.Errorf(codes.InvalidArgument, "invalid update path: %s", field) return nil, status.Errorf(codes.InvalidArgument, "invalid update path: %s", field)
} }
......
...@@ -3,7 +3,6 @@ package mysql ...@@ -3,7 +3,6 @@ package mysql
import ( import (
"context" "context"
"database/sql" "database/sql"
"strconv"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -30,8 +29,6 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) ...@@ -30,8 +29,6 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting)
valueString = upsert.GetMemoVisibility() valueString = upsert.GetMemoVisibility()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID { } else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
valueString = upsert.GetTelegramUserId() valueString = upsert.GetTelegramUserId()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
valueString = strconv.FormatBool(upsert.GetCompactView())
} else { } else {
return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String()) return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String())
} }
...@@ -96,14 +93,6 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ...@@ -96,14 +93,6 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
userSetting.Value = &storepb.UserSetting_TelegramUserId{ userSetting.Value = &storepb.UserSetting_TelegramUserId{
TelegramUserId: valueString, TelegramUserId: valueString,
} }
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
compactView, err := strconv.ParseBool(valueString)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse compact view value: %s", valueString)
}
userSetting.Value = &storepb.UserSetting_CompactView{
CompactView: compactView,
}
} else { } else {
// Skip unknown user setting key. // Skip unknown user setting key.
continue continue
......
...@@ -3,7 +3,6 @@ package postgres ...@@ -3,7 +3,6 @@ package postgres
import ( import (
"context" "context"
"database/sql" "database/sql"
"strconv"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -37,8 +36,6 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) ...@@ -37,8 +36,6 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting)
valueString = upsert.GetMemoVisibility() valueString = upsert.GetMemoVisibility()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID { } else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
valueString = upsert.GetTelegramUserId() valueString = upsert.GetTelegramUserId()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
valueString = strconv.FormatBool(upsert.GetCompactView())
} else { } else {
return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String()) return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String())
} }
...@@ -109,14 +106,6 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ...@@ -109,14 +106,6 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
userSetting.Value = &storepb.UserSetting_TelegramUserId{ userSetting.Value = &storepb.UserSetting_TelegramUserId{
TelegramUserId: valueString, TelegramUserId: valueString,
} }
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
compactView, err := strconv.ParseBool(valueString)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse compact view value: %s", valueString)
}
userSetting.Value = &storepb.UserSetting_CompactView{
CompactView: compactView,
}
} else { } else {
// Skip unknown user setting key. // Skip unknown user setting key.
continue continue
......
...@@ -3,7 +3,6 @@ package sqlite ...@@ -3,7 +3,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql" "database/sql"
"strconv"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -37,8 +36,6 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) ...@@ -37,8 +36,6 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting)
valueString = upsert.GetMemoVisibility() valueString = upsert.GetMemoVisibility()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID { } else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
valueString = upsert.GetTelegramUserId() valueString = upsert.GetTelegramUserId()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
valueString = strconv.FormatBool(upsert.GetCompactView())
} else { } else {
return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String()) return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String())
} }
...@@ -109,14 +106,6 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ...@@ -109,14 +106,6 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
userSetting.Value = &storepb.UserSetting_TelegramUserId{ userSetting.Value = &storepb.UserSetting_TelegramUserId{
TelegramUserId: valueString, TelegramUserId: valueString,
} }
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_COMPACT_VIEW {
compactView, err := strconv.ParseBool(valueString)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse compact view value: %s", valueString)
}
userSetting.Value = &storepb.UserSetting_CompactView{
CompactView: compactView,
}
} else { } else {
// Skip unknown user setting key. // Skip unknown user setting key.
continue continue
......
...@@ -363,6 +363,7 @@ ...@@ -363,6 +363,7 @@
"syscall/js.finalizeRef": (v_ref) => { "syscall/js.finalizeRef": (v_ref) => {
// Note: TinyGo does not support finalizers so this should never be // Note: TinyGo does not support finalizers so this should never be
// called. // called.
console.warn("syscall/js.finalizeRef not implemented");
}, },
// func stringVal(value string) ref // func stringVal(value string) ref
......
...@@ -28,10 +28,13 @@ const CodeBlock: React.FC<Props> = ({ language, content }: Props) => { ...@@ -28,10 +28,13 @@ const CodeBlock: React.FC<Props> = ({ language, content }: Props) => {
let highlightedCode = content; let highlightedCode = content;
try { try {
const temp = hljs.highlight(content, { const lang = hljs.getLanguage(formatedLanguage);
language: formatedLanguage, if (lang) {
}).value; const temp = hljs.highlight(content, {
highlightedCode = temp; language: formatedLanguage,
}).value;
highlightedCode = temp;
}
} catch (error) { } catch (error) {
// Skip error and use default highlighted code. // Skip error and use default highlighted code.
} }
......
...@@ -62,67 +62,66 @@ interface Props { ...@@ -62,67 +62,66 @@ interface Props {
node: Node; node: Node;
} }
const Renderer: React.FC<Props> = ({ index, node: rawNode }: Props) => { const Renderer: React.FC<Props> = ({ index, node }: Props) => {
const { type, node } = rawNode; switch (node.type) {
switch (type) {
case NodeType.LINE_BREAK: case NodeType.LINE_BREAK:
return <LineBreak index={index} />; return <LineBreak index={index} />;
case NodeType.PARAGRAPH: case NodeType.PARAGRAPH:
return <Paragraph index={index} {...(node as ParagraphNode)} />; return <Paragraph index={index} {...(node.value as ParagraphNode)} />;
case NodeType.CODE_BLOCK: case NodeType.CODE_BLOCK:
return <CodeBlock index={index} {...(node as CodeBlockNode)} />; return <CodeBlock index={index} {...(node.value as CodeBlockNode)} />;
case NodeType.HEADING: case NodeType.HEADING:
return <Heading index={index} {...(node as HeadingNode)} />; return <Heading index={index} {...(node.value as HeadingNode)} />;
case NodeType.HORIZONTAL_RULE: case NodeType.HORIZONTAL_RULE:
return <HorizontalRule index={index} {...(node as HorizontalRuleNode)} />; return <HorizontalRule index={index} {...(node.value as HorizontalRuleNode)} />;
case NodeType.BLOCKQUOTE: case NodeType.BLOCKQUOTE:
return <Blockquote index={index} {...(node as BlockquoteNode)} />; return <Blockquote index={index} {...(node.value as BlockquoteNode)} />;
case NodeType.ORDERED_LIST: case NodeType.ORDERED_LIST:
return <OrderedList index={index} {...(node as OrderedListNode)} />; return <OrderedList index={index} {...(node.value as OrderedListNode)} />;
case NodeType.UNORDERED_LIST: case NodeType.UNORDERED_LIST:
return <UnorderedList {...(node as UnorderedListNode)} />; return <UnorderedList {...(node.value as UnorderedListNode)} />;
case NodeType.TASK_LIST: case NodeType.TASK_LIST:
return <TaskList index={index} {...(node as TaskListNode)} />; return <TaskList index={index} {...(node.value as TaskListNode)} />;
case NodeType.MATH_BLOCK: case NodeType.MATH_BLOCK:
return <Math {...(node as MathNode)} block={true} />; return <Math {...(node.value as MathNode)} block={true} />;
case NodeType.TABLE: case NodeType.TABLE:
return <Table {...(node as TableNode)} />; return <Table {...(node.value as TableNode)} />;
case NodeType.EMBEDDED_CONTENT: case NodeType.EMBEDDED_CONTENT:
return <EmbeddedContent {...(node as EmbeddedContentNode)} />; return <EmbeddedContent {...(node.value as EmbeddedContentNode)} />;
case NodeType.TEXT: case NodeType.TEXT:
return <Text {...(node as TextNode)} />; return <Text {...(node.value as TextNode)} />;
case NodeType.BOLD: case NodeType.BOLD:
return <Bold {...(node as BoldNode)} />; return <Bold {...(node.value as BoldNode)} />;
case NodeType.ITALIC: case NodeType.ITALIC:
return <Italic {...(node as ItalicNode)} />; return <Italic {...(node.value as ItalicNode)} />;
case NodeType.BOLD_ITALIC: case NodeType.BOLD_ITALIC:
return <BoldItalic {...(node as BoldItalicNode)} />; return <BoldItalic {...(node.value as BoldItalicNode)} />;
case NodeType.CODE: case NodeType.CODE:
return <Code {...(node as CodeNode)} />; return <Code {...(node.value as CodeNode)} />;
case NodeType.IMAGE: case NodeType.IMAGE:
return <Image {...(node as ImageNode)} />; return <Image {...(node.value as ImageNode)} />;
case NodeType.LINK: case NodeType.LINK:
return <Link {...(node as LinkNode)} />; return <Link {...(node.value as LinkNode)} />;
case NodeType.AUTO_LINK: case NodeType.AUTO_LINK:
return <Link {...(node as AutoLinkNode)} />; return <Link {...(node.value as AutoLinkNode)} />;
case NodeType.TAG: case NodeType.TAG:
return <Tag {...(node as TagNode)} />; return <Tag {...(node.value as TagNode)} />;
case NodeType.STRIKETHROUGH: case NodeType.STRIKETHROUGH:
return <Strikethrough {...(node as StrikethroughNode)} />; return <Strikethrough {...(node.value as StrikethroughNode)} />;
case NodeType.MATH: case NodeType.MATH:
return <Math {...(node as MathNode)} />; return <Math {...(node.value as MathNode)} />;
case NodeType.HIGHLIGHT: case NodeType.HIGHLIGHT:
return <Highlight {...(node as HighlightNode)} />; return <Highlight {...(node.value as HighlightNode)} />;
case NodeType.ESCAPING_CHARACTER: case NodeType.ESCAPING_CHARACTER:
return <EscapingCharacter {...(node as EscapingCharacterNode)} />; return <EscapingCharacter {...(node.value as EscapingCharacterNode)} />;
case NodeType.SUBSCRIPT: case NodeType.SUBSCRIPT:
return <Subscript {...(node as SubscriptNode)} />; return <Subscript {...(node.value as SubscriptNode)} />;
case NodeType.SUPERSCRIPT: case NodeType.SUPERSCRIPT:
return <Superscript {...(node as SuperscriptNode)} />; return <Superscript {...(node.value as SuperscriptNode)} />;
case NodeType.REFERENCED_CONTENT: case NodeType.REFERENCED_CONTENT:
return <ReferencedContent {...(node as ReferencedContentNode)} />; return <ReferencedContent {...(node.value as ReferencedContentNode)} />;
case NodeType.SPOILER: case NodeType.SPOILER:
return <Spoiler {...(node as SpoilerNode)} />; return <Spoiler {...(node.value as SpoilerNode)} />;
default: default:
return null; return null;
} }
......
...@@ -31,11 +31,11 @@ const TaskList: React.FC<Props> = ({ index, indent, complete, children }: Props) ...@@ -31,11 +31,11 @@ const TaskList: React.FC<Props> = ({ index, indent, complete, children }: Props)
} }
const node = context.nodes[nodeIndex]; const node = context.nodes[nodeIndex];
if (node.type !== NodeType.TASK_LIST || !node.node) { if (node.type !== NodeType.TASK_LIST || !node.value) {
return; return;
} }
(node.node as TaskListNode)!.complete = on; (node.value as TaskListNode)!.complete = on;
const content = window.restore(context.nodes); const content = window.restore(context.nodes);
await memoStore.updateMemo( await memoStore.updateMemo(
{ {
......
import { memo, useRef } from "react"; import classNames from "classnames";
import { memo, useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
import { Node, NodeType } from "@/types/node"; import { Node, NodeType } from "@/types/node";
import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
import Renderer from "./Renderer"; import Renderer from "./Renderer";
import { RendererContext } from "./types"; import { RendererContext } from "./types";
// MAX_DISPLAY_HEIGHT is the maximum height of the memo content to display in compact mode.
const MAX_DISPLAY_HEIGHT = 256;
interface Props { interface Props {
content: string; content: string;
memoId?: number; memoId?: number;
compact?: boolean;
readonly?: boolean; readonly?: boolean;
disableFilter?: boolean; disableFilter?: boolean;
// embeddedMemos is a set of memo resource names that are embedded in the current memo. // embeddedMemos is a set of memo resource names that are embedded in the current memo.
...@@ -19,11 +27,28 @@ interface Props { ...@@ -19,11 +27,28 @@ interface Props {
const MemoContent: React.FC<Props> = (props: Props) => { const MemoContent: React.FC<Props> = (props: Props) => {
const { className, content, memoId, embeddedMemos, onClick } = props; const { className, content, memoId, embeddedMemos, onClick } = props;
const t = useTranslate();
const currentUser = useCurrentUser(); const currentUser = useCurrentUser();
const memoStore = useMemoStore(); const memoStore = useMemoStore();
const memoContentContainerRef = useRef<HTMLDivElement>(null); const memoContentContainerRef = useRef<HTMLDivElement>(null);
const [showCompactMode, setShowCompactMode] = useState<boolean>(false);
const memo = memoId ? memoStore.getMemoById(memoId) : null;
const nodes = window.parse(content); const nodes = window.parse(content);
const allowEdit = !props.readonly && memoId && currentUser?.id === memoStore.getMemoById(memoId)?.creatorId; const allowEdit = !props.readonly && memo && currentUser?.id === memo.creatorId;
// Initial compact mode.
useEffect(() => {
if (!props.compact) {
return;
}
if (!memoContentContainerRef.current) {
return;
}
if ((memoContentContainerRef.current as HTMLDivElement).getBoundingClientRect().height > MAX_DISPLAY_HEIGHT) {
setShowCompactMode(true);
}
}, []);
const handleMemoContentClick = async (e: React.MouseEvent) => { const handleMemoContentClick = async (e: React.MouseEvent) => {
if (onClick) { if (onClick) {
...@@ -35,34 +60,50 @@ const MemoContent: React.FC<Props> = (props: Props) => { ...@@ -35,34 +60,50 @@ const MemoContent: React.FC<Props> = (props: Props) => {
let skipNextLineBreakFlag = false; let skipNextLineBreakFlag = false;
return ( return (
<RendererContext.Provider <>
value={{ <RendererContext.Provider
nodes, value={{
memoId, nodes,
readonly: !allowEdit, memoId,
disableFilter: props.disableFilter, readonly: !allowEdit,
embeddedMemos: embeddedMemos || new Set(), disableFilter: props.disableFilter,
}} embeddedMemos: embeddedMemos || new Set(),
> }}
<div className={`w-full flex flex-col justify-start items-start text-gray-800 dark:text-gray-300 ${className || ""}`}> >
<div <div className={`w-full flex flex-col justify-start items-start text-gray-800 dark:text-gray-300 ${className || ""}`}>
ref={memoContentContainerRef} <div
className="w-full max-w-full word-break text-base leading-6 space-y-1 whitespace-pre-wrap" ref={memoContentContainerRef}
onClick={handleMemoContentClick} className={classNames(
> "w-full max-w-full word-break text-base leading-6 space-y-1 whitespace-pre-wrap",
{nodes.map((node, index) => { showCompactMode && "line-clamp-6",
if (prevNode?.type !== NodeType.LINE_BREAK && node.type === NodeType.LINE_BREAK && skipNextLineBreakFlag) { )}
skipNextLineBreakFlag = false; onClick={handleMemoContentClick}
return null; >
} {nodes.map((node, index) => {
if (prevNode?.type !== NodeType.LINE_BREAK && node.type === NodeType.LINE_BREAK && skipNextLineBreakFlag) {
skipNextLineBreakFlag = false;
return null;
}
prevNode = node; prevNode = node;
skipNextLineBreakFlag = true; skipNextLineBreakFlag = true;
return <Renderer key={`${node.type}-${index}`} index={String(index)} node={node} />; return <Renderer key={`${node.type}-${index}`} index={String(index)} node={node} />;
})} })}
</div>
</div>
</RendererContext.Provider>
{memo && showCompactMode && (
<div className="w-full mt-2">
<Link
className="w-auto inline-flex flex-row justify-start items-center text-sm text-blue-600 dark:text-blue-400 hover:underline"
to={`/m/${memo.name}`}
>
<span>{t("memo.show-more")}</span>
<Icon.ChevronRight className="w-4 h-auto" />
</Link>
</div> </div>
</div> )}
</RendererContext.Provider> </>
); );
}; };
......
...@@ -42,7 +42,6 @@ const MemoView: React.FC<Props> = (props: Props) => { ...@@ -42,7 +42,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
const [displayTime, setDisplayTime] = useState<string>(getRelativeTimeString(getTimeStampByDate(memo.displayTime))); const [displayTime, setDisplayTime] = useState<string>(getRelativeTimeString(getTimeStampByDate(memo.displayTime)));
const [creator, setCreator] = useState(userStore.getUserByUsername(extractUsernameFromName(memo.creator))); const [creator, setCreator] = useState(userStore.getUserByUsername(extractUsernameFromName(memo.creator)));
const memoContainerRef = useRef<HTMLDivElement>(null); const memoContainerRef = useRef<HTMLDivElement>(null);
const [showCompactMode, setShowCompactMode] = useState(false);
const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE); const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
const commentAmount = memo.relations.filter((relation) => relation.type === MemoRelation_Type.COMMENT).length; const commentAmount = memo.relations.filter((relation) => relation.type === MemoRelation_Type.COMMENT).length;
const readonly = memo.creator !== user?.name; const readonly = memo.creator !== user?.name;
...@@ -55,16 +54,6 @@ const MemoView: React.FC<Props> = (props: Props) => { ...@@ -55,16 +54,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
})(); })();
}, []); }, []);
// Initial compact mode.
useEffect(() => {
if (!memoContainerRef.current) {
return;
}
if ((memoContainerRef.current as HTMLDivElement).getBoundingClientRect().height > 512) {
setShowCompactMode(true);
}
}, []);
// Update display time string. // Update display time string.
useEffect(() => { useEffect(() => {
let intervalFlag: any = -1; let intervalFlag: any = -1;
...@@ -163,24 +152,13 @@ const MemoView: React.FC<Props> = (props: Props) => { ...@@ -163,24 +152,13 @@ const MemoView: React.FC<Props> = (props: Props) => {
</div> </div>
</div> </div>
<MemoContent <MemoContent
className={showCompactMode ? "!line-clamp-6" : ""}
key={`${memo.id}-${memo.updateTime}`} key={`${memo.id}-${memo.updateTime}`}
memoId={memo.id} memoId={memo.id}
content={memo.content} content={memo.content}
readonly={readonly} readonly={readonly}
onClick={handleMemoContentClick} onClick={handleMemoContentClick}
compact={true}
/> />
{showCompactMode && (
<div className="w-full mt-2">
<Link
className="w-auto flex flex-row justify-start items-center text-sm text-blue-600 dark:text-blue-400 hover:underline"
to={`/m/${memo.name}`}
>
<span>{t("memo.show-more")}</span>
<Icon.ChevronRight className="w-4 h-auto" />
</Link>
</div>
)}
<MemoResourceListView resources={memo.resources} /> <MemoResourceListView resources={memo.resources} />
<MemoRelationListView memo={memo} relations={referencedMemos} /> <MemoRelationListView memo={memo} relations={referencedMemos} />
<MemoReactionistView memo={memo} reactions={memo.reactions} /> <MemoReactionistView memo={memo} reactions={memo.reactions} />
......
...@@ -216,7 +216,7 @@ const SystemSection = () => { ...@@ -216,7 +216,7 @@ const SystemSection = () => {
<div className="w-full flex flex-col gap-2 pt-2 pb-4"> <div className="w-full flex flex-col gap-2 pt-2 pb-4">
<p className="font-medium text-gray-700 dark:text-gray-500">{t("common.basic")}</p> <p className="font-medium text-gray-700 dark:text-gray-500">{t("common.basic")}</p>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="normal-text"> <div>
{t("setting.system-section.server-name")}: <span className="font-mono font-bold">{systemStatus.customizedProfile.name}</span> {t("setting.system-section.server-name")}: <span className="font-mono font-bold">{systemStatus.customizedProfile.name}</span>
</div> </div>
<Button onClick={handleUpdateCustomizedProfileButtonClick}>{t("common.edit")}</Button> <Button onClick={handleUpdateCustomizedProfileButtonClick}>{t("common.edit")}</Button>
...@@ -267,7 +267,7 @@ const SystemSection = () => { ...@@ -267,7 +267,7 @@ const SystemSection = () => {
</div> </div>
<div className="space-y-2 border rounded-md py-2 px-3 dark:border-zinc-700"> <div className="space-y-2 border rounded-md py-2 px-3 dark:border-zinc-700">
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.additional-style")}</span> <span>{t("setting.system-section.additional-style")}</span>
<Button variant="outlined" color="neutral" onClick={handleSaveAdditionalStyle}> <Button variant="outlined" color="neutral" onClick={handleSaveAdditionalStyle}>
{t("common.save")} {t("common.save")}
</Button> </Button>
...@@ -285,7 +285,7 @@ const SystemSection = () => { ...@@ -285,7 +285,7 @@ const SystemSection = () => {
onChange={(event) => handleAdditionalStyleChanged(event.target.value)} onChange={(event) => handleAdditionalStyleChanged(event.target.value)}
/> />
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.additional-script")}</span> <span>{t("setting.system-section.additional-script")}</span>
<Button variant="outlined" color="neutral" onClick={handleSaveAdditionalScript}> <Button variant="outlined" color="neutral" onClick={handleSaveAdditionalScript}>
{t("common.save")} {t("common.save")}
</Button> </Button>
...@@ -317,16 +317,16 @@ const SystemSection = () => { ...@@ -317,16 +317,16 @@ const SystemSection = () => {
<Divider className="!my-3" /> <Divider className="!my-3" />
<p className="font-medium text-gray-700 dark:text-gray-500">Others</p> <p className="font-medium text-gray-700 dark:text-gray-500">Others</p>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.disable-public-memos")}</span> <span>{t("setting.system-section.disable-public-memos")}</span>
<Switch checked={state.disablePublicMemos} onChange={(event) => handleDisablePublicMemosChanged(event.target.checked)} /> <Switch checked={state.disablePublicMemos} onChange={(event) => handleDisablePublicMemosChanged(event.target.checked)} />
</div> </div>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.display-with-updated-time")}</span> <span>{t("setting.system-section.display-with-updated-time")}</span>
<Switch checked={state.memoDisplayWithUpdatedTs} onChange={(event) => handleMemoDisplayWithUpdatedTs(event.target.checked)} /> <Switch checked={state.memoDisplayWithUpdatedTs} onChange={(event) => handleMemoDisplayWithUpdatedTs(event.target.checked)} />
</div> </div>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<span className="text-sm mr-1">{t("setting.system-section.max-upload-size")}</span> <span className="mr-1">{t("setting.system-section.max-upload-size")}</span>
<Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top"> <Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top">
<Icon.HelpCircle className="w-4 h-auto" /> <Icon.HelpCircle className="w-4 h-auto" />
</Tooltip> </Tooltip>
......
...@@ -32,7 +32,7 @@ export enum NodeType { ...@@ -32,7 +32,7 @@ export enum NodeType {
export interface Node { export interface Node {
type: NodeType; type: NodeType;
node: value:
| LineBreakNode | LineBreakNode
| ParagraphNode | ParagraphNode
| CodeBlockNode | CodeBlockNode
......
import { Node } from "@/types/node"; import { Node, TagNode } from "@/types/node";
export const TAG_REG = /#([^\s#,]+)/; export const TAG_REG = /#([^\s#,]+)/;
...@@ -15,7 +15,7 @@ export const extractTagsFromContent = (content: string) => { ...@@ -15,7 +15,7 @@ export const extractTagsFromContent = (content: string) => {
handle(node); handle(node);
if (node.type === "PARAGRAPH" || node.type === "ORDERED_LIST" || node.type === "UNORDERED_LIST") { if (node.type === "PARAGRAPH" || node.type === "ORDERED_LIST" || node.type === "UNORDERED_LIST") {
const children = (node.node as any).children; const children = (node.value as any).children;
if (Array.isArray(children)) { if (Array.isArray(children)) {
traverse(children, handle); traverse(children, handle);
} }
...@@ -24,8 +24,8 @@ export const extractTagsFromContent = (content: string) => { ...@@ -24,8 +24,8 @@ export const extractTagsFromContent = (content: string) => {
}; };
traverse(nodes, (node) => { traverse(nodes, (node) => {
if (node.type === "TAG" && node.node) { if (node.type === "TAG" && node.value) {
tags.add((node.node as any).content); tags.add((node.value as TagNode).content);
} }
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment