sodesu-comment
Version:
Comment system with SolidJS
194 lines (178 loc) • 4.66 kB
text/typescript
import type { WalineComment, WalineCommentStatus } from '@waline/api';
import type { WalineCommentSorting } from '@waline/client';
import type { Accessor, Setter } from 'solid-js';
import { getComment } from '@waline/api';
import { createRoot, createSignal } from 'solid-js';
import configProvider from './configProvider';
import userInfoState from './userInfoState';
type SortKey = 'insertedAt_desc' | 'insertedAt_asc' | 'like_desc';
const sortKeyMap: Record<WalineCommentSorting, SortKey> = {
latest: 'insertedAt_desc',
oldest: 'insertedAt_asc',
hottest: 'like_desc',
};
export const sortingMethods = Object.keys(sortKeyMap) as WalineCommentSorting[];
interface ReactiveCommentData {
/**
* User Nickname
*/
nick: string;
/**
* User link
*/
link?: string;
/**
* Content of comment
*/
comment: Accessor<string>;
/**
* Parent comment id
*/
pid?: number;
/**
* Root comment id
*/
rid?: number;
/**
* User id being at
*/
at?: string;
/**
* Recaptcha Token
*/
recaptchaV3?: string;
/**
* Reply user information
*/
reply_user?: {
nick: string;
link: string;
avatar: string;
};
}
export interface ReactiveComment extends Exclude<ReactiveCommentData, 'ua'> {
avatar: string;
/**
* User type
*/
type?: 'administrator' | 'guest' | `verify:${string}`;
objectId: number;
/**
* Time ISOString when the comment is created
*/
children: Accessor<ReactiveComment[]>;
sticky?: boolean;
browser?: string;
os?: string;
level?: number;
addr?: string;
label?: string;
time: number;
user_id?: string | number;
status?: WalineCommentStatus;
like: Accessor<number>;
orig: Accessor<string | undefined>;
setLike: Setter<number>;
setChildren: Setter<ReactiveComment[]>;
setComment: Setter<string>;
setOrig: Setter<string>;
}
export function makeDataReactive(data: WalineComment): ReactiveComment {
const [children, setChildren] = createSignal(
makeDatasReactive('children' in data ? data.children : []),
);
const [like, setLike] = createSignal(data.like || 0);
const [comment, setComment] = createSignal(data.comment);
const [orig, setOrig] = createSignal(data.orig);
return {
...data,
time: (data as unknown as { time: number }).time,
children,
setChildren,
like,
setLike,
comment,
setComment,
orig,
setOrig,
};
}
export function makeDatasReactive(datas: WalineComment[]) {
return datas.map((data) => makeDataReactive(data));
}
const commentListState = createRoot(() => {
const { config } = configProvider;
const [status, setStatus] = createSignal<'loading' | 'success' | 'error'>('loading');
const [sorting, setSorting] = createSignal(config().commentSorting);
const [page, setPage] = createSignal(1);
const [count, setCount] = createSignal(1);
const [data, setData] = createSignal<ReactiveComment[]>([]);
const [totalPages, setTotalPages] = createSignal(0);
return {
status,
sorting,
page,
count,
data,
totalPages,
setStatus,
setSorting,
setPage,
setCount,
setData,
setTotalPages,
};
});
let abort: () => void;
export function getCommentData(page: number) {
const { sorting, setStatus, setCount, setData, setPage, setTotalPages } = commentListState;
const { config } = configProvider;
const { userInfo } = userInfoState;
const controller = new AbortController();
setStatus('loading');
abort?.();
getComment({
serverURL: config().serverURL,
lang: config().lang,
path: config().path,
pageSize: config().pageSize,
sortBy: sortKeyMap[sorting()],
page,
signal: controller.signal,
token: userInfo()?.token,
})
.then((res) => {
setStatus('success');
setCount(res.count);
setData((data) => data.concat(...makeDatasReactive(res.data)));
setPage(page);
setTotalPages(res.totalPages);
})
.catch((err: Error) => {
if (err.name !== 'AbortError') {
console.error(err.message);
setStatus('error');
}
});
abort = controller.abort.bind(controller);
}
export function loadMore() {
const { page } = commentListState;
getCommentData(page() + 1);
}
export function refresh() {
const { setCount, setData } = commentListState;
setCount(0);
setData([]);
getCommentData(1);
}
export function deleteComment(id: number) {
const { setData } = commentListState;
setData((data) =>
data.filter((item) => {
item.setChildren((children) => children.filter((child) => child.objectId !== id));
return item.objectId !== id;
}),
);
}
export default commentListState;