Skip to content

Commit 22c2053

Browse files
committed
かわいい率追加, レイアウト調節
1 parent 95a0950 commit 22c2053

18 files changed

+193
-80
lines changed

src/components/SlotItem/AutoLoadedBadge.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,26 @@ import type { StateSlotDetail } from '@/ncoverlay/state'
33
import { cn } from '@nextui-org/react'
44

55
export type AutoLoadedBadgeProps = {
6+
className?: string
67
isAutoLoaded: StateSlotDetail['isAutoLoaded']
78
}
89

910
export const AutoLoadedBadge: React.FC<AutoLoadedBadgeProps> = ({
11+
className,
1012
isAutoLoaded,
1113
}) => {
1214
if (isAutoLoaded) return
1315

1416
return (
1517
<div
1618
className={cn(
17-
'absolute bottom-[3px] left-[3px] z-10',
1819
'px-1 py-[1px]',
1920
'border-1 border-gray-800/50',
2021
'rounded-md',
2122
'text-mini',
2223
'bg-gray-100 text-gray-800',
23-
'select-none'
24+
'select-none',
25+
className
2426
)}
2527
>
2628
手動

src/components/SlotItem/ButtonsOverlay.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const ButtonsOverlay: React.FC<ButtonsOverlayProps> = ({
2525
{/* 削除 */}
2626
<Button
2727
className={cn(
28-
'absolute right-[2px] top-[2px]',
28+
'absolute right-[1px] top-[1px]',
2929
'!size-6 min-h-0 min-w-0',
3030
'border-1 border-white/80'
3131
)}

src/components/SlotItem/Counts.tsx

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { StateSlotDetail } from '@/ncoverlay/state'
22

3-
import { Skeleton } from '@nextui-org/react'
4-
import { PlayIcon, MessageSquareTextIcon } from 'lucide-react'
3+
import { useMemo } from 'react'
4+
import { cn, Skeleton } from '@nextui-org/react'
5+
import { PlayIcon, MessageSquareTextIcon, HeartIcon } from 'lucide-react'
6+
7+
import { useSettings } from '@/hooks/useSettings'
58

69
export type CountsProps = {
710
status: StateSlotDetail['status']
@@ -14,8 +17,22 @@ export const Counts: React.FC<CountsProps> = ({
1417
infoCount,
1518
isSearch,
1619
}) => {
20+
const [showKawaiiPct] = useSettings('settings:showKawaiiPct')
21+
22+
const kawaiiPct = useMemo(() => {
23+
if (!showKawaiiPct || !infoCount.kawaii) return
24+
25+
return Math.round((infoCount.kawaii / infoCount.comment) * 100 * 10) / 10
26+
}, [showKawaiiPct, infoCount.comment, infoCount.kawaii])
27+
1728
return (
18-
<div className="flex flex-row gap-4">
29+
<div
30+
className={cn(
31+
'flex flex-row gap-3',
32+
'shrink-0',
33+
'text-foreground-500 dark:text-foreground-600'
34+
)}
35+
>
1936
{/* 再生数 */}
2037
{'view' in infoCount && (
2138
<div className="flex flex-row items-center gap-1">
@@ -45,6 +62,17 @@ export const Counts: React.FC<CountsProps> = ({
4562
{infoCount.comment.toLocaleString('ja-JP')}
4663
</Skeleton>
4764
</div>
65+
66+
{/* かわいい率 */}
67+
{kawaiiPct && (
68+
<div className="flex flex-row items-center gap-1">
69+
<HeartIcon className={isSearch ? 'size-mini' : 'size-tiny'} />
70+
71+
<span className={isSearch ? 'text-mini' : 'text-tiny'}>
72+
{kawaiiPct}%
73+
</span>
74+
</div>
75+
)}
4876
</div>
4977
)
5078
}

src/components/SlotItem/Duration.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@ import { cn } from '@nextui-org/react'
55
import { formatDuration } from '@/utils/format'
66

77
export type DurationProps = {
8-
infoDuration: StateSlotDetail['info']['duration']
8+
className?: string
9+
duration: StateSlotDetail['info']['duration']
910
}
1011

11-
export const Duration: React.FC<DurationProps> = ({ infoDuration }) => {
12+
export const Duration: React.FC<DurationProps> = ({ className, duration }) => {
1213
return (
1314
<div
1415
className={cn(
15-
'absolute bottom-[3px] right-[3px] z-10',
1616
'px-1 py-[1px]',
1717
'border-1 border-white/25',
1818
'rounded-md',
1919
'text-mini',
2020
'bg-black/50 text-white backdrop-blur-md',
21-
'select-none'
21+
'select-none',
22+
className
2223
)}
2324
>
24-
{formatDuration(infoDuration)}
25+
{formatDuration(duration)}
2526
</div>
2627
)
2728
}

src/components/SlotItem/Offset.tsx

+22-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
11
import type { StateSlotDetail } from '@/ncoverlay/state'
22

3+
import { cn } from '@nextui-org/react'
34
import { ClockIcon } from 'lucide-react'
45

56
export type OffsetProps = {
7+
className?: string
68
offsetMs: StateSlotDetail['offsetMs']
79
}
810

9-
export const Offset: React.FC<OffsetProps> = ({ offsetMs }) => {
10-
const ofs = Math.round((offsetMs ?? 0) / 1000)
11+
export const Offset: React.FC<OffsetProps> = ({ className, offsetMs }) => {
12+
const offset = Math.round((offsetMs ?? 0) / 1000)
1113

1214
return (
13-
ofs !== 0 && (
14-
<div className="flex h-full flex-row items-center gap-1">
15-
<ClockIcon className="size-tiny" />
16-
<span className="text-tiny">
17-
{0 < ofs && '+'}
18-
{ofs.toLocaleString()}
15+
offset !== 0 && (
16+
<div
17+
className={cn(
18+
'flex flex-row items-center gap-0.5',
19+
'px-1 py-[1px]',
20+
'border-1 border-white/25',
21+
'rounded-md',
22+
'text-mini',
23+
'bg-black/50 text-white backdrop-blur-md',
24+
'select-none',
25+
className
26+
)}
27+
>
28+
<ClockIcon className="size-mini" />
29+
30+
<span>
31+
{0 < offset && '+'}
32+
{offset.toLocaleString()}
1933
</span>
2034
</div>
2135
)

src/components/SlotItem/SourceBadge.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { StateSlotDetail } from '@/ncoverlay/state'
33
import { cn } from '@nextui-org/react'
44

55
export type SourceTagProps = {
6+
className?: string
67
type: StateSlotDetail['type']
78
}
89

@@ -26,19 +27,19 @@ const SOURCE_BADGE_NAME: {
2627
jikkyo: '実況',
2728
}
2829

29-
export const SourceBadge: React.FC<SourceTagProps> = ({ type }) => {
30+
export const SourceBadge: React.FC<SourceTagProps> = ({ className, type }) => {
3031
if (type === 'normal') return
3132

3233
return (
3334
<div
3435
className={cn(
35-
'absolute left-[3px] top-[3px] z-10',
3636
'px-1 py-[1px]',
3737
'border-1 border-white/80',
3838
'rounded-md',
3939
'text-mini',
4040
'select-none',
41-
SOURCE_BADGE_CLASSES[type]
41+
SOURCE_BADGE_CLASSES[type],
42+
className
4243
)}
4344
>
4445
{SOURCE_BADGE_NAME[type]}

src/components/SlotItem/Thumbnail.tsx

+25-5
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { JIKKYO_CHANNELS } from '@midra/nco-api/constants'
77

88
import { SourceBadge } from './SourceBadge'
99
import { AutoLoadedBadge } from './AutoLoadedBadge'
10+
import { Offset } from './Offset'
1011
import { Duration } from './Duration'
1112

1213
export type ThumbnailProps = {
1314
id: StateSlotDetail['id']
1415
type: StateSlotDetail['type']
16+
offsetMs: StateSlotDetail['offsetMs']
1517
isAutoLoaded: StateSlotDetail['isAutoLoaded']
1618
info: StateSlotDetail['info']
1719
isSearch?: boolean
@@ -20,6 +22,7 @@ export type ThumbnailProps = {
2022
export const Thumbnail: React.FC<ThumbnailProps> = ({
2123
id,
2224
type,
25+
offsetMs,
2326
isAutoLoaded,
2427
info,
2528
isSearch,
@@ -45,6 +48,7 @@ export const Thumbnail: React.FC<ThumbnailProps> = ({
4548
<span className={cn('absolute top-[4px]', 'text-mini text-white/80')}>
4649
{jkChId}
4750
</span>
51+
4852
<span
4953
className={cn(
5054
'line-clamp-1',
@@ -74,14 +78,30 @@ export const Thumbnail: React.FC<ThumbnailProps> = ({
7478
<>
7579
{thumbnail}
7680

77-
{/* ソース */}
78-
<SourceBadge type={type} />
81+
<div
82+
className={cn(
83+
'absolute left-[2px] top-[2px] z-10',
84+
'flex flex-col items-start gap-[1px]'
85+
)}
86+
>
87+
{/* ソース */}
88+
<SourceBadge type={type} />
7989

80-
{/* 自動 / 手動 */}
81-
{!isSearch && <AutoLoadedBadge isAutoLoaded={isAutoLoaded} />}
90+
{/* 自動 / 手動 */}
91+
{!isSearch && <AutoLoadedBadge isAutoLoaded={isAutoLoaded} />}
92+
</div>
93+
94+
{/* オフセット */}
95+
<Offset
96+
className="absolute bottom-[2px] left-[2px] z-10"
97+
offsetMs={offsetMs}
98+
/>
8299

83100
{/* 長さ */}
84-
<Duration infoDuration={info.duration} />
101+
<Duration
102+
className="absolute bottom-[2px] right-[2px] z-10"
103+
duration={info.duration}
104+
/>
85105
</>
86106
)
87107
}

src/components/SlotItem/index.tsx

+13-21
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { Thumbnail } from './Thumbnail'
2323
import { DateTime } from './DateTime'
2424
import { Title } from './Title'
2525
import { Counts } from './Counts'
26-
import { Offset } from './Offset'
2726
import { HideButton } from './HideButton'
2827
import { TranslucentButton } from './TranslucentButton'
2928
import { Options, OptionsButton } from './Options'
@@ -68,7 +67,7 @@ export const SlotItem: React.FC<SlotItemProps> = ({
6867
])
6968

7069
if (comment) {
71-
const { thread, markers } = comment
70+
const { thread, markers, kawaiiCount } = comment
7271

7372
slotDetail = {
7473
id,
@@ -77,6 +76,7 @@ export const SlotItem: React.FC<SlotItemProps> = ({
7776
info: {
7877
count: {
7978
comment: thread.commentCount,
79+
kawaii: kawaiiCount,
8080
},
8181
},
8282
}
@@ -87,7 +87,7 @@ export const SlotItem: React.FC<SlotItemProps> = ({
8787
const [comment] = await getNiconicoComments([{ contentId: id }])
8888

8989
if (comment) {
90-
const { data, threads } = comment
90+
const { data, threads, kawaiiCount } = comment
9191

9292
slotDetail = {
9393
id,
@@ -96,6 +96,7 @@ export const SlotItem: React.FC<SlotItemProps> = ({
9696
count: {
9797
view: data.video.count.view,
9898
comment: data.video.count.comment,
99+
kawaii: kawaiiCount,
99100
},
100101
thumbnail:
101102
data.video.thumbnail.largeUrl ||
@@ -168,6 +169,7 @@ export const SlotItem: React.FC<SlotItemProps> = ({
168169
<Thumbnail
169170
id={detail.id}
170171
type={detail.type}
172+
offsetMs={detail.offsetMs}
171173
isAutoLoaded={detail.isAutoLoaded}
172174
info={detail.info}
173175
isSearch={isSearch}
@@ -204,24 +206,14 @@ export const SlotItem: React.FC<SlotItemProps> = ({
204206
isSearch={isSearch}
205207
/>
206208

207-
<div
208-
className={cn(
209-
'flex shrink-0 flex-row items-center justify-between',
210-
'text-foreground-500 dark:text-foreground-600'
211-
)}
212-
>
213-
{/* 再生数・コメント数 */}
214-
{!(detail.type === 'jikkyo' && isSearch) && (
215-
<Counts
216-
status={detail.status}
217-
infoCount={detail.info.count}
218-
isSearch={isSearch}
219-
/>
220-
)}
221-
222-
{/* オフセット */}
223-
{!isError && !isSearch && <Offset offsetMs={detail.offsetMs} />}
224-
</div>
209+
{/* 再生数 / コメント数 / かわいい率 */}
210+
{!(detail.type === 'jikkyo' && isSearch) && (
211+
<Counts
212+
status={detail.status}
213+
infoCount={detail.info.count}
214+
isSearch={isSearch}
215+
/>
216+
)}
225217
</div>
226218

227219
{/* サイドボタン */}

src/constants/index.ts

+11
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,14 @@ export const NICONICO_COLOR_COMMANDS: Record<string, string> = {
7272
export const COLOR_CODE = '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$'
7373

7474
export const COLOR_CODE_REGEXP = new RegExp(COLOR_CODE)
75+
76+
export const KAWAII_REGEXP = new RegExp(
77+
[
78+
'(可愛|かわい)(い|すぎ|過ぎ)',
79+
'かわ(ぃぃ|E)',
80+
'(カワ|カワ)(イイ|イイ|ィィ|ィィ)',
81+
'kawaii',
82+
'かーいー',
83+
].join('|'),
84+
'i'
85+
)

src/constants/settings/default.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ import { VOD_KEYS } from '../vods'
88
export const SETTINGS_DEFAULT: SettingItems = {
99
// 全般
1010
'settings:theme': 'auto',
11-
'settings:showChangelog': true,
12-
13-
// 動画配信サービス
1411
'settings:vods': [...VOD_KEYS.filter((v) => v !== 'niconico')],
15-
16-
// キャプチャー
1712
'settings:capture:format': 'jpeg',
1813
'settings:capture:method': 'window',
14+
'settings:showChangelog': true,
15+
'settings:showKawaiiPct': false,
1916

2017
// コメント
2118
'settings:comment:fps': 60,

src/constants/settings/init-data.ts

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ export const SETTINGS_INIT_DATA: SettingsInitData = [
7979
label: '更新内容を表示',
8080
description: 'アップデート後に更新内容を新しいタブで開きます。',
8181
},
82+
{
83+
settingsKey: 'settings:showKawaiiPct',
84+
inputType: 'toggle',
85+
label: 'かわいい率を表示',
86+
description:
87+
'コメント数の右側にかわいい率(かわいいコメの出現率)を表示します。',
88+
},
8289
],
8390
},
8491
{

0 commit comments

Comments
 (0)