-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: adicionado indicador de estado
- Loading branch information
1 parent
63e80a7
commit a298933
Showing
12 changed files
with
679 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
type EmitterType = 'state'; | ||
type Callback<T> = (value: CustomEvent<T>) => any; | ||
|
||
type MapValue<T> = { | ||
name: EmitterType; | ||
callback: Callback<T>; | ||
} | ||
|
||
type EmitOptions<T> = { | ||
state: T, | ||
element?: HTMLElement | ||
} | ||
|
||
class EventEmitter<T extends string> { | ||
private emitter: EventTarget; | ||
private map = new Map<Number, MapValue<T>>(); | ||
private next = 0; | ||
|
||
constructor() { | ||
this.emitter = new EventTarget(); | ||
} | ||
|
||
public emit({ state, element }: EmitOptions<T>) { | ||
if (element) { | ||
element.setAttribute('kashi', state); | ||
} | ||
this.emitter.dispatchEvent(new CustomEvent<T>('state', { | ||
detail: state | ||
})); | ||
} | ||
|
||
public on(name: EmitterType, callback: Callback<T>) { | ||
const value = this.next++; | ||
this.map.set(value, { name, callback }); | ||
this.emitter.addEventListener(name, callback); | ||
return value; | ||
} | ||
|
||
public once(name: EmitterType, callback: Callback<T>) { | ||
const value = this.next++; | ||
this.map.set(value, { name, callback }); | ||
this.emitter.addEventListener(name, callback, { | ||
once: true | ||
}); | ||
return value; | ||
} | ||
|
||
public off(value: number) { | ||
if(this.map.has(value)) { | ||
const { name, callback} = this.map.get(value); | ||
this.emitter.removeEventListener(name, callback); | ||
} | ||
} | ||
} | ||
|
||
export default EventEmitter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import React from 'react'; | ||
import { createRoot } from 'react-dom/client' | ||
|
||
import { Lyric, romanize } from './services'; | ||
import { LyricsState } from './react/hooks/lyrics-state'; | ||
import { Root } from './react/Indicator.style'; | ||
import Indicator, { IndicatorProps } from './react/Indicator'; | ||
import EventEmitter from './event/EventEmitter'; | ||
|
||
getRootElement().then((array) => { | ||
const portal = document.createElement('div'); | ||
array[0].appendChild(portal).classList.add(Root); | ||
|
||
const react = createRoot(portal); | ||
react.render(React.createElement<IndicatorProps>(Indicator, { | ||
listener: initializeLyricObserver(array[1]) | ||
})) | ||
}); | ||
|
||
function getRootElement() { | ||
return new Promise<Element[]>((resolve) => { | ||
var observer = new MutationObserver((context) => { | ||
const mutation = context[context.length - 1]; | ||
const target = mutation.target as HTMLBodyElement; | ||
const root = target.querySelector('.main-view-container'); | ||
const main = root.querySelector(['.os-padding','main'].join(' ')); | ||
|
||
if (main) { | ||
resolve([root, main]); | ||
observer.disconnect(); | ||
} | ||
}); | ||
|
||
observer.observe(document.body, { | ||
attributes: true, | ||
childList: true, | ||
characterData: true, | ||
}); | ||
}); | ||
} | ||
|
||
function initializeLyricObserver(node: Element) { | ||
const emitter = new EventEmitter<LyricsState>(); | ||
const observer = new MutationObserver(async (records) => { | ||
const record = records.filter(({ addedNodes }) => addedNodes.length !== 0) | ||
.map<HTMLElement>((record) => record.target as HTMLElement) | ||
.find((e) => e.querySelector('[data-testid="fullscreen-lyric"]')); | ||
|
||
if (!record) { | ||
emitter.emit({ state: LyricsState.Idle }); | ||
return; | ||
} | ||
|
||
const lyric = record.querySelector('[data-testid="fullscreen-lyric"]') | ||
.parentElement; | ||
if (lyric.hasAttribute('kashi')) { | ||
emitter.emit({ state: LyricsState[lyric.getAttribute('kashi')] }); | ||
return; | ||
} | ||
|
||
const lyrics = Array.from(lyric.childNodes) as HTMLElement[]; | ||
emitter.emit({ | ||
state: LyricsState.Loading, | ||
element: lyric | ||
}); | ||
|
||
await romanize(lyrics | ||
.filter((node) => node.hasAttribute('data-testid')) | ||
.map<Lyric>((value, index) => ({ | ||
index: index, | ||
node: value, | ||
text: value.textContent | ||
}))); | ||
|
||
emitter.emit({ | ||
state: LyricsState.Loaded, | ||
element: lyric | ||
}); | ||
}); | ||
observer.observe(node, { childList: true, subtree: true }); | ||
return emitter; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { css } from '@emotion/css'; | ||
import styled from '@emotion/styled'; | ||
import { motion } from 'framer-motion'; | ||
|
||
export const Root = css` | ||
position: absolute; | ||
bottom: 5px; | ||
right: 2.5%; | ||
` | ||
|
||
export const Container = styled(motion.div)` | ||
background: #8325db; | ||
display: flex; | ||
align-items: center; | ||
gap: 0.5rem; | ||
border-radius: 0.4rem; | ||
padding: 0.2rem 0.6rem; | ||
user-select: none; | ||
box-shadow: 0px 0px 2px 0px #0006; | ||
`; | ||
|
||
Container.defaultProps = { | ||
initial: { | ||
y: -25, | ||
opacity: 0 | ||
}, | ||
animate: { | ||
y: 0, | ||
opacity: 1 | ||
}, | ||
exit: { | ||
y: -25, | ||
opacity: 0 | ||
}, | ||
whileHover: { | ||
opacity: 0.8, | ||
scale: 1.03 | ||
} | ||
}; | ||
|
||
export const Link = styled.a` | ||
cursor: pointer; | ||
`; | ||
|
||
export const Image = styled.img` | ||
width: 22px; | ||
height: 22px; | ||
border-radius: 50%; | ||
display: block; | ||
`; | ||
|
||
export const Text = styled.span` | ||
color: #fff; | ||
font-weight: 600; | ||
font-family: "spotify-circular"; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from 'react'; | ||
import { AnimatePresence } from 'framer-motion'; | ||
import { Container, Image, Link, Text } from './Indicator.style'; | ||
import { LyricsState, useLyricsState } from './hooks/lyrics-state'; | ||
import EventEmitter from '../event/EventEmitter'; | ||
|
||
export type IndicatorProps = { | ||
listener: EventEmitter<LyricsState>; | ||
}; | ||
|
||
const REPOSITORY_URL = 'https://github.com/Cristian-Sknz/kashi-extension'; | ||
|
||
const Indicator: React.FC<IndicatorProps> = ({ listener }) => { | ||
const state = useLyricsState(listener); | ||
|
||
return ( | ||
<AnimatePresence> | ||
{state !== 'Idle' && ( | ||
<Container layout key={state}> | ||
<Link href={REPOSITORY_URL} rel='external' target={'_blank'}> | ||
<Image src={`${REPOSITORY_URL}/raw/master/public/icons/icon48.png`}/> | ||
</Link> | ||
<Text>{state}</Text> | ||
</Container> | ||
)} | ||
</AnimatePresence> | ||
); | ||
}; | ||
|
||
export default Indicator; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { useEffect, useState } from 'react'; | ||
import EventEmitter from '../../event/EventEmitter'; | ||
|
||
export enum LyricsState { | ||
Idle = 'Idle', | ||
Loading = 'Loading', | ||
Loaded = 'Loaded', | ||
}; | ||
|
||
|
||
export function useLyricsState(listener: EventEmitter<LyricsState>) { | ||
const [state, setState] = useState<LyricsState>(LyricsState.Idle); | ||
|
||
useEffect(() => { | ||
const value = listener.on('state', (e) => { | ||
setState(e.detail); | ||
}); | ||
|
||
return () => listener.off(value); | ||
}, [listener]) | ||
|
||
return state; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.