mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-14 02:50:07 -04:00
Update src/
This commit is contained in:
parent
e411cec545
commit
2a893a827b
33 changed files with 416 additions and 186 deletions
|
@ -13,14 +13,14 @@
|
|||
{$selected.count()} selected
|
||||
</div>
|
||||
<div class="flex space-x-1 sm:space-x-2 items-center">
|
||||
<ResetButton />
|
||||
<SelectAllButton />
|
||||
<DownloadButton />
|
||||
<ResetButton variant="dark" />
|
||||
<SelectAllButton variant="dark" />
|
||||
<DownloadButton variant="dark" />
|
||||
<CloseButton
|
||||
onClick={() => {
|
||||
downloadMode.set(false)
|
||||
}}
|
||||
variant="light"
|
||||
variant="dark"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
class="w-full text-left rounded-md text-sm h-10 flex items-center text-gray-500 dark:text-gray-400 font-normal hover:bg-gray-100 dark:hover:bg-primary-750 space-x-3 px-2 border border-transparent cursor-pointer"
|
||||
{...$$restProps}
|
||||
>
|
||||
<div class="w-5 h-5 flex items-center justify-center">
|
||||
<div class="w-5 flex shrink-0 items-center justify-center">
|
||||
<slot name="left" />
|
||||
</div>
|
||||
<div class="w-full">{label}</div>
|
||||
<div>
|
||||
<div class="w-4 flex shrink-0">
|
||||
<slot name="right" />
|
||||
</div>
|
||||
</button>
|
||||
|
|
23
src/components/ChannelMenu.svelte
Normal file
23
src/components/ChannelMenu.svelte
Normal file
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { ChannelRemoveButton, ChannelEditButton, CopyLinkButton, Menu } from '~/components'
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import type { Channel } from '~/models'
|
||||
|
||||
export let channel: Channel
|
||||
|
||||
let isMenuOpened = false
|
||||
function closeMenu() {
|
||||
isMenuOpened = false
|
||||
}
|
||||
|
||||
function onLinkCopy() {
|
||||
toast.push('Link copied to clipboard')
|
||||
closeMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu bind:isOpened={isMenuOpened}>
|
||||
<CopyLinkButton link={channel.getPageUrl()} onCopy={onLinkCopy} />
|
||||
<ChannelEditButton {channel} onClick={closeMenu} />
|
||||
<ChannelRemoveButton {channel} onClick={closeMenu} />
|
||||
</Menu>
|
|
@ -1,20 +1,16 @@
|
|||
<script lang="ts">
|
||||
import type { Context } from 'svelte-simple-modal'
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import { getContext } from 'svelte'
|
||||
import { Channel } from '~/models'
|
||||
import {
|
||||
ChannelRemoveButton,
|
||||
ShareChannelButton,
|
||||
ChannelEditButton,
|
||||
CopyLinkButton,
|
||||
BlockedBadge,
|
||||
CloseButton,
|
||||
ClosedBadge,
|
||||
ChannelMenu,
|
||||
HTMLPreview,
|
||||
Popup,
|
||||
Card,
|
||||
Menu
|
||||
Card
|
||||
} from '~/components'
|
||||
|
||||
export let channel: Channel
|
||||
|
@ -29,16 +25,6 @@
|
|||
close()
|
||||
}
|
||||
}
|
||||
|
||||
let isMenuOpened = false
|
||||
function closeMenu() {
|
||||
isMenuOpened = false
|
||||
}
|
||||
|
||||
function onLinkCopy() {
|
||||
toast.push('Link copied to clipboard')
|
||||
closeMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup onClose={close}>
|
||||
|
@ -58,11 +44,7 @@
|
|||
{#if isTouchDevice}
|
||||
<ShareChannelButton {channel} />
|
||||
{/if}
|
||||
<Menu bind:isOpened={isMenuOpened}>
|
||||
<CopyLinkButton link={channel.getPageUrl()} onCopy={onLinkCopy} />
|
||||
<ChannelEditButton {channel} onClick={closeMenu} />
|
||||
<ChannelRemoveButton {channel} onClick={closeMenu} />
|
||||
</Menu>
|
||||
<ChannelMenu {channel} />
|
||||
<CloseButton onClick={close} />
|
||||
</div>
|
||||
<div slot="body" class="pt-4 pb-3 px-4 sm:py-9 sm:px-11">
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
import { selected } from '~/store'
|
||||
import * as Icon from '~/icons'
|
||||
|
||||
export let variant = 'default'
|
||||
|
||||
const playlistCreator = new PlaylistCreator()
|
||||
|
||||
function onClick() {
|
||||
|
@ -52,7 +54,7 @@
|
|||
disabled={!$selected.count()}
|
||||
aria-label="Download Playlist"
|
||||
title="Download Playlist"
|
||||
variant="light"
|
||||
{variant}
|
||||
>
|
||||
<Icon.Download size={16} />
|
||||
</IconButton>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
const params = qs.stringify({
|
||||
labels: 'feeds:add',
|
||||
template: '4_feeds_add.yml',
|
||||
title: 'Add: ',
|
||||
title: `Add: ${channel.name} Feed`,
|
||||
channel_id: channel.id
|
||||
})
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<Button onClick={_onClick} label="Add feed">
|
||||
<Icon.Add slot="left" class="text-gray-400" size={20} />
|
||||
<Button onClick={_onClick} label="Add Feed">
|
||||
<Icon.Add slot="left" class="text-gray-400" size={19} />
|
||||
<Icon.ExternalLink slot="right" class="text-gray-400 dark:text-gray-500" size={17} />
|
||||
</Button>
|
||||
|
|
28
src/components/FeedAddIconButton.svelte
Normal file
28
src/components/FeedAddIconButton.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<script lang="ts">
|
||||
import IconButton from '~/components/IconButton.svelte'
|
||||
import type { Channel } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
import qs from 'qs'
|
||||
|
||||
export let channel: Channel
|
||||
export let onClick = () => {}
|
||||
|
||||
const endpoint = 'https://github.com/iptv-org/database/issues/new'
|
||||
const params = qs.stringify({
|
||||
labels: 'feeds:add',
|
||||
template: '4_feeds_add.yml',
|
||||
title: `Add: ${channel.name} Feed`,
|
||||
channel_id: channel.id
|
||||
})
|
||||
|
||||
const url = `${endpoint}?${params}`
|
||||
|
||||
function _onClick() {
|
||||
window.open(url, '_blank')
|
||||
onClick()
|
||||
}
|
||||
</script>
|
||||
|
||||
<IconButton onClick={_onClick} title="Add Feed">
|
||||
<Icon.AddCircle class="text-gray-400" size={20} />
|
||||
</IconButton>
|
|
@ -1,20 +1,16 @@
|
|||
<script lang="ts">
|
||||
import type { Context } from 'svelte-simple-modal'
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import { getContext } from 'svelte'
|
||||
import { page } from '$app/state'
|
||||
import * as Icon from '~/icons'
|
||||
import { Feed } from '~/models'
|
||||
import {
|
||||
FeedRemoveButton,
|
||||
CopyLinkButton,
|
||||
FeedEditButton,
|
||||
ExpandButton,
|
||||
StreamsPopup,
|
||||
HTMLPreview,
|
||||
GuidesPopup,
|
||||
CodeBlock,
|
||||
Menu
|
||||
FeedMenu
|
||||
} from '~/components'
|
||||
|
||||
export let feed: Feed
|
||||
|
@ -28,7 +24,7 @@
|
|||
function showGuides() {
|
||||
modal.open(
|
||||
GuidesPopup,
|
||||
{ guides: feed.getGuides(), title: 'Guides' },
|
||||
{ feed },
|
||||
{ transitionBgProps: { duration: 0 }, transitionWindowProps: { duration: 0 } }
|
||||
)
|
||||
}
|
||||
|
@ -36,7 +32,7 @@
|
|||
function showStreams() {
|
||||
modal.open(
|
||||
StreamsPopup,
|
||||
{ streams: feed.getStreams(), title: 'Streams' },
|
||||
{ feed },
|
||||
{ transitionBgProps: { duration: 0 }, transitionWindowProps: { duration: 0 } }
|
||||
)
|
||||
}
|
||||
|
@ -45,16 +41,6 @@
|
|||
modal.close()
|
||||
onClose()
|
||||
}
|
||||
|
||||
let isMenuOpened = false
|
||||
function closeMenu() {
|
||||
isMenuOpened = false
|
||||
}
|
||||
|
||||
function onLinkCopy() {
|
||||
toast.push('Link copied to clipboard')
|
||||
closeMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full rounded-md border border-gray-200 dark:border-gray-700" id={feed.id}>
|
||||
|
@ -86,23 +72,19 @@
|
|||
<button
|
||||
onclick={showGuides}
|
||||
class="text-sm text-gray-400 inline-flex space-x-1 flex items-center hover:text-blue-500 dark:hover:text-blue-400 cursor-pointer"
|
||||
title="Streams"
|
||||
title="Guides"
|
||||
>
|
||||
<Icon.Guide size={20} />
|
||||
<div>{feed.getGuides().count()}</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<Menu bind:isOpened={isMenuOpened}>
|
||||
<CopyLinkButton link={feed.getPageUrl()} onCopy={onLinkCopy} />
|
||||
<FeedEditButton {feed} onClick={closeMenu} />
|
||||
<FeedRemoveButton {feed} onClick={closeMenu} />
|
||||
</Menu>
|
||||
<FeedMenu {feed} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if isExpanded}
|
||||
<div class="w-full flex px-6 py-6">
|
||||
<div class="w-full flex px-6 pt-5 pb-2">
|
||||
<HTMLPreview fieldset={feed.getFieldset()} onClick={_onClose} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
30
src/components/FeedMenu.svelte
Normal file
30
src/components/FeedMenu.svelte
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script lang="ts">
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import type { Feed } from '~/models'
|
||||
import {
|
||||
FeedRemoveButton,
|
||||
StreamAddButton,
|
||||
CopyLinkButton,
|
||||
FeedEditButton,
|
||||
Menu
|
||||
} from '~/components'
|
||||
|
||||
export let feed: Feed
|
||||
|
||||
let isMenuOpened = false
|
||||
function closeMenu() {
|
||||
isMenuOpened = false
|
||||
}
|
||||
|
||||
function onLinkCopy() {
|
||||
toast.push('Link copied to clipboard')
|
||||
closeMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu bind:isOpened={isMenuOpened}>
|
||||
<CopyLinkButton link={feed.getPageUrl()} onCopy={onLinkCopy} />
|
||||
<StreamAddButton {feed} onClick={closeMenu} />
|
||||
<FeedEditButton {feed} onClick={closeMenu} />
|
||||
<FeedRemoveButton {feed} onClick={closeMenu} />
|
||||
</Menu>
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { Popup, Card, Menu, FeedAddButton, CloseButton } from '~/components'
|
||||
import { Popup, Card, FeedAddIconButton, CloseButton } from '~/components'
|
||||
import { Collection } from '@freearhey/core/browser'
|
||||
import type { Context } from 'svelte-simple-modal'
|
||||
import type { Channel, Feed } from '~/models'
|
||||
|
@ -17,11 +17,6 @@
|
|||
)
|
||||
|
||||
const { close } = getContext<Context>('simple-modal')
|
||||
|
||||
let isMenuOpened = false
|
||||
function closeMenu() {
|
||||
isMenuOpened = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup onClose={close}>
|
||||
|
@ -37,9 +32,7 @@
|
|||
</span>{channel.getDisplayName()}
|
||||
</div>
|
||||
<div slot="headerRight" class="inline-flex">
|
||||
<Menu bind:isOpened={isMenuOpened}>
|
||||
<FeedAddButton {channel} onClick={closeMenu} />
|
||||
</Menu>
|
||||
<FeedAddIconButton {channel} />
|
||||
<CloseButton onClick={close} />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { CloseButton, GuideItem, Popup, Card } from '~/components'
|
||||
import { Collection } from '@freearhey/core/browser'
|
||||
import type { Context } from 'svelte-simple-modal'
|
||||
import type { Feed } from '~/models'
|
||||
import { getContext } from 'svelte'
|
||||
import * as Icon from '~/icons'
|
||||
|
||||
export let feed: Feed
|
||||
export let title = 'Guides'
|
||||
export let guides: Collection = new Collection()
|
||||
|
||||
const { close } = getContext<Context>('simple-modal')
|
||||
</script>
|
||||
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div slot="body" class="p-2 sm:p-5 w-full">
|
||||
<div class="dark:border-gray-700 rounded-md border border-gray-200">
|
||||
{#each guides.all() as guide}
|
||||
{#each feed.getGuides().all() as guide}
|
||||
<GuideItem {guide} />
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -8,74 +8,76 @@
|
|||
<table class="table-fixed w-full">
|
||||
<tbody>
|
||||
{#each fieldset as field}
|
||||
<tr>
|
||||
<td class="align-top w-[140px] sm:w-[200px]">
|
||||
<div class="flex pr-5 pb-3 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400">
|
||||
{field.name}
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-top w-full overflow-hidden">
|
||||
<div class="pb-3 text-sm text-gray-900 dark:text-gray-100">
|
||||
{#if field.type === 'image'}
|
||||
<img
|
||||
src={field.value.src}
|
||||
alt={field.value.alt}
|
||||
title={field.value.title}
|
||||
referrerpolicy="no-referrer"
|
||||
class="border rounded-sm overflow-hidden border-gray-200 bg-[#e6e6e6]"
|
||||
/>
|
||||
{:else if field.type === 'link'}
|
||||
<div class="truncate">
|
||||
<a
|
||||
href="/?q={field.value.query}"
|
||||
onclick={onClick}
|
||||
class="underline hover:text-blue-400"
|
||||
title={field.value.label}
|
||||
>
|
||||
{field.value.label}
|
||||
</a>
|
||||
</div>
|
||||
{:else if field.type === 'link[]'}
|
||||
<div class="overflow-hidden text-ellipsis">
|
||||
{#each field.value as value, i}
|
||||
{#if i > 0}<span>, </span>
|
||||
{/if}
|
||||
{#if field}
|
||||
<tr>
|
||||
<td class="align-top w-[135px] sm:w-[200px]">
|
||||
<div class="flex pr-5 pb-3 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400">
|
||||
{field.name}
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-top w-full overflow-hidden">
|
||||
<div class="pb-3 text-sm text-gray-900 dark:text-gray-100">
|
||||
{#if field.type === 'image'}
|
||||
<img
|
||||
src={field.value.src}
|
||||
alt={field.value.alt}
|
||||
title={field.value.title}
|
||||
referrerpolicy="no-referrer"
|
||||
class="border rounded-sm overflow-hidden border-gray-200 bg-[#e6e6e6]"
|
||||
/>
|
||||
{:else if field.type === 'link'}
|
||||
<div class="truncate">
|
||||
<a
|
||||
href="/?q={value.query}"
|
||||
href="/?q={field.value.query}"
|
||||
onclick={onClick}
|
||||
class="underline hover:text-blue-400"
|
||||
title={value.label}
|
||||
title={field.value.label}
|
||||
>
|
||||
{value.label}
|
||||
{field.value.label}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if field.type === 'external_link'}
|
||||
<div class="truncate">
|
||||
<a
|
||||
href={field.value.href}
|
||||
class="underline hover:text-blue-400"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={field.value.title}>{field.value.label}</a
|
||||
>
|
||||
</div>
|
||||
{:else if field.name === 'id'}
|
||||
<span class="break-all" title={field.value.toString()}>{field.value}</span>
|
||||
{:else if field.type === 'string[]'}
|
||||
<div class="overflow-hidden text-ellipsis">
|
||||
{#each field.value as value, i}
|
||||
{#if i > 0}<span>, </span>
|
||||
{/if}
|
||||
<span title={value.toString()}>{value}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if field.type === 'string'}
|
||||
<span title={field.title}>{field.value}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
{:else if field.type === 'link[]'}
|
||||
<div class="overflow-hidden text-ellipsis">
|
||||
{#each field.value as value, i}
|
||||
{#if i > 0}<span>, </span>
|
||||
{/if}
|
||||
<a
|
||||
href="/?q={value.query}"
|
||||
onclick={onClick}
|
||||
class="underline hover:text-blue-400"
|
||||
title={value.label}
|
||||
>
|
||||
{value.label}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if field.type === 'external_link'}
|
||||
<div class="truncate">
|
||||
<a
|
||||
href={field.value.href}
|
||||
class="underline hover:text-blue-400"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={field.value.title}>{field.value.label}</a
|
||||
>
|
||||
</div>
|
||||
{:else if field.name === 'id'}
|
||||
<span class="break-all" title={field.value.toString()}>{field.value}</span>
|
||||
{:else if field.type === 'string[]'}
|
||||
<div class="overflow-hidden text-ellipsis">
|
||||
{#each field.value as value, i}
|
||||
{#if i > 0}<span>, </span>
|
||||
{/if}
|
||||
<span title={value.toString()}>{value}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if field.type === 'string'}
|
||||
<span class="break-words" title={field.title}>{field.value}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
export let variant = 'default'
|
||||
export let size = 40
|
||||
|
||||
let className = 'rounded-lg text-sm flex items-center justify-center cursor-pointer shrink-0'
|
||||
if (variant === 'light') className += ' hover:bg-primary-810 text-gray-300'
|
||||
else className += ' hover:bg-gray-100 dark:hover:bg-primary-750 text-gray-400'
|
||||
let className =
|
||||
'rounded-lg text-sm flex items-center justify-center cursor-pointer shrink-0 text-gray-400'
|
||||
if (variant === 'dark') className += ' hover:bg-primary-750'
|
||||
else if (variant === 'light') className += ' hover:bg-gray-100'
|
||||
else className += ' hover:bg-gray-100 dark:hover:bg-primary-750'
|
||||
</script>
|
||||
|
||||
<button
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
</script>
|
||||
|
||||
<div class="relative" use:clickOutside on:outside={closeMenu}>
|
||||
<IconButton onClick={toggleMenu} aria-label="Menu">
|
||||
<IconButton onClick={toggleMenu} aria-label="Menu" title="Menu">
|
||||
<Icon.Menu size={16} />
|
||||
</IconButton>
|
||||
|
||||
{#if isOpened}
|
||||
<div
|
||||
class="rounded-md bg-white dark:bg-primary-810 absolute top-11 right-0 w-48 z-10 p-1 border border-gray-200 dark:border-primary-750"
|
||||
class="rounded-md bg-white dark:bg-primary-810 absolute top-10 right-0 w-48 z-10 p-1 border border-gray-200 dark:border-primary-750"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
import { selected } from '~/store'
|
||||
import * as Icon from '~/icons'
|
||||
|
||||
export let variant = 'default'
|
||||
|
||||
let isAnySelected = true
|
||||
|
||||
selected.subscribe((_selected: Collection) => {
|
||||
|
@ -16,7 +18,7 @@
|
|||
</script>
|
||||
|
||||
{#if isAnySelected}
|
||||
<IconButton onClick={reset} aria-label="Reset" title="Reset" variant="light">
|
||||
<IconButton onClick={reset} aria-label="Reset" title="Reset" {variant}>
|
||||
<Icon.Reset size={24} />
|
||||
</IconButton>
|
||||
{/if}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
import { Channel } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
|
||||
export let variant = 'default'
|
||||
|
||||
const channelsWithStreams: Collection = $channels.filter((channel: Channel) =>
|
||||
channel.hasStreams()
|
||||
)
|
||||
|
@ -91,11 +93,11 @@
|
|||
<Icon.Spinner size={21} />
|
||||
</div>
|
||||
{:else if isAllSelected}
|
||||
<IconButton onClick={deselectAll} aria-label="Deselect All" title="Deselect All" variant="light">
|
||||
<IconButton onClick={deselectAll} aria-label="Deselect All" title="Deselect All" {variant}>
|
||||
<Icon.DeselectAll size={24} />
|
||||
</IconButton>
|
||||
{:else}
|
||||
<IconButton onClick={selectAll} aria-label="Select All" title="Select All" variant="light">
|
||||
<IconButton onClick={selectAll} aria-label="Select All" title="Select All" {variant}>
|
||||
<Icon.SelectAll size={24} />
|
||||
</IconButton>
|
||||
{/if}
|
||||
|
|
29
src/components/StreamAddButton.svelte
Normal file
29
src/components/StreamAddButton.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
import Button from '~/components/Button.svelte'
|
||||
import type { Feed } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
import qs from 'qs'
|
||||
|
||||
export let feed: Feed
|
||||
export let onClick = () => {}
|
||||
|
||||
const endpoint = 'https://github.com/iptv-org/iptv/issues/new'
|
||||
const params = qs.stringify({
|
||||
labels: 'streams:add',
|
||||
template: '1_streams_add.yml',
|
||||
title: `Add: ${feed.getDisplayName()}`,
|
||||
stream_id: feed.getStreamId()
|
||||
})
|
||||
|
||||
const url = `${endpoint}?${params}`
|
||||
|
||||
function _onClick() {
|
||||
window.open(url, '_blank')
|
||||
onClick()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button onClick={_onClick} label="Add Stream">
|
||||
<Icon.Stream slot="left" class="text-gray-400" size={19} />
|
||||
<Icon.ExternalLink slot="right" class="text-gray-400 dark:text-gray-500" size={17} />
|
||||
</Button>
|
28
src/components/StreamAddIconButton.svelte
Normal file
28
src/components/StreamAddIconButton.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<script lang="ts">
|
||||
import IconButton from '~/components/IconButton.svelte'
|
||||
import type { Feed } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
import qs from 'qs'
|
||||
|
||||
export let feed: Feed
|
||||
export let onClick = () => {}
|
||||
|
||||
const endpoint = 'https://github.com/iptv-org/iptv/issues/new'
|
||||
const params = qs.stringify({
|
||||
labels: 'streams:add',
|
||||
template: '1_streams_add.yml',
|
||||
title: `Add: ${feed.getDisplayName()}`,
|
||||
stream_id: feed.getStreamId()
|
||||
})
|
||||
|
||||
const url = `${endpoint}?${params}`
|
||||
|
||||
function _onClick() {
|
||||
window.open(url, '_blank')
|
||||
onClick()
|
||||
}
|
||||
</script>
|
||||
|
||||
<IconButton onClick={_onClick} title="Add Stream">
|
||||
<Icon.AddCircle class="text-gray-400" size={20} />
|
||||
</IconButton>
|
29
src/components/StreamEditButton.svelte
Normal file
29
src/components/StreamEditButton.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
import Button from '~/components/Button.svelte'
|
||||
import type { Stream } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
import qs from 'qs'
|
||||
|
||||
export let stream: Stream
|
||||
export let onClick = () => {}
|
||||
|
||||
const endpoint = 'https://github.com/iptv-org/iptv/issues/new'
|
||||
const params = qs.stringify({
|
||||
labels: 'streams:edit',
|
||||
template: '2_streams_edit.yml',
|
||||
title: `Edit: ${stream.getDisplayName()}`,
|
||||
stream_url: stream.url
|
||||
})
|
||||
|
||||
const editUrl = `${endpoint}?${params}`
|
||||
|
||||
function _onClick() {
|
||||
window.open(editUrl, '_blank')
|
||||
onClick()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button onClick={_onClick} label="Edit">
|
||||
<Icon.Edit slot="left" class="text-gray-400" size={16} />
|
||||
<Icon.ExternalLink slot="right" class="text-gray-400 dark:text-gray-500" size={17} />
|
||||
</Button>
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { CopyToClipboard, ExpandButton, JsonDataViewer } from '~/components'
|
||||
import { StreamMenu, ExpandButton, HTMLPreview } from '~/components'
|
||||
import { Stream } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
|
||||
|
@ -8,11 +8,9 @@
|
|||
let isExpanded = false
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="w-full bg-gray-100 dark:bg-primary-750 dark:border-gray-600 rounded-md border border-gray-200"
|
||||
>
|
||||
<div class="w-full rounded-md border border-gray-200 dark:border-gray-700">
|
||||
<div
|
||||
class="w-full inline-flex justify-between pl-2 pr-3 py-2 border-gray-200 dark:border-gray-600"
|
||||
class="w-full inline-flex justify-between px-2 py-1.5 border-gray-200 dark:border-gray-700"
|
||||
class:border-b={isExpanded}
|
||||
>
|
||||
<div class="flex space-x-2 items-center w-full">
|
||||
|
@ -33,14 +31,14 @@
|
|||
<Icon.ExternalLink size={17} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-8 justify-end shrink-0">
|
||||
<CopyToClipboard text={stream.url} />
|
||||
<div class="flex w-9 justify-end shrink-0">
|
||||
<StreamMenu {stream} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if isExpanded}
|
||||
<div class="w-full flex px-2 py-4">
|
||||
<JsonDataViewer fieldset={stream.getFieldset()} />
|
||||
<div class="w-full flex px-6 pt-5 pb-2">
|
||||
<HTMLPreview fieldset={stream.getFieldset()} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
23
src/components/StreamMenu.svelte
Normal file
23
src/components/StreamMenu.svelte
Normal file
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { CopyLinkButton, Menu, StreamEditButton, StreamReportButton } from '~/components'
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import type { Stream } from '~/models'
|
||||
|
||||
export let stream: Stream
|
||||
|
||||
let isMenuOpened = false
|
||||
function closeMenu() {
|
||||
isMenuOpened = false
|
||||
}
|
||||
|
||||
function onLinkCopy() {
|
||||
toast.push('Link copied to clipboard')
|
||||
closeMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu bind:isOpened={isMenuOpened}>
|
||||
<CopyLinkButton link={stream.url} onCopy={onLinkCopy} />
|
||||
<StreamEditButton {stream} onClick={closeMenu} />
|
||||
<StreamReportButton {stream} onClick={closeMenu} />
|
||||
</Menu>
|
29
src/components/StreamReportButton.svelte
Normal file
29
src/components/StreamReportButton.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
import Button from '~/components/Button.svelte'
|
||||
import type { Stream } from '~/models'
|
||||
import * as Icon from '~/icons'
|
||||
import qs from 'qs'
|
||||
|
||||
export let stream: Stream
|
||||
export let onClick = () => {}
|
||||
|
||||
const endpoint = 'https://github.com/iptv-org/iptv/issues/new'
|
||||
const params = qs.stringify({
|
||||
labels: 'streams:remove',
|
||||
template: '3_streams_report.yml',
|
||||
title: `Report: ${stream.getDisplayName()}`,
|
||||
stream_url: stream.url
|
||||
})
|
||||
|
||||
const editUrl = `${endpoint}?${params}`
|
||||
|
||||
function _onClick() {
|
||||
window.open(editUrl, '_blank')
|
||||
onClick()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button onClick={_onClick} label="Report">
|
||||
<Icon.Alert slot="left" class="text-gray-400" size={17} />
|
||||
<Icon.ExternalLink slot="right" class="text-gray-400 dark:text-gray-500" size={17} />
|
||||
</Button>
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { CloseButton, StreamItem, Popup, Card } from '~/components'
|
||||
import { Collection } from '@freearhey/core/browser'
|
||||
import { CloseButton, StreamItem, Popup, Card, StreamAddIconButton } from '~/components'
|
||||
import type { Context } from 'svelte-simple-modal'
|
||||
import type { Feed } from '~/models'
|
||||
import { getContext } from 'svelte'
|
||||
import * as Icon from '~/icons'
|
||||
|
||||
export let streams: Collection = new Collection()
|
||||
export let feed: Feed
|
||||
export let title = 'Streams'
|
||||
|
||||
const { close } = getContext<Context>('simple-modal')
|
||||
|
@ -23,11 +23,12 @@
|
|||
<Icon.Stream size={21} />
|
||||
</span>{title}
|
||||
</div>
|
||||
<div slot="headerRight">
|
||||
<div slot="headerRight" class="inline-flex">
|
||||
<StreamAddIconButton {feed} />
|
||||
<CloseButton onClick={() => close()} />
|
||||
</div>
|
||||
<div slot="body" class="flex flex-col gap-2 p-2 sm:p-5">
|
||||
{#each streams.all() as stream, index (stream.getUUID())}
|
||||
{#each feed.getStreams().all() as stream (stream.getUUID())}
|
||||
<StreamItem {stream} />
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ export { default as Card } from './Card.svelte'
|
|||
export { default as ChannelEditButton } from './ChannelEditButton.svelte'
|
||||
export { default as ChannelGrid } from './ChannelGrid.svelte'
|
||||
export { default as ChannelItem } from './ChannelItem.svelte'
|
||||
export { default as ChannelMenu } from './ChannelMenu.svelte'
|
||||
export { default as ChannelPopup } from './ChannelPopup.svelte'
|
||||
export { default as ChannelRemoveButton } from './ChannelRemoveButton.svelte'
|
||||
export { default as Checkbox } from './Checkbox.svelte'
|
||||
|
@ -20,8 +21,10 @@ export { default as CreatePlaylistButton } from './CreatePlaylistButton.svelte'
|
|||
export { default as DownloadButton } from './DownloadButton.svelte'
|
||||
export { default as ExpandButton } from './ExpandButton.svelte'
|
||||
export { default as FeedAddButton } from './FeedAddButton.svelte'
|
||||
export { default as FeedAddIconButton } from './FeedAddIconButton.svelte'
|
||||
export { default as FeedEditButton } from './FeedEditButton.svelte'
|
||||
export { default as FeedItem } from './FeedItem.svelte'
|
||||
export { default as FeedMenu } from './FeedMenu.svelte'
|
||||
export { default as FeedPopup } from './FeedPopup.svelte'
|
||||
export { default as FeedRemoveButton } from './FeedRemoveButton.svelte'
|
||||
export { default as GitHubButton } from './GitHubButton.svelte'
|
||||
|
@ -40,6 +43,11 @@ export { default as SearchField } from './SearchField.svelte'
|
|||
export { default as SearchSyntaxPopup } from './SearchSyntaxPopup.svelte'
|
||||
export { default as SelectAllButton } from './SelectAllButton.svelte'
|
||||
export { default as ShareChannelButton } from './ShareChannelButton.svelte'
|
||||
export { default as StreamAddButton } from './StreamAddButton.svelte'
|
||||
export { default as StreamAddIconButton } from './StreamAddIconButton.svelte'
|
||||
export { default as StreamEditButton } from './StreamEditButton.svelte'
|
||||
export { default as StreamReportButton } from './StreamReportButton.svelte'
|
||||
export { default as StreamItem } from './StreamItem.svelte'
|
||||
export { default as StreamMenu } from './StreamMenu.svelte'
|
||||
export { default as StreamsPopup } from './StreamsPopup.svelte'
|
||||
export { default as ToggleModeButton } from './ToggleModeButton.svelte'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue