@@ -6,6 +6,7 @@ import { useRecoilValue } from "recoil"
6
6
import { lastEpgUpdatedAtom } from "../../../atoms/contentPlayer"
7
7
import { useNow } from "../../../hooks/date"
8
8
import { ChannelType , Program , Service } from "../../../infra/mirakurun/api"
9
+ import { convertVariationSelectedClosed } from "../../../utils/enclosed"
9
10
import { EscapeEnclosed } from "../../common/EscapeEnclosed"
10
11
11
12
export const ControllerSidebar : React . FC < {
@@ -106,6 +107,96 @@ export const ControllerSidebar: React.FC<{
106
107
) ) }
107
108
</ div >
108
109
< div className = { clsx ( "overflow-auto" ) } >
110
+ < div
111
+ className = { clsx ( "grid" , "grid-cols-2" , "gap-2" , "lg:grid-cols-3" ) }
112
+ >
113
+ { Object . values (
114
+ targetServices . reduce (
115
+ ( services : Record < string , Service [ ] > , service ) => {
116
+ const identifier =
117
+ service . channel ?. type === "CS"
118
+ ? service . id
119
+ : service . remoteControlKeyId ??
120
+ service . channel ?. channel ??
121
+ service . id
122
+ if ( ! identifier ) {
123
+ return services
124
+ }
125
+ if ( ! services [ identifier ] ) {
126
+ services [ identifier ] = [ service ]
127
+ } else {
128
+ services [ identifier ] . push ( service )
129
+ }
130
+ return services
131
+ } ,
132
+ { }
133
+ )
134
+ )
135
+ . sort (
136
+ ( a , b ) =>
137
+ ( a [ 0 ] . remoteControlKeyId ?? a [ 0 ] . serviceId ) -
138
+ ( b [ 0 ] . remoteControlKeyId ?? b [ 0 ] . serviceId )
139
+ )
140
+ . map ( ( services ) => {
141
+ const service = services [ 0 ]
142
+ const programs = queriedPrograms
143
+ . filter (
144
+ ( program ) =>
145
+ program . serviceId === service . serviceId &&
146
+ program . networkId === service . networkId
147
+ )
148
+ . sort ( ( a , b ) => a . startAt - b . startAt )
149
+ const current = programs ?. [ 0 ]
150
+ return (
151
+ < button
152
+ key = { service . id }
153
+ type = "button"
154
+ className = { clsx (
155
+ "bg-gray-800" ,
156
+ "bg-opacity-70" ,
157
+ "rounded-md" ,
158
+ "flex" ,
159
+ "flex-col" ,
160
+ "truncate" ,
161
+ "p-1" ,
162
+ "cursor-pointer"
163
+ ) }
164
+ onClick = { ( e ) => {
165
+ e . preventDefault ( )
166
+ setService ( service )
167
+ } }
168
+ title = { convertVariationSelectedClosed (
169
+ [ ( service . name , current ?. name ) ]
170
+ . filter ( ( s ) => s )
171
+ . join ( "\n" )
172
+ ) }
173
+ >
174
+ < span
175
+ className = { clsx (
176
+ "flex" ,
177
+ "space-x-2" ,
178
+ "pointer-events-none"
179
+ ) }
180
+ >
181
+ { service . logoData && (
182
+ < img
183
+ className = { clsx ( "h-6" , "rounded-md" , "flex-shrink-0" ) }
184
+ src = { `data:image/jpeg;base64,${ service . logoData } ` }
185
+ />
186
+ ) }
187
+ < span className = { clsx ( "flex-shrink-0" ) } >
188
+ { service . remoteControlKeyId } { service . name }
189
+ </ span >
190
+ </ span >
191
+ { current ?. name && (
192
+ < span className = { clsx ( "pointer-events-none" ) } >
193
+ < EscapeEnclosed str = { current . name || "" } />
194
+ </ span >
195
+ ) }
196
+ </ button >
197
+ )
198
+ } ) }
199
+ </ div >
109
200
{ targetServices . map ( ( service ) => {
110
201
const programs = queriedPrograms
111
202
. filter (
0 commit comments