Skip to content

Commit 7c26bc1

Browse files
committed
検索機能の改善 & しょぼいカレンダー対応
1 parent b6c7fdf commit 7c26bc1

31 files changed

+1849
-840
lines changed

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"dependencies": {
1919
"@icons-pack/react-simple-icons": "^10.0.0",
2020
"@internationalized/date": "^3.5.6",
21-
"@midra/nco-api": "^1.6.6",
21+
"@midra/nco-api": "^1.6.12",
2222
"@midra/nco-parser": "^1.0.25",
2323
"@nextui-org/react": "^2.4.8",
2424
"@webext-core/messaging": "^1.4.0",
@@ -27,28 +27,28 @@
2727
"deepmerge": "^4.3.1",
2828
"fast-deep-equal": "^3.1.3",
2929
"filesize": "^10.1.6",
30-
"framer-motion": "^11.11.7",
30+
"framer-motion": "^11.11.8",
3131
"hotkeys-js": "^3.13.7",
32-
"lucide-react": "^0.451.0",
32+
"lucide-react": "^0.452.0",
3333
"nanoid": "^5.0.7",
3434
"nanoid-dictionary": "^4.3.0",
3535
"react": "^18.3.1",
3636
"react-detectable-overflow": "^0.8.0",
3737
"react-dom": "^18.3.1",
3838
"react-hotkeys-hook": "^4.5.1",
39-
"react-virtuoso": "^4.10.4"
39+
"react-virtuoso": "^4.11.0"
4040
},
4141
"devDependencies": {
4242
"@types/nanoid-dictionary": "^4.2.3",
4343
"@types/react": "^18.3.11",
44-
"@types/react-dom": "^18.3.0",
44+
"@types/react-dom": "^18.3.1",
4545
"@wxt-dev/module-react": "^1.1.1",
4646
"autoprefixer": "^10.4.20",
4747
"cssnano": "^7.0.6",
4848
"postcss": "^8.4.47",
4949
"prettier": "^3.3.3",
5050
"prettier-plugin-tailwindcss": "^0.6.8",
51-
"sass": "^1.79.4",
51+
"sass": "^1.79.5",
5252
"tailwindcss": "^3.4.13",
5353
"typescript": "^5.6.3",
5454
"utility-types": "^3.11.0",

pnpm-lock.yaml

+459-314
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/modal.tsx

+42-29
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ export type ModalProps = {
1818
okText?: string
1919
okIcon?: React.ReactNode
2020
isOkDisabled?: boolean
21-
onOk: () => void
21+
onOk?: () => void
2222

2323
cancelText?: string
2424
cancelIcon?: React.ReactNode
2525

26-
header: React.ReactNode
26+
header?: React.ReactNode
2727
headerEndContent?: React.ReactNode
28+
29+
footer?: React.ReactNode
30+
2831
children: React.ReactNode
2932
}
3033

@@ -48,45 +51,55 @@ export const Modal: React.FC<ModalProps> = (props) => {
4851
<NextUIModalContent>
4952
{(onClose) => {
5053
const onPressOk = useCallback(() => {
51-
props.onOk()
54+
props.onOk?.()
5255
onClose()
5356
}, [props.onOk])
5457

5558
return (
5659
<>
57-
<NextUIModalHeader className="flex flex-row justify-between">
58-
<div className="flex min-h-8 flex-row items-center">
59-
{props.header}
60-
</div>
60+
{props.header && (
61+
<NextUIModalHeader className="flex flex-row justify-between">
62+
<div className="flex min-h-8 flex-row items-center">
63+
{props.header}
64+
</div>
6165

62-
{props.headerEndContent}
63-
</NextUIModalHeader>
66+
{props.headerEndContent}
67+
</NextUIModalHeader>
68+
)}
6469

6570
<NextUIModalBody className="max-h-full gap-0 overflow-auto bg-background">
6671
{props.children}
6772
</NextUIModalBody>
6873

69-
<NextUIModalFooter>
70-
<Button
71-
size="sm"
72-
variant="flat"
73-
color="default"
74-
startContent={props.cancelIcon}
75-
onPress={onClose}
76-
>
77-
{props.cancelText || 'キャンセル'}
78-
</Button>
74+
{props.footer !== false && (
75+
<NextUIModalFooter>
76+
{props.footer ?? (
77+
<>
78+
<Button
79+
size="sm"
80+
variant="flat"
81+
color="default"
82+
startContent={props.cancelIcon}
83+
onPress={onClose}
84+
>
85+
{props.cancelText || 'キャンセル'}
86+
</Button>
7987

80-
<Button
81-
size="sm"
82-
color="primary"
83-
isDisabled={props.isOkDisabled}
84-
startContent={props.okIcon}
85-
onPress={onPressOk}
86-
>
87-
{props.okText || 'OK'}
88-
</Button>
89-
</NextUIModalFooter>
88+
{props.onOk && (
89+
<Button
90+
size="sm"
91+
color="primary"
92+
isDisabled={props.isOkDisabled}
93+
startContent={props.okIcon}
94+
onPress={onPressOk}
95+
>
96+
{props.okText || 'OK'}
97+
</Button>
98+
)}
99+
</>
100+
)}
101+
</NextUIModalFooter>
102+
)}
90103
</>
91104
)
92105
}}

src/components/slot-item/counts.tsx

+18-17
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ export const Counts: React.FC<CountsProps> = ({
1515
isSearch,
1616
}) => {
1717
return (
18-
<div className="flex flex-row items-center gap-4">
18+
<div className="flex flex-row gap-4">
1919
{/* 再生数 */}
2020
{'view' in infoCount && (
21-
<div className="flex h-full flex-row items-center gap-1">
21+
<div className="flex flex-row items-center gap-1">
2222
<PlayIcon className={isSearch ? 'size-mini' : 'size-tiny'} />
2323

2424
<span className={isSearch ? 'text-mini' : 'text-tiny'}>
@@ -28,22 +28,23 @@ export const Counts: React.FC<CountsProps> = ({
2828
)}
2929

3030
{/* コメント数 */}
31-
<div className="flex h-full flex-row items-center gap-1">
32-
<MessageSquareTextIcon
33-
className={isSearch ? 'size-mini' : 'size-tiny'}
34-
/>
31+
{!isSearch && (
32+
<div className="flex flex-row items-center gap-1">
33+
<MessageSquareTextIcon
34+
className={isSearch ? 'size-mini' : 'size-tiny'}
35+
/>
3536

36-
<Skeleton
37-
classNames={{
38-
base: ['min-w-12 data-[loaded=true]:min-w-0', 'rounded-[4px]'],
39-
content: [isSearch ? 'text-mini' : 'text-tiny'],
40-
}}
41-
disableAnimation={isSearch}
42-
isLoaded={0 < infoCount.comment || status === 'ready'}
43-
>
44-
{infoCount.comment.toLocaleString('ja-JP')}
45-
</Skeleton>
46-
</div>
37+
<Skeleton
38+
classNames={{
39+
base: ['min-w-12 data-[loaded=true]:min-w-0', 'rounded-[4px]'],
40+
content: 'text-tiny',
41+
}}
42+
isLoaded={0 < infoCount.comment || status === 'ready'}
43+
>
44+
{infoCount.comment.toLocaleString('ja-JP')}
45+
</Skeleton>
46+
</div>
47+
)}
4748
</div>
4849
)
4950
}

src/components/slot-item/date-time.tsx

+7-10
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,16 @@ export const DateTime: React.FC<DateTimeProps> = ({ infoDate, isSearch }) => {
1414
return (
1515
<div
1616
className={cn(
17-
'flex flex-row items-center justify-between',
18-
'h-fit shrink-0',
17+
'flex shrink-0 flex-row items-center gap-1',
1918
'text-foreground-500 dark:text-foreground-600'
2019
)}
2120
>
22-
<div className="flex h-full flex-row items-center gap-1">
23-
<CalendarDaysIcon className={isSearch ? 'size-mini' : 'size-tiny'} />
24-
<span className={isSearch ? 'text-mini' : 'text-tiny'}>
25-
{typeof infoDate === 'number'
26-
? formatDate(infoDate, 'YYYY/MM/DD(d) hh:mm')
27-
: `${formatDate(infoDate[0], 'YYYY/MM/DD(d) hh:mm')} 〜`}
28-
</span>
29-
</div>
21+
<CalendarDaysIcon className={isSearch ? 'size-mini' : 'size-tiny'} />
22+
<span className={isSearch ? 'text-mini' : 'text-tiny'}>
23+
{typeof infoDate === 'number'
24+
? formatDate(infoDate, 'YYYY/MM/DD(d) hh:mm')
25+
: `${formatDate(infoDate[0], 'YYYY/MM/DD(d) hh:mm')} 〜`}
26+
</span>
3027
</div>
3128
)
3229
}

src/components/slot-item/index.tsx

+69-26
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import type { StateSlotDetail } from '@/ncoverlay/state'
1+
import type { JikkyoChannelId } from '@midra/nco-api/types/constants'
2+
import type {
3+
StateSlot,
4+
StateSlotDetail,
5+
StateSlotDetailUpdate,
6+
} from '@/ncoverlay/state'
27

38
import { useCallback, useState } from 'react'
49
import { cn } from '@nextui-org/react'
510

611
import { getNiconicoComments } from '@/utils/api/getNiconicoComments'
12+
import { getJikkyoKakologs } from '@/utils/api/getJikkyoKakologs'
713

814
import { ncoState } from '@/hooks/useNco'
915

@@ -42,29 +48,64 @@ export const SlotItem: React.FC<SlotItemProps> = ({
4248

4349
await ncoState?.set('status', 'loading')
4450

45-
const { id } = detail
51+
const { type, id, info } = detail
4652

47-
const [comment] = await getNiconicoComments([{ contentId: id }])
53+
let slotDetail: StateSlotDetailUpdate | undefined
54+
let slot: StateSlot | undefined
4855

49-
if (comment) {
50-
const { data, threads } = comment
56+
if (type === 'jikkyo') {
57+
const [comment] = await getJikkyoKakologs([
58+
{
59+
jkChId: id.split(':')[0] as JikkyoChannelId,
60+
starttime: info.date[0] / 1000,
61+
endtime: info.date[1] / 1000,
62+
},
63+
])
64+
65+
if (comment) {
66+
const { thread, markers } = comment
67+
68+
slotDetail = {
69+
id,
70+
status: 'ready',
71+
markers,
72+
info: {
73+
count: {
74+
comment: thread.commentCount,
75+
},
76+
},
77+
}
5178

52-
await ncoState?.update('slotDetails', ['id'], {
53-
id,
54-
status: 'ready',
55-
info: {
56-
count: {
57-
view: data.video.count.view,
58-
comment: data.video.count.comment,
79+
slot = { id, threads: [thread] }
80+
}
81+
} else {
82+
const [comment] = await getNiconicoComments([{ contentId: id }])
83+
84+
if (comment) {
85+
const { data, threads } = comment
86+
87+
slotDetail = {
88+
id,
89+
status: 'ready',
90+
info: {
91+
count: {
92+
view: data.video.count.view,
93+
comment: data.video.count.comment,
94+
},
95+
thumbnail:
96+
data.video.thumbnail.largeUrl ||
97+
data.video.thumbnail.middleUrl ||
98+
data.video.thumbnail.url,
5999
},
60-
thumbnail:
61-
data.video.thumbnail.largeUrl ||
62-
data.video.thumbnail.middleUrl ||
63-
data.video.thumbnail.url,
64-
},
65-
})
100+
}
101+
102+
slot = { id, threads }
103+
}
104+
}
66105

67-
await ncoState?.add('slots', { id, threads })
106+
if (slotDetail && slot) {
107+
await ncoState?.update('slotDetails', ['id'], slotDetail)
108+
await ncoState?.add('slots', slot)
68109
} else {
69110
await ncoState?.remove('slotDetails', { id })
70111
}
@@ -93,7 +134,12 @@ export const SlotItem: React.FC<SlotItemProps> = ({
93134
<div
94135
className={cn(
95136
'relative flex flex-row p-1',
96-
isSearch ? 'h-[5.125rem] gap-1.5' : 'h-[5.75rem] gap-2'
137+
isSearch
138+
? [
139+
'gap-1.5',
140+
detail.type === 'jikkyo' ? 'h-[4.25rem]' : 'h-[5.125rem]',
141+
]
142+
: 'h-[5.75rem] gap-2'
97143
)}
98144
>
99145
<div
@@ -112,10 +158,8 @@ export const SlotItem: React.FC<SlotItemProps> = ({
112158
/>
113159

114160
{isSearch ? (
115-
detail.type !== 'jikkyo' && (
116-
// 追加
117-
<AddButton onPress={onPressAdd} />
118-
)
161+
// 追加
162+
<AddButton onPress={onPressAdd} />
119163
) : (
120164
<>
121165
<ButtonsOverlay status={detail.status} onRemove={onPressRemove} />
@@ -146,8 +190,7 @@ export const SlotItem: React.FC<SlotItemProps> = ({
146190

147191
<div
148192
className={cn(
149-
'flex flex-row items-center justify-between',
150-
'h-fit shrink-0',
193+
'flex shrink-0 flex-row items-center justify-between',
151194
'text-foreground-500 dark:text-foreground-600'
152195
)}
153196
>

src/components/slot-item/options.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export const Options: React.FC<OptionsProps> = ({ isOpen, id, offsetMs }) => {
104104
variants={transitionVariants}
105105
onKeyDown={(e) => e.stopPropagation()}
106106
>
107-
<div className="border-t-1 border-divider p-2">
107+
<div className="border-t-1 border-foreground-200 p-2">
108108
<SlotOffsetControl id={id} offsetMs={offsetMs} />
109109
</div>
110110
</m.div>

src/constants/settings/default.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ export const SETTINGS_DEFAULT: SettingItems = {
1717
'settings:capture:format': 'jpeg',
1818
'settings:capture:method': 'window',
1919

20-
// 検索
21-
'settings:search:sort': '-startTime',
22-
'settings:search:dateRange': [null, null],
23-
'settings:search:genre': 'アニメ',
24-
'settings:search:lengthRange': [null, null],
25-
2620
// コメント
2721
'settings:comment:fps': 60,
2822
'settings:comment:opacity': 100,
@@ -55,4 +49,10 @@ export const SETTINGS_DEFAULT: SettingItems = {
5549

5650
// プラグイン
5751
'settings:plugins': [],
52+
53+
// 検索 (設定には非表示)
54+
'settings:search:sort': '-startTime',
55+
'settings:search:dateRange': [null, null],
56+
'settings:search:genre': 'アニメ',
57+
'settings:search:lengthRange': [null, null],
5858
}

0 commit comments

Comments
 (0)