refactor intro selector
parent
b9e9822ca6
commit
1e68c465c9
|
@ -0,0 +1,2 @@
|
||||||
|
<h3>Download New Intro</h3>
|
||||||
|
<input>
|
|
@ -1,16 +1,57 @@
|
||||||
<script>
|
<script>
|
||||||
import { intros } from './store.ts';
|
import { intros } from './store.ts';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
|
import { fade, fly, crossfade } from 'svelte/transition';
|
||||||
|
import { quintOut } from 'svelte/easing';
|
||||||
|
import { flip } from 'svelte/animate';
|
||||||
|
|
||||||
export let guild = null;
|
export let guild = null;
|
||||||
export let channel = null;
|
export let channel = null;
|
||||||
|
|
||||||
export let guildIntros = null;
|
|
||||||
export let introList = null;
|
|
||||||
export let exclude = null;
|
export let exclude = null;
|
||||||
|
export let include = null;
|
||||||
export let btnLabel = 'Add';
|
export let btnLabel = 'Add';
|
||||||
export let emptyMsg = null;
|
export let emptyMsg = null;
|
||||||
|
|
||||||
|
let filteredIntroList = [];
|
||||||
|
|
||||||
|
const getFiltered = () => {
|
||||||
|
console.log("GET FILTERD");
|
||||||
|
const guildIntros = Array.from($intros.get(guild).entries()).map(([index, intro]) => {
|
||||||
|
return {
|
||||||
|
index: index,
|
||||||
|
...intro,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return guildIntros.filter((e) => {
|
||||||
|
if (!!exclude) {
|
||||||
|
for(const item of exclude) {
|
||||||
|
if (e.index == item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (!!include) {
|
||||||
|
for(const item of include) {
|
||||||
|
if (e.index == item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$: if (!!($intros.get(guild)) && (!!include || !!exclude)) {
|
||||||
|
filteredIntroList = getFiltered();
|
||||||
|
console.log(filteredIntroList);
|
||||||
|
};
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
|
@ -22,34 +63,37 @@
|
||||||
selectedIntros = [];
|
selectedIntros = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [send, receive] = crossfade({
|
||||||
|
duration: (d) => Math.sqrt(d * 20),
|
||||||
|
|
||||||
|
fallback(node, params) {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const transform = style.transform === 'none' ? '' : style.transform;
|
||||||
|
|
||||||
|
return {
|
||||||
|
duration: 200,
|
||||||
|
easing: quintOut,
|
||||||
|
css: (t) => `
|
||||||
|
transform: ${transform} scale(${t});
|
||||||
|
opacity: ${t}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let selectedIntros = [];
|
let selectedIntros = [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="list">
|
<div id="list">
|
||||||
{#if !!introList && !!guildIntros}
|
{#if !!filteredIntroList && filteredIntroList.length > 0}
|
||||||
{#if introList.length > 0}
|
{#each filteredIntroList as intro (intro.index)}
|
||||||
{#each introList as intro}
|
<label out:send={{ key: intro.index }} in:receive={{ key: intro.index }} animate:flip={{ duration: 200 }} id="list-item">
|
||||||
<label id="list-item">
|
<input type="checkbox" bind:group={selectedIntros} name="selectedIntros" value={intro.index}>
|
||||||
<input type="checkbox" bind:group={selectedIntros} name="selectedIntros" value={intro.index}>
|
{intro.name}
|
||||||
{guildIntros[intro.index].name} ({!!guildIntros[intro.index].filename ? guildIntros[intro.index].filename : guildIntros[intro.index].url})
|
</label>
|
||||||
</label>
|
|
||||||
{/each}
|
|
||||||
{:else if !!emptyMsg}
|
|
||||||
<p style='color: yellow'>{emptyMsg}</p>
|
|
||||||
{:else}
|
|
||||||
<p>No intros</p>
|
|
||||||
{/if}
|
|
||||||
{:else if !!exclude && !!guildIntros}
|
|
||||||
{#each guildIntros as intro, i}
|
|
||||||
{#if (!exclude.map((e) => e.index).includes(i))}
|
|
||||||
<label id="list-item">
|
|
||||||
<input type="checkbox" bind:group={selectedIntros} name="selectedIntros" value={i}>
|
|
||||||
{intro.name} ({!!intro.filename ? intro.filename : intro.url})
|
|
||||||
</label>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
<p>No intros</p>
|
<p style="color: red">{emptyMsg}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<button on:click={onConfirm}>{btnLabel}</button>
|
<button on:click={onConfirm}>{btnLabel}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import { member, intros } from './store.ts';
|
import { member, intros } from './store.ts';
|
||||||
|
|
||||||
const authorizeUri = "https://discord.com/api/oauth2/authorize?client_id=577634620728934400&redirect_uri=http%3A%2F%2Flocalhost%3A5173%2Fauth&response_type=code&scope=identify%20guilds%20guilds.members.read";
|
const authorizeUri = "https://discord.com/api/oauth2/authorize?client_id=577634620728934400&redirect_uri=http%3A%2F%2Flocalhost%3A5173%2Fauth&response_type=code&scope=identify%20guilds%20guilds.members.read";
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const token = window.localStorage.getItem('token');
|
||||||
|
|
||||||
|
if (!!token) {
|
||||||
|
await member.pullData(token);
|
||||||
|
await intros.fetchIntros($member.guilds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const login = async (username) => {
|
const login = async (username) => {
|
||||||
window.location = authorizeUri;
|
window.location = authorizeUri;
|
||||||
//await member._fakeLogin(username);
|
|
||||||
//await intros.fetchIntros($member.guilds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let loginPromise = null;
|
let loginPromise = null;
|
||||||
|
|
16
src/app.html
16
src/app.html
|
@ -12,6 +12,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #313052;
|
background-color: #313052;
|
||||||
|
font-family: 'Cantarell';
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, p, label, li {
|
h1, h2, h3, h4, h5, p, label, li {
|
||||||
|
@ -49,22 +50,27 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#list-item {
|
button, #list-item {
|
||||||
border-style: solid;
|
color: lightgray;
|
||||||
|
border-style: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border-color: #1f1f36;
|
background-image: linear-gradient(0deg, #1f1f36, #23233d);
|
||||||
background-color: #1f1f36;
|
|
||||||
padding: 0.5em 0.5em 0.5em;
|
padding: 0.5em 0.5em 0.5em;
|
||||||
margin: 0 0 16px 0px;
|
margin: 0 0 16px 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:hover, #list-item:hover {
|
||||||
|
background-image: linear-gradient(0deg, #34345b, #393963);
|
||||||
|
box-shadow: 1px 3px 15px 1px #1f1f36;
|
||||||
|
}
|
||||||
|
|
||||||
#list-item > input[type="checkbox"] {
|
#list-item > input[type="checkbox"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#list-item:has(input[type="checkbox"]:checked) {
|
#list-item:has(input[type="checkbox"]:checked) {
|
||||||
background-color: #40406e;
|
background-image: linear-gradient(0deg, #40406e, #444475);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
type Permission = {
|
||||||
|
None: 1,
|
||||||
|
CanDownload: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const member_can = (member, perm) => {
|
||||||
|
return (member.permission & perm) > 0;
|
||||||
|
};
|
|
@ -1,9 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { member, intros } from '../store.ts';
|
import { member, intros } from '../store.ts';
|
||||||
|
import { member_can } from '../permissions.ts';
|
||||||
import Login from '../Login.svelte';
|
import Login from '../Login.svelte';
|
||||||
import IntroSelector from '../IntroSelector.svelte';
|
import IntroSelector from '../IntroSelector.svelte';
|
||||||
|
import IntroDownloader from '../IntroDownloader.svelte';
|
||||||
|
|
||||||
let addIntroPromise = null;
|
let addIntroPromise = null;
|
||||||
|
let removeIntroPromise = null;
|
||||||
|
|
||||||
const apiAddIntro = async (guild, channel, username, selectedIntros) => {
|
const apiAddIntro = async (guild, channel, username, selectedIntros) => {
|
||||||
for (const intro of selectedIntros) {
|
for (const intro of selectedIntros) {
|
||||||
|
@ -17,7 +20,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await member._fakeLogin(username);
|
await member.pullData($member.token);
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiRemoveIntro = async (guild, channel, username, selectedIntros) => {
|
const apiRemoveIntro = async (guild, channel, username, selectedIntros) => {
|
||||||
|
@ -32,7 +35,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await member._fakeLogin(username);
|
await member.pullData($member.token);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addIntros = (event) => {
|
const addIntros = (event) => {
|
||||||
|
@ -40,7 +43,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
const removeIntros = (event) => {
|
const removeIntros = (event) => {
|
||||||
addIntroPromise = apiRemoveIntro(event.detail.guild, event.detail.channel, $member.username, event.detail.intros);
|
removeIntroPromise = apiRemoveIntro(event.detail.guild, event.detail.channel, $member.username, event.detail.intros);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -53,19 +56,23 @@
|
||||||
<div id="intros">
|
<div id="intros">
|
||||||
{#each $member.guilds as guild}
|
{#each $member.guilds as guild}
|
||||||
<h4>{guild.name}</h4>
|
<h4>{guild.name}</h4>
|
||||||
|
<IntroDownloader />
|
||||||
<div id="guild-settings">
|
<div id="guild-settings">
|
||||||
{#each guild.channels as channel}
|
{#each guild.channels as channel}
|
||||||
<div id="channel-settings">
|
<div id="channel-settings">
|
||||||
<h4>{channel.name}</h4>
|
<h4>{channel.name}</h4>
|
||||||
{#await addIntroPromise then result}
|
{#await addIntroPromise then result}
|
||||||
{:catch err}
|
{:catch err}
|
||||||
<p style='color: red'>Failed to add intro: {err}</p>
|
<p style='color: red'>Failed to add intro</p>
|
||||||
|
{/await}
|
||||||
|
{#await removeIntroPromise then result}
|
||||||
|
{:catch err}
|
||||||
|
<p style='color: red'>Failed to add remove</p>
|
||||||
{/await}
|
{/await}
|
||||||
<IntroSelector
|
<IntroSelector
|
||||||
guildIntros={$intros[guild.name]}
|
|
||||||
guild={guild.name}
|
guild={guild.name}
|
||||||
channel={channel.name}
|
channel={channel.name}
|
||||||
introList={channel.intros}
|
include={channel.intros.map((x) => x.index)}
|
||||||
on:confirm={removeIntros}
|
on:confirm={removeIntros}
|
||||||
btnLabel="Remove"
|
btnLabel="Remove"
|
||||||
emptyMsg="You don't have any intros, try adding one"
|
emptyMsg="You don't have any intros, try adding one"
|
||||||
|
@ -73,11 +80,11 @@
|
||||||
|
|
||||||
<h3>Add Intros</h3>
|
<h3>Add Intros</h3>
|
||||||
<IntroSelector
|
<IntroSelector
|
||||||
guildIntros={$intros[guild.name]}
|
|
||||||
guild={guild.name}
|
guild={guild.name}
|
||||||
channel={channel.name}
|
channel={channel.name}
|
||||||
exclude={channel.intros}
|
exclude={channel.intros.map((x) => x.index)}
|
||||||
on:confirm={addIntros}
|
on:confirm={addIntros}
|
||||||
|
emptyMsg="There are no intros"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
loginFailed = true
|
loginFailed = true
|
||||||
} else {
|
} else {
|
||||||
await member.login(body.token);
|
await member.pullData(body.token);
|
||||||
await intros.fetchIntros($member.guilds);
|
await intros.fetchIntros($member.guilds);
|
||||||
|
|
||||||
|
window.localStorage.setItem('token', body.token);
|
||||||
|
|
||||||
goto('/')
|
goto('/')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
39
src/store.ts
39
src/store.ts
|
@ -1,26 +1,14 @@
|
||||||
import { readable, writable } from 'svelte/store';
|
import { readable, writable } from 'svelte/store';
|
||||||
|
|
||||||
type IntroIndex = number;
|
|
||||||
|
|
||||||
interface MemberStore {
|
function createMemberStore() {
|
||||||
username: string
|
|
||||||
intros: IntroIndex[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Intro {
|
|
||||||
name: string
|
|
||||||
filename: string
|
|
||||||
length: number
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMemberStore(): MemberStore {
|
|
||||||
const { subscribe, set, update } = writable(null)
|
const { subscribe, set, update } = writable(null)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: subscribe,
|
subscribe: subscribe,
|
||||||
set: set,
|
set: set,
|
||||||
addIntro: (intro: IntroIndex) => { update((n) => n.intros.push(intro)); return intro },
|
addIntro: (intro: IntroIndex) => { update((n) => n.intros.push(intro)); return intro },
|
||||||
login: async (token) => {
|
pullData: async (token) => {
|
||||||
const response = (await (await fetch(
|
const response = (await (await fetch(
|
||||||
'http://localhost:7756/me',
|
'http://localhost:7756/me',
|
||||||
{ headers: {"token": token} })).json())
|
{ headers: {"token": token} })).json())
|
||||||
|
@ -34,37 +22,36 @@ function createMemberStore(): MemberStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createIntroStore(): IntroStore {
|
function createIntroStore() {
|
||||||
const { subscribe, set, update } = writable({})
|
const { subscribe, set, update } = writable(new Map())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: subscribe,
|
subscribe: subscribe,
|
||||||
set: set,
|
|
||||||
update: update,
|
|
||||||
fetchIntros: async (guilds) => {
|
fetchIntros: async (guilds) => {
|
||||||
console.debug('Fetching intros');
|
console.debug('Fetching intros');
|
||||||
console.log(guilds)
|
console.log(guilds)
|
||||||
|
|
||||||
let intros = {};
|
let intros = new Map();
|
||||||
|
|
||||||
for (const guild of guilds) {
|
for (const guild of guilds) {
|
||||||
const response = (await (await fetch(`http://localhost:7756/intros/${guild.name}`)).json())
|
const response = (await (await fetch(`http://localhost:7756/intros/${guild.name}`)).json())
|
||||||
if (response !== "NoGuildFound") {
|
if (response !== "NoGuildFound") {
|
||||||
const guild_intros = response.Intros.map((intro) => {
|
let guild_intros = new Map();
|
||||||
|
|
||||||
|
Object.entries(response.Intros).forEach(([index, intro]) => {
|
||||||
if (!!intro.File) {
|
if (!!intro.File) {
|
||||||
return { name: intro.File.friendlyName, filename: intro.File.filename }
|
guild_intros.set(index, { name: intro.File.friendlyName, filename: intro.File.filename });
|
||||||
} else if (!!intro.Online) {
|
} else if (!!intro.Online) {
|
||||||
return { name: intro.Online.friendlyName, url: intro.Online.url }
|
guild_intros.set(index, { name: intro.Online.friendlyName, filename: intro.Online.filename });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
intros[guild.name] = guild_intros;
|
intros.set(guild.name, guild_intros);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug('Setting Intros store');
|
console.debug('Setting Intros store');
|
||||||
console.debug(intros);
|
console.debug(intros);
|
||||||
console.debug(intros[123]);
|
|
||||||
set(intros)
|
set(intros)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +59,6 @@ function createIntroStore(): IntroStore {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const intros: IntroStore = createIntroStore()
|
export const intros = createIntroStore()
|
||||||
export const member: MemberStore = createMemberStore()
|
export const member = createMemberStore()
|
||||||
|
|
||||||
|
|
Reference in New Issue