rename blocks to widgets

This commit is contained in:
Ren Amamiya 2023-08-11 15:25:33 +07:00
parent 0cfc3a48d8
commit 36b2acba6a
22 changed files with 118 additions and 107 deletions

View File

@ -0,0 +1,3 @@
-- Add migration script here
ALTER TABLE blocks
RENAME TO widgets;

View File

@ -110,6 +110,12 @@ fn main() {
sql: include_str!("../migrations/20230808085847_add_relays_table.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230811074423,
description: "rename blocks to widgets",
sql: include_str!("../migrations/20230811074423_rename_blocks_to_widgets.sql"),
kind: MigrationKind::Up,
},
],
)
.build(),

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { createBlock } from '@libs/storage';
import { createWidget } from '@libs/storage';
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
@ -50,7 +50,7 @@ export function OnboardStep2Screen() {
setLoading(true);
for (const tag of tags) {
await createBlock(BLOCK_KINDS.hashtag, tag, tag.replace('#', ''));
await createWidget(BLOCK_KINDS.hashtag, tag, tag.replace('#', ''));
}
setTimeout(() => navigate('/auth/onboarding/step-3', { replace: true }), 1000);

View File

@ -9,11 +9,11 @@ import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { NoteSkeleton } from '@shared/notes/skeleton';
import { TitleBar } from '@shared/titleBar';
import { Block, LumeEvent } from '@utils/types';
import { LumeEvent, Widget } from '@utils/types';
const ITEM_PER_PAGE = 10;
export function FeedBlock({ params }: { params: Block }) {
export function FeedBlock({ params }: { params: Widget }) {
const { status, data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
queryKey: ['newsfeed', params.content],

View File

@ -8,9 +8,9 @@ import { NoteKind_1, NoteSkeleton } from '@shared/notes';
import { TitleBar } from '@shared/titleBar';
import { nHoursAgo } from '@utils/date';
import { Block, LumeEvent } from '@utils/types';
import { LumeEvent, Widget } from '@utils/types';
export function HashtagBlock({ params }: { params: Block }) {
export function HashtagBlock({ params }: { params: Widget }) {
const { relayUrls, fetcher } = useNDK();
const { status, data } = useQuery(['hashtag', params.content], async () => {
const events = (await fetcher.fetchAllEvents(

View File

@ -1,13 +1,13 @@
import { CancelIcon } from '@shared/icons';
import { Image } from '@shared/image';
import { useBlocks } from '@stores/blocks';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { Block } from '@utils/types';
import { Widget } from '@utils/types';
export function ImageBlock({ params }: { params: Block }) {
const removeBlock = useBlocks((state) => state.removeBlock);
export function ImageBlock({ params }: { params: Widget }) {
const remove = useWidgets((state) => state.removeWidget);
return (
<div className="flex h-full w-[400px] shrink-0 flex-col justify-between">
@ -17,7 +17,7 @@ export function ImageBlock({ params }: { params: Block }) {
<h3 className="font-medium text-white drop-shadow-lg">{params.title}</h3>
<button
type="button"
onClick={() => removeBlock(params.id)}
onClick={() => remove(params.id)}
className="inline-flex h-7 w-7 items-center justify-center rounded-md bg-white/30 backdrop-blur-lg"
>
<CancelIcon width={16} height={16} className="text-white" />

View File

@ -12,9 +12,9 @@ import { TitleBar } from '@shared/titleBar';
import { useAccount } from '@utils/hooks/useAccount';
import { useEvent } from '@utils/hooks/useEvent';
import { Block } from '@utils/types';
import { Widget } from '@utils/types';
export function ThreadBlock({ params }: { params: Block }) {
export function ThreadBlock({ params }: { params: Widget }) {
const { status, data } = useEvent(params.content);
const { account } = useAccount();

View File

@ -9,9 +9,9 @@ import { TitleBar } from '@shared/titleBar';
import { UserProfile } from '@shared/userProfile';
import { nHoursAgo } from '@utils/date';
import { Block, LumeEvent } from '@utils/types';
import { LumeEvent, Widget } from '@utils/types';
export function UserBlock({ params }: { params: Block }) {
export function UserBlock({ params }: { params: Widget }) {
const parentRef = useRef<HTMLDivElement>(null);
const { fetcher, relayUrls } = useNDK();

View File

@ -8,7 +8,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { User } from '@app/auth/components/user';
import { createBlock } from '@libs/storage';
import { createWidget } from '@libs/storage';
import { CancelIcon, CheckCircleIcon, CommandIcon, LoaderIcon } from '@shared/icons';
@ -31,7 +31,7 @@ export function FeedModal() {
const block = useMutation({
mutationFn: (data: { kind: number; title: string; content: string }) => {
return createBlock(data.kind, data.title, data.content);
return createWidget(data.kind, data.title, data.content);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['blocks'] });

View File

@ -4,7 +4,7 @@ import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { createBlock } from '@libs/storage';
import { createWidget } from '@libs/storage';
import { CancelIcon, CommandIcon, LoaderIcon } from '@shared/icons';
@ -28,7 +28,7 @@ export function HashtagModal() {
const block = useMutation({
mutationFn: (data: { kind: number; title: string; content: string }) => {
return createBlock(data.kind, data.title, data.content);
return createWidget(data.kind, data.title, data.content);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['blocks'] });

View File

@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { createBlock } from '@libs/storage';
import { createWidget } from '@libs/storage';
import { CancelIcon, CommandIcon, LoaderIcon } from '@shared/icons';
import { Image } from '@shared/image';
@ -39,7 +39,7 @@ export function ImageModal() {
const block = useMutation({
mutationFn: (data: { kind: number; title: string; content: string }) => {
return createBlock(data.kind, data.title, data.content);
return createWidget(data.kind, data.title, data.content);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['blocks'] });

View File

@ -12,48 +12,51 @@ import { ImageModal } from '@app/space/components/modals/image';
import { LoaderIcon } from '@shared/icons';
import { useBlocks } from '@stores/blocks';
import { useWidgets } from '@stores/widgets';
import { Block } from '@utils/types';
import { Widget } from '@utils/types';
export function SpaceScreen() {
const [blocks, fetchBlocks] = useBlocks((state) => [state.blocks, state.fetchBlocks]);
const [widgets, fetchWidgets] = useWidgets((state) => [
state.widgets,
state.fetchWidgets,
]);
const renderBlock = useCallback(
(block: Block) => {
switch (block.kind) {
const renderItem = useCallback(
(widget: Widget) => {
switch (widget.kind) {
case 0:
return <ImageBlock key={block.id} params={block} />;
return <ImageBlock key={widget.id} params={widget} />;
case 1:
return <FeedBlock key={block.id} params={block} />;
return <FeedBlock key={widget.id} params={widget} />;
case 2:
return <ThreadBlock key={block.id} params={block} />;
return <ThreadBlock key={widget.id} params={widget} />;
case 3:
return <HashtagBlock key={block.id} params={block} />;
return <HashtagBlock key={widget.id} params={widget} />;
case 5:
return <UserBlock key={block.id} params={block} />;
return <UserBlock key={widget.id} params={widget} />;
default:
break;
}
},
[blocks]
[widgets]
);
useEffect(() => {
fetchBlocks();
}, [fetchBlocks]);
fetchWidgets();
}, [fetchWidgets]);
return (
<div className="scrollbar-hide flex h-full w-full flex-nowrap divide-x divide-white/5 overflow-x-auto overflow-y-hidden">
<NetworkBlock />
{!blocks ? (
{!widgets ? (
<div className="flex w-[350px] shrink-0 flex-col">
<div className="flex w-full flex-1 items-center justify-center p-3">
<LoaderIcon className="h-5 w-5 animate-spin text-white/10" />
</div>
</div>
) : (
blocks.map((block) => renderBlock(block))
widgets.map((widget) => renderItem(widget))
)}
<div className="flex w-[350px] shrink-0 flex-col">
<div className="inline-flex h-full w-full flex-col items-center justify-center gap-1">

View File

@ -5,12 +5,12 @@ import { parser } from '@utils/parser';
import { getParentID } from '@utils/transform';
import {
Account,
Block,
Chats,
LumeEvent,
Profile,
Relays,
Settings,
Widget,
} from '@utils/types';
let db: null | Database = null;
@ -66,7 +66,7 @@ export async function createAccount(
[npub, pubkey, 'privkey is stored in secure storage', follows || '', is_active || 0]
);
if (res) {
await createBlock(
await createWidget(
0,
'Have fun together!',
'https://void.cat/d/N5KUHEQCVg7SywXUPiJ7yq.jpg'
@ -417,18 +417,18 @@ export async function updateLastLogin(value: number) {
);
}
// get all blocks
export async function getBlocks() {
// get all widgets
export async function getWidgets() {
const db = await connect();
const account = await getActiveAccount();
const result: Array<Block> = await db.select(
`SELECT * FROM blocks WHERE account_id = "${account.id}" ORDER BY created_at DESC;`
const result: Array<Widget> = await db.select(
`SELECT * FROM widgets WHERE account_id = "${account.id}" ORDER BY created_at DESC;`
);
return result;
}
// create block
export async function createBlock(
export async function createWidget(
kind: number,
title: string,
content: string | string[]
@ -436,13 +436,13 @@ export async function createBlock(
const db = await connect();
const activeAccount = await getActiveAccount();
const insert = await db.execute(
'INSERT OR IGNORE INTO blocks (account_id, kind, title, content) VALUES (?, ?, ?, ?);',
'INSERT OR IGNORE INTO widgets (account_id, kind, title, content) VALUES (?, ?, ?, ?);',
[activeAccount.id, kind, title, content]
);
if (insert) {
const record: Block = await db.select(
'SELECT * FROM blocks ORDER BY id DESC LIMIT 1;'
const record: Widget = await db.select(
'SELECT * FROM widgets ORDER BY id DESC LIMIT 1;'
);
return record[0];
} else {
@ -451,9 +451,9 @@ export async function createBlock(
}
// remove block
export async function removeBlock(id: string) {
export async function removeWidget(id: string) {
const db = await connect();
return await db.execute(`DELETE FROM blocks WHERE id = "${id}";`);
return await db.execute(`DELETE FROM widgets WHERE id = "${id}";`);
}
// logout
@ -462,8 +462,7 @@ export async function removeAll() {
await db.execute(`UPDATE settings SET value = "0" WHERE key = "last_login";`);
await db.execute('DELETE FROM replies;');
await db.execute('DELETE FROM notes;');
await db.execute('DELETE FROM blacklist;');
await db.execute('DELETE FROM blocks;');
await db.execute('DELETE FROM widgets;');
await db.execute('DELETE FROM chats;');
await db.execute('DELETE FROM accounts;');
return true;

View File

@ -7,8 +7,8 @@ import { NoteReply } from '@shared/notes/actions/reply';
import { NoteRepost } from '@shared/notes/actions/repost';
import { NoteZap } from '@shared/notes/actions/zap';
import { useBlocks } from '@stores/blocks';
import { BLOCK_KINDS } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { useAccount } from '@utils/hooks/useAccount';
@ -24,7 +24,7 @@ export function NoteActions({
root?: string;
}) {
const { account } = useAccount();
const setBlock = useBlocks((state) => state.setBlock);
const setWidget = useWidgets((state) => state.setWidget);
return (
<Tooltip.Provider>
@ -43,7 +43,7 @@ export function NoteActions({
<button
type="button"
onClick={() =>
setBlock({
setWidget({
kind: BLOCK_KINDS.thread,
title: 'Thread',
content: id,

View File

@ -1,14 +1,14 @@
import { useBlocks } from '@stores/blocks';
import { BLOCK_KINDS } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
export function Hashtag({ tag }: { tag: string }) {
const setBlock = useBlocks((state) => state.setBlock);
const setWidget = useWidgets((state) => state.setWidget);
return (
<button
type="button"
onClick={() =>
setBlock({
setWidget({
kind: BLOCK_KINDS.hashtag,
title: tag,
content: tag.replace('#', ''),

View File

@ -5,19 +5,19 @@ import remarkGfm from 'remark-gfm';
import { MentionUser, NoteSkeleton } from '@shared/notes';
import { User } from '@shared/user';
import { useBlocks } from '@stores/blocks';
import { BLOCK_KINDS } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { useEvent } from '@utils/hooks/useEvent';
export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
const { status, data } = useEvent(id);
const setBlock = useBlocks((state) => state.setBlock);
const setWidget = useWidgets((state) => state.setWidget);
const openThread = (event, thread: string) => {
const selection = window.getSelection();
if (selection.toString().length === 0) {
setBlock({ kind: BLOCK_KINDS.thread, title: 'Thread', content: thread });
setWidget({ kind: BLOCK_KINDS.thread, title: 'Thread', content: thread });
} else {
event.stopPropagation();
}

View File

@ -1,18 +1,18 @@
import { useBlocks } from '@stores/blocks';
import { BLOCK_KINDS } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { useProfile } from '@utils/hooks/useProfile';
import { displayNpub } from '@utils/shortenKey';
export function MentionUser({ pubkey }: { pubkey: string }) {
const { user } = useProfile(pubkey);
const setBlock = useBlocks((state) => state.setBlock);
const setWidget = useWidgets((state) => state.setWidget);
return (
<button
type="button"
onClick={() =>
setBlock({
setWidget({
kind: BLOCK_KINDS.user,
title: user?.nip05 || user?.name || user?.displayNam,
content: pubkey,

View File

@ -8,13 +8,13 @@ import { createReplyNote } from '@libs/storage';
import { LoaderIcon } from '@shared/icons';
import { MiniUser } from '@shared/notes/users/mini';
import { useBlocks } from '@stores/blocks';
import { BLOCK_KINDS } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { compactNumber } from '@utils/number';
export function NoteMetadata({ id }: { id: string }) {
const setBlock = useBlocks((state) => state.setBlock);
const setWidget = useWidgets((state) => state.setWidget);
const { ndk } = useNDK();
const { status, data } = useQuery(
@ -96,7 +96,7 @@ export function NoteMetadata({ id }: { id: string }) {
<button
type="button"
onClick={() =>
setBlock({ kind: BLOCK_KINDS.thread, title: 'Thread', content: id })
setWidget({ kind: BLOCK_KINDS.thread, title: 'Thread', content: id })
}
className="text-white/50"
>

View File

@ -1,9 +1,9 @@
import { CancelIcon } from '@shared/icons';
import { useBlocks } from '@stores/blocks';
import { useWidgets } from '@stores/widgets';
export function TitleBar({ id, title }: { id?: string; title: string }) {
const removeBlock = useBlocks((state) => state.removeBlock);
const remove = useWidgets((state) => state.removeWidget);
return (
<div
@ -15,7 +15,7 @@ export function TitleBar({ id, title }: { id?: string; title: string }) {
{id ? (
<button
type="button"
onClick={() => removeBlock(id)}
onClick={() => remove(id)}
className="inline-flex h-6 w-6 shrink translate-y-8 transform items-center justify-center rounded transition-transform duration-150 ease-in-out hover:bg-white/10 group-hover:translate-y-0"
>
<CancelIcon className="h-3 w-3 text-white" />

View File

@ -1,37 +0,0 @@
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import { createBlock, getBlocks, removeBlock } from '@libs/storage';
import { Block } from '@utils/types';
interface BlockState {
blocks: null | Array<Block>;
fetchBlocks: () => void;
setBlock: ({ kind, title, content }: Block) => void;
removeBlock: (id: string) => void;
}
export const useBlocks = create<BlockState>()(
persist(
(set) => ({
blocks: null,
fetchBlocks: async () => {
const blocks = await getBlocks();
set({ blocks: blocks });
},
setBlock: async ({ kind, title, content }: Block) => {
const block: Block = await createBlock(kind, title, content);
set((state) => ({ blocks: [...state.blocks, block] }));
},
removeBlock: async (id: string) => {
await removeBlock(id);
set((state) => ({ blocks: state.blocks.filter((block) => block.id !== id) }));
},
}),
{
name: 'blocks',
storage: createJSONStorage(() => localStorage),
}
)
);

37
src/stores/widgets.tsx Normal file
View File

@ -0,0 +1,37 @@
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import { createWidget, getWidgets, removeWidget } from '@libs/storage';
import { Widget } from '@utils/types';
interface WidgetState {
widgets: null | Array<Widget>;
fetchWidgets: () => void;
setWidget: ({ kind, title, content }: Widget) => void;
removeWidget: (id: string) => void;
}
export const useWidgets = create<WidgetState>()(
persist(
(set) => ({
widgets: null,
fetchWidgets: async () => {
const widgets = await getWidgets();
set({ widgets: widgets });
},
setWidget: async ({ kind, title, content }: Widget) => {
const widget: Widget = await createWidget(kind, title, content);
set((state) => ({ widgets: [...state.widgets, widget] }));
},
removeWidget: async (id: string) => {
await removeWidget(id);
set((state) => ({ widgets: state.widgets.filter((widget) => widget.id !== id) }));
},
}),
{
name: 'blocks',
storage: createJSONStorage(() => localStorage),
}
)
);

View File

@ -31,7 +31,7 @@ export interface Profile extends NDKUserProfile {
pubkey?: string;
}
export interface Block {
export interface Widget {
id?: string;
account_id?: number;
kind: number;