diff --git a/.gitignore b/.gitignore index 8df878e8..3e78d89b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ chrome dist node_modules yarn-error.log +.idea diff --git a/src/post-reaction-component.ts b/src/post-reaction-component.ts new file mode 100644 index 00000000..3c2726f2 --- /dev/null +++ b/src/post-reaction-component.ts @@ -0,0 +1,125 @@ +import { + createIssue as createGitHubIssue, + Issue, + loadIssueByNumber, + ReactionID, + Reactions, + reactionTypes, + toggleReaction, + User +} from './github'; +import { EmptyReactions, reactionEmoji, reactionNames } from './reactions'; +import { pageAttributes as page } from './page-attributes'; + +export class PostReactionComponent { + public readonly element: HTMLElement; + private readonly countAnchor: HTMLAnchorElement; + private readonly reactionListContainer: HTMLFormElement; + private reactions: Reactions = new EmptyReactions(); + private reactionsCount: number = 0; + private issueURL: string = ''; + + constructor( + private user: User | null, + private issue: Issue | null, + private createIssueCallback: (issue: Issue) => Promise + ) { + this.element = document.createElement('section'); + this.element.classList.add('post-reactions'); + this.element.innerHTML = ` +
+ +
+
+
`; + this.countAnchor = this.element.querySelector('header a') as HTMLAnchorElement; + this.reactionListContainer = this.element.querySelector('.post-reaction-list') as HTMLFormElement; + this.setIssue(this.issue) + this.render(); + } + + public setIssue(issue: Issue | null) { + this.issue = issue; + if (issue) { + this.reactions = issue.reactions; + this.reactionsCount = issue.reactions.total_count; + this.issueURL = issue.html_url; + this.render(); + } + } + + private setupSubmitHandler() { + const buttons = this.reactionListContainer.querySelectorAll('button'); + + function toggleButtons(disabled: boolean) { + buttons.forEach(b => b.disabled = disabled); + } + + const handler = async (event: Event) => { + event.preventDefault(); + + const button = event.target as HTMLButtonElement; + toggleButtons(true); + const id = button.value as ReactionID; + const issueExists = !!this.issue; + + if (!issueExists) { + const newIssue = await createGitHubIssue( + page.issueTerm as string, + page.url, + page.title, + page.description || '', + page.label + ); + const issue = await loadIssueByNumber(newIssue.number); + this.issue = issue; + this.reactions = issue.reactions; + await this.createIssueCallback(issue); + } + + const url = this.reactions.url; + const {deleted} = await toggleReaction(url, id); + const delta = deleted ? -1 : 1; + this.reactions[id] += delta; + this.reactions.total_count += delta; + this.issue!.reactions = this.reactions; + toggleButtons(false); + this.setIssue(this.issue); + } + + buttons.forEach(b => b.addEventListener('click', handler, true)) + } + + private getSubmitButtons(): string { + function buttonFor(url: string, reaction: ReactionID, disabled: boolean, count: number): string { + return ` + `; + } + + const issueLocked = this.issue ? this.issue.locked : false; + return reactionTypes + .map(id => buttonFor(this.reactions.url, id, !this.user || issueLocked, this.reactions[id])) + .join('') + } + + private render() { + if (this.issueURL !== '') { + this.countAnchor.href = this.issueURL; + } else { + this.countAnchor.removeAttribute('href'); + } + this.countAnchor.textContent = `${this.reactionsCount} Reaction${this.reactionsCount === 1 ? '' : 's'}`; + this.reactionListContainer.innerHTML = this.getSubmitButtons(); + this.setupSubmitHandler(); + } +} diff --git a/src/reactions.ts b/src/reactions.ts index c9305696..ae2c2be9 100644 --- a/src/reactions.ts +++ b/src/reactions.ts @@ -1,4 +1,4 @@ -import { toggleReaction, ReactionID, reactionTypes } from './github'; +import { ReactionID, Reactions, reactionTypes, toggleReaction } from './github'; import { getLoginUrl } from './oauth'; import { pageAttributes } from './page-attributes'; import { scheduleMeasure } from './measure'; @@ -25,6 +25,20 @@ export const reactionEmoji: Record = { 'eyes': '👀' }; +export class EmptyReactions implements Reactions { + '+1' = 0; + '-1' = 0; + confused = 0; + eyes = 0; + heart = 0; + hooray = 0; + laugh = 0; + rocket = 0; + // tslint:disable-next-line:variable-name + total_count = 0; + url = ''; +} + export function getReactionHtml(url: string, reaction: ReactionID, disabled: boolean, count: number) { return `