morethanwords
5 years ago
35 changed files with 3093 additions and 1911 deletions
@ -0,0 +1,214 @@
@@ -0,0 +1,214 @@
|
||||
import Scrollable from "./scrollable_new"; |
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager"; |
||||
import { $rootScope, cancelEvent, findUpTag, findUpClassName } from "../lib/utils"; |
||||
import appDialogsManager from "../lib/appManagers/appDialogsManager"; |
||||
import appChatsManager from "../lib/appManagers/appChatsManager"; |
||||
import appUsersManager from "../lib/appManagers/appUsersManager"; |
||||
import { appPeersManager } from "../lib/services"; |
||||
import appProfileManager from "../lib/appManagers/appProfileManager"; |
||||
import appPhotosManager from "../lib/appManagers/appPhotosManager"; |
||||
|
||||
export class AppSelectPeers { |
||||
public container = document.createElement('div'); |
||||
private list = document.createElement('ul'); |
||||
private chatsContainer = document.createElement('div'); |
||||
private scrollable: Scrollable; |
||||
private selectedScrollable: Scrollable; |
||||
|
||||
private selectedContainer = document.createElement('div'); |
||||
private input = document.createElement('input'); |
||||
|
||||
private selected: {[peerID: number]: HTMLDivElement} = {}; |
||||
|
||||
public freezed = false; |
||||
|
||||
private myID = $rootScope.myID; |
||||
|
||||
private offsetIndex = 0; |
||||
private promise: Promise<number[]>; |
||||
|
||||
private query = ''; |
||||
private cachedContacts: number[]; |
||||
|
||||
constructor(private appendTo: HTMLDivElement, private onChange?: (length: number) => void, private peerType: 'contacts' | 'dialogs' = 'dialogs') { |
||||
this.container.classList.add('selector'); |
||||
|
||||
let topContainer = document.createElement('div'); |
||||
topContainer.classList.add('selector-search-container'); |
||||
|
||||
this.selectedContainer.classList.add('selector-search'); |
||||
this.input.placeholder = peerType == 'contacts' ? 'Add People...' : 'Select chat'; |
||||
this.input.type = 'text'; |
||||
this.selectedContainer.append(this.input); |
||||
topContainer.append(this.selectedContainer); |
||||
this.selectedScrollable = new Scrollable(topContainer); |
||||
|
||||
let delimiter = document.createElement('hr'); |
||||
|
||||
this.chatsContainer.classList.add('chats-container'); |
||||
this.chatsContainer.append(this.list); |
||||
this.scrollable = new Scrollable(this.chatsContainer); |
||||
|
||||
this.list.addEventListener('click', (e) => { |
||||
let target = e.target as HTMLElement; |
||||
cancelEvent(e); |
||||
|
||||
if(this.freezed) return; |
||||
|
||||
if(target.tagName != 'LI') { |
||||
target = findUpTag(target, 'LI'); |
||||
} |
||||
|
||||
if(!target) return; |
||||
|
||||
let peerID = +target.getAttribute('data-peerID'); |
||||
target.classList.toggle('active'); |
||||
if(peerID in this.selected) { |
||||
this.remove(peerID); |
||||
} else { |
||||
this.add(peerID); |
||||
} |
||||
|
||||
let checkbox = target.querySelector('input') as HTMLInputElement; |
||||
checkbox.checked = !checkbox.checked; |
||||
}); |
||||
|
||||
this.selectedContainer.addEventListener('click', (e) => { |
||||
if(this.freezed) return; |
||||
let target = e.target as HTMLElement; |
||||
target = findUpClassName(target, 'selector-user'); |
||||
|
||||
if(!target) return; |
||||
|
||||
let peerID = target.dataset.peerID; |
||||
let li = this.list.querySelector('[data-peerid="' + peerID + '"]') as HTMLElement; |
||||
li.click(); |
||||
}); |
||||
|
||||
this.input.addEventListener('input', () => { |
||||
let value = this.input.value; |
||||
if(this.query != value) { |
||||
if(this.peerType == 'contacts') { |
||||
this.cachedContacts = null; |
||||
this.promise = null; |
||||
} |
||||
|
||||
this.list.innerHTML = ''; |
||||
this.query = value; |
||||
|
||||
console.log('selectPeers input:', this.query); |
||||
this.getMoreResults(); |
||||
} |
||||
}); |
||||
|
||||
this.scrollable.onScrolledBottom = () => { |
||||
this.getMoreResults(); |
||||
}; |
||||
|
||||
this.container.append(topContainer, delimiter, this.chatsContainer); |
||||
appendTo.append(this.container); |
||||
|
||||
this.getMoreResults(); |
||||
} |
||||
|
||||
private getMoreDialogs() { |
||||
// в десктопе - сначала без группы, потом архивные, потом контакты без сообщений
|
||||
appMessagesManager.getConversations(this.offsetIndex, 50, 0).then(value => { |
||||
let dialogs = value.dialogs; |
||||
|
||||
this.offsetIndex = dialogs[value.dialogs.length - 1].index || 0; |
||||
|
||||
if(dialogs[0].peerID != this.myID) { |
||||
dialogs.findAndSplice(d => d.peerID == this.myID); |
||||
dialogs.unshift({ |
||||
peerID: this.myID, |
||||
pFlags: {} |
||||
} as any); |
||||
} |
||||
|
||||
this.renderResults(dialogs.map(dialog => dialog.peerID)); |
||||
}); |
||||
} |
||||
|
||||
private async getMoreContacts() { |
||||
if(this.promise) return this.promise; |
||||
|
||||
if(!this.cachedContacts) { |
||||
this.promise = appUsersManager.getContacts(this.query); |
||||
this.cachedContacts = (await this.promise).slice(); |
||||
this.cachedContacts.findAndSplice(userID => userID == this.myID); // no my account
|
||||
this.promise = null; |
||||
} |
||||
|
||||
if(this.cachedContacts.length) { |
||||
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; |
||||
let arr = this.cachedContacts.splice(0, pageCount); |
||||
this.renderResults(arr); |
||||
} |
||||
} |
||||
|
||||
private getMoreResults() { |
||||
if(this.peerType == 'dialogs') { |
||||
this.getMoreDialogs(); |
||||
} else { |
||||
this.getMoreContacts(); |
||||
} |
||||
} |
||||
|
||||
private renderResults(peerIDs: number[]) { |
||||
peerIDs.forEach(peerID => { |
||||
let {dom} = appDialogsManager.addDialog(peerID, this.list, false, false); |
||||
dom.containerEl.insertAdjacentHTML('afterbegin', '<div class="checkbox"><label><input type="checkbox"><span></span></label></div>'); |
||||
|
||||
let subtitle = ''; |
||||
if(peerID < 0) { |
||||
subtitle = appChatsManager.getChatMembersString(-peerID); |
||||
} else if(peerID == this.myID) { |
||||
subtitle = 'chat with yourself'; |
||||
} else { |
||||
subtitle = appUsersManager.getUserStatusString(peerID); |
||||
if(subtitle == 'online') { |
||||
subtitle = `<i>${subtitle}</i>`; |
||||
} |
||||
} |
||||
|
||||
dom.lastMessageSpan.innerHTML = subtitle; |
||||
}); |
||||
} |
||||
|
||||
private add(peerID: number) { |
||||
let div = document.createElement('div'); |
||||
div.classList.add('selector-user', 'scale-in'); |
||||
div.dataset.peerID = '' + peerID; |
||||
this.selected[peerID] = div; |
||||
|
||||
let title = appPeersManager.getPeerTitle(peerID, false, true); |
||||
|
||||
let avatarDiv = document.createElement('div'); |
||||
avatarDiv.classList.add('user-avatar', 'tgico'); |
||||
appProfileManager.putPhoto(avatarDiv, peerID); |
||||
|
||||
div.innerHTML = title; |
||||
div.insertAdjacentElement('afterbegin', avatarDiv); |
||||
|
||||
this.selectedContainer.insertBefore(div, this.input); |
||||
this.selectedScrollable.scrollTop = this.selectedScrollable.scrollHeight; |
||||
this.onChange && this.onChange(Object.keys(this.selected).length); |
||||
} |
||||
|
||||
private remove(peerID: number) { |
||||
let div = this.selected[peerID]; |
||||
div.classList.remove('scale-in'); |
||||
void div.offsetWidth; |
||||
div.classList.add('scale-out'); |
||||
div.addEventListener('animationend', () => { |
||||
delete this.selected[peerID]; |
||||
div.remove(); |
||||
this.onChange && this.onChange(Object.keys(this.selected).length); |
||||
}, {once: true}); |
||||
} |
||||
|
||||
public getSelected() { |
||||
return Object.keys(this.selected).map(p => +p); |
||||
} |
||||
} |
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
import appPollsManager, { PollResults } from "../lib/appManagers/appPollsManager"; |
||||
import { RichTextProcessor } from "../lib/richtextprocessor"; |
||||
|
||||
let lineTotalLength = 0; |
||||
const tailLength = 9; |
||||
const times = 10; |
||||
const fullTime = 340; |
||||
const oneTime = fullTime / times; |
||||
|
||||
export default class PollElement extends HTMLElement { |
||||
private svgLines: SVGSVGElement[]; |
||||
private numberDivs: HTMLDivElement[]; |
||||
private maxOffset = -44.8; |
||||
private maxLength: number; |
||||
private maxLengths: number[]; |
||||
|
||||
constructor() { |
||||
super(); |
||||
// элемент создан
|
||||
} |
||||
|
||||
connectedCallback() { |
||||
// браузер вызывает этот метод при добавлении элемента в документ
|
||||
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
||||
|
||||
if(!lineTotalLength) { |
||||
lineTotalLength = (document.getElementById('poll-line') as any as SVGPathElement).getTotalLength(); |
||||
console.log('line total length:', lineTotalLength); |
||||
} |
||||
|
||||
let pollID = this.getAttribute('poll-id'); |
||||
let {poll, results} = appPollsManager.getPoll(pollID); |
||||
|
||||
console.log('pollElement poll:', poll, results); |
||||
|
||||
let desc = ''; |
||||
if(poll.pFlags) { |
||||
if(poll.pFlags.closed) { |
||||
desc = 'Final results'; |
||||
} else { |
||||
desc = poll.pFlags.public_voters ? 'Public Poll' : 'Anonymous Poll'; |
||||
} |
||||
} |
||||
|
||||
let votes = poll.answers.map(answer => { |
||||
return ` |
||||
<div class="poll-answer"> |
||||
<div class="circle-hover"> |
||||
<div class="animation-ring"></div> |
||||
<svg class="progress-ring"> |
||||
<circle class="progress-ring__circle" cx="13" cy="13" r="9"></circle> |
||||
</svg> |
||||
</div> |
||||
<div class="poll-answer-percents"></div> |
||||
<svg version="1.1" class="poll-line" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 480 28" xml:space="preserve"> |
||||
<use href="#poll-line"></use> |
||||
</svg> |
||||
<div class="poll-answer-text">${RichTextProcessor.wrapEmojiText(answer.text)}</div> |
||||
</div> |
||||
`;
|
||||
}).join(''); |
||||
|
||||
this.innerHTML = ` |
||||
<div class="poll-title">${poll.rQuestion}</div> |
||||
<div class="poll-desc">${desc}</div> |
||||
${votes} |
||||
<div class="poll-votes-count">${results.total_voters ? results.total_voters + ' voters' : 'No votes'}</div> |
||||
`;
|
||||
|
||||
let width = this.getBoundingClientRect().width; |
||||
this.maxLength = width + tailLength + this.maxOffset + -9; // 13 - position left
|
||||
this.performResults(results); |
||||
} |
||||
|
||||
disconnectedCallback() { |
||||
// браузер вызывает этот метод при удалении элемента из документа
|
||||
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
||||
} |
||||
|
||||
static get observedAttributes(): string[] { |
||||
return [/* массив имён атрибутов для отслеживания их изменений */]; |
||||
} |
||||
|
||||
attributeChangedCallback(name: string, oldValue: string, newValue: string) { |
||||
// вызывается при изменении одного из перечисленных выше атрибутов
|
||||
} |
||||
|
||||
adoptedCallback() { |
||||
// вызывается, когда элемент перемещается в новый документ
|
||||
// (происходит в document.adoptNode, используется очень редко)
|
||||
} |
||||
|
||||
performResults(results: PollResults) { |
||||
const percents = results.results.map(v => v.voters / results.total_voters * 100); |
||||
this.setResults(percents); |
||||
} |
||||
|
||||
setResults(percents: number[]) { |
||||
if(!this.svgLines) { |
||||
this.svgLines = Array.from(this.querySelectorAll('.poll-line')) as SVGSVGElement[]; |
||||
this.numberDivs = Array.from(this.querySelectorAll('.poll-answer-percents')) as HTMLDivElement[]; |
||||
} |
||||
|
||||
let maxValue = Math.max(...percents); |
||||
|
||||
this.maxLengths = percents.map(p => p / maxValue * this.maxLength); |
||||
|
||||
/* this.svgLines.forEach((svg, idx) => { |
||||
this.setLineProgress(idx, 1); |
||||
}); */ |
||||
|
||||
/* percents = percents.map(p => { |
||||
return Math.round(p); |
||||
}); */ |
||||
|
||||
console.log('setResults before percents:', percents); |
||||
|
||||
let sum = percents.reduce((acc, p) => acc + Math.round(p), 0); |
||||
if(sum > 100) { |
||||
let diff = sum - 100; |
||||
let length = percents.length; |
||||
for(let i = 0; i < diff; ++i) { |
||||
let minIndex = -1, minRemainder = 1; |
||||
for(let k = 0; k < length; ++k) { |
||||
let remainder = percents[k] % 1; |
||||
if(remainder >= 0.5 && remainder < minRemainder) { |
||||
minRemainder = remainder; |
||||
minIndex = k; |
||||
} |
||||
} |
||||
|
||||
if(minIndex == -1) { |
||||
throw new Error('lol chto'); |
||||
} |
||||
|
||||
percents[minIndex] -= minRemainder; |
||||
} |
||||
} else { |
||||
let diff = 100 - sum; |
||||
let length = percents.length; |
||||
for(let i = 0; i < diff; ++i) { |
||||
let minIndex = -1, maxRemainder = 0; |
||||
for(let k = 0; k < length; ++k) { |
||||
let remainder = percents[k] % 1; |
||||
if(remainder < 0.5 && remainder > maxRemainder) { |
||||
maxRemainder = remainder; |
||||
minIndex = k; |
||||
} |
||||
} |
||||
|
||||
if(minIndex == -1) { |
||||
throw new Error('lol chto'); |
||||
} |
||||
|
||||
percents[minIndex] += 1 - maxRemainder; |
||||
} |
||||
} |
||||
|
||||
console.log('setResults after percents:', percents, sum); |
||||
|
||||
let start = Date.now(); |
||||
let r = () => { |
||||
let diff = Date.now() - start; |
||||
let progress = diff / fullTime; |
||||
if(progress > 1) progress = 1; |
||||
|
||||
this.svgLines.forEach((svg, idx) => { |
||||
this.setLineProgress(idx, progress); |
||||
}); |
||||
|
||||
if(progress < 1) { |
||||
window.requestAnimationFrame(r); |
||||
} |
||||
}; |
||||
window.requestAnimationFrame(r); |
||||
|
||||
for(let i = 0; i < times; ++i) { |
||||
setTimeout(() => { |
||||
percents.forEach((percents, idx) => { |
||||
let value = Math.round(percents / times * (i + 1)); |
||||
let div = this.numberDivs[idx]; |
||||
//div.style.opacity = ((i + 1) * 0.10).toFixed(1); // опасити в 10 шагов от 0.1 до 1
|
||||
div.innerText = value + '%'; |
||||
}); |
||||
}, oneTime * i); |
||||
} |
||||
|
||||
this.classList.add('is-voted'); |
||||
} |
||||
|
||||
setLineProgress(index: number, percents: number) { |
||||
let svg = this.svgLines[index]; |
||||
svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9'; |
||||
svg.style.strokeDashoffset = '' + percents * this.maxOffset; |
||||
} |
||||
|
||||
// у элемента могут быть ещё другие методы и свойства
|
||||
} |
||||
|
||||
customElements.define("poll-element", PollElement); |
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
import resizeableImage from "../lib/cropper"; |
||||
import apiFileManager from "../lib/mtproto/apiFileManager"; |
||||
|
||||
export class PopupAvatar { |
||||
private container = document.getElementById('popup-avatar'); |
||||
private input = this.container.querySelector('input') as HTMLInputElement; |
||||
private cropContainer = this.container.querySelector('.crop') as HTMLDivElement; |
||||
private closeBtn = this.container.querySelector('.popup-close') as HTMLButtonElement; |
||||
private image = new Image(); |
||||
|
||||
private canvas: HTMLCanvasElement; |
||||
private blob: Blob; |
||||
private cropper = { |
||||
crop: () => {}, |
||||
removeHandlers: () => {} |
||||
}; |
||||
|
||||
private onCrop: (upload: () => Promise<any>) => void; |
||||
|
||||
constructor() { |
||||
this.container.style.display = ''; // need for no blink
|
||||
this.cropContainer.append(this.image); |
||||
|
||||
this.input.addEventListener('change', (e: any) => { |
||||
var file = e.target.files[0]; |
||||
if(!file) { |
||||
return; |
||||
} |
||||
|
||||
var reader = new FileReader(); |
||||
reader.onload = (e) => { |
||||
var contents = e.target.result as string; |
||||
|
||||
this.image = new Image(); |
||||
this.cropContainer.append(this.image); |
||||
this.image.src = contents; |
||||
|
||||
this.image.onload = () => { |
||||
/* let {w, h} = calcImageInBox(this.image.naturalWidth, this.image.naturalHeight, 460, 554); |
||||
cropContainer.style.width = w + 'px'; |
||||
cropContainer.style.height = h + 'px'; */ |
||||
this.container.classList.remove('hide'); |
||||
void this.container.offsetWidth; // reflow
|
||||
this.container.classList.add('active'); |
||||
|
||||
this.cropper = resizeableImage(this.image, this.canvas); |
||||
this.input.value = ''; |
||||
}; |
||||
}; |
||||
|
||||
reader.readAsDataURL(file); |
||||
}, false); |
||||
|
||||
// apply
|
||||
this.container.querySelector('.btn-crop').addEventListener('click', () => { |
||||
this.cropper.crop(); |
||||
this.closeBtn.click(); |
||||
|
||||
this.canvas.toBlob(blob => { |
||||
this.blob = blob; // save blob to send after reg
|
||||
|
||||
// darken
|
||||
let ctx = this.canvas.getContext('2d'); |
||||
ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; |
||||
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); |
||||
|
||||
this.resolve(); |
||||
}, 'image/jpeg', 1); |
||||
}); |
||||
|
||||
this.closeBtn.addEventListener('click', () => { |
||||
setTimeout(() => { |
||||
this.cropper.removeHandlers(); |
||||
if(this.image) { |
||||
this.image.remove(); |
||||
} |
||||
|
||||
this.container.classList.add('hide'); |
||||
}, 200); |
||||
}); |
||||
} |
||||
|
||||
private resolve() { |
||||
this.onCrop(() => { |
||||
return apiFileManager.uploadFile(this.blob); |
||||
}); |
||||
} |
||||
|
||||
public open(postCanvas: HTMLCanvasElement, onCrop: (upload: () => Promise<any>) => void) { |
||||
this.canvas = postCanvas; |
||||
this.onCrop = onCrop; |
||||
|
||||
this.input.click(); |
||||
} |
||||
} |
||||
|
||||
export default new PopupAvatar(); |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
let json = require('./schema'); |
||||
|
||||
let top = {}; |
||||
/* ['MTProto', 'API'].forEach(key => { |
||||
let schema = json[key]; |
||||
let out = {constructors: {}, methods: {}}; |
||||
|
||||
['constructors', 'methods'].forEach(key => { |
||||
schema[key].forEach(smth => { |
||||
let id = smth.id; |
||||
|
||||
if(id < 0) { |
||||
id = +id + 4294967296; |
||||
} |
||||
|
||||
out[key][id] = smth; |
||||
delete smth.id; |
||||
}); |
||||
}); |
||||
|
||||
top[key] = out; |
||||
|
||||
//console.log(out);
|
||||
//process.exit(0);
|
||||
}); */ |
||||
|
||||
['MTProto', 'API'].forEach(key => { |
||||
let schema = json[key]; |
||||
|
||||
['constructors', 'methods'].forEach(key => { |
||||
schema[key].forEach(smth => { |
||||
if(+smth.id < 0) { |
||||
smth.id = +smth.id + 4294967296; |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
//console.log(out);
|
||||
//process.exit(0);
|
||||
}); |
||||
top = json; |
||||
|
||||
/* ['API'].forEach(key => { |
||||
let schema = json[key]; |
||||
let out = {constructors: {}, methods: {}}; |
||||
|
||||
['constructors', 'methods'].forEach(key => { |
||||
schema[key].forEach(smth => { |
||||
let id = smth.id; |
||||
|
||||
if(id < 0) { |
||||
id = id + 4294967296; |
||||
} |
||||
|
||||
out[key][id] = smth; |
||||
delete smth.id; |
||||
}); |
||||
}); |
||||
|
||||
top[key] = out; |
||||
|
||||
//console.log(out);
|
||||
//process.exit(0);
|
||||
}); */ |
||||
|
||||
//console.log(out);
|
||||
|
||||
require('fs').writeFileSync('./schema_pretty.json', JSON.stringify(top/* , null, '\t' */)); |
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
import { RichTextProcessor } from "../richtextprocessor"; |
||||
|
||||
export type PollAnswer = { |
||||
_: 'pollAnswer', |
||||
text: string, |
||||
option: Uint8Array |
||||
}; |
||||
|
||||
export type PollAnswerVoters = { |
||||
_: 'pollAnswerVoters', |
||||
flags: number, |
||||
option: Uint8Array, |
||||
voters: number, |
||||
|
||||
pFlags: Partial<{ |
||||
chosen: true, |
||||
correct: true |
||||
}> |
||||
}; |
||||
|
||||
export type PollResult = { |
||||
_: 'pollAnswerVoters', |
||||
flags: number, |
||||
option: Uint8Array, |
||||
voters: number, |
||||
|
||||
pFlags?: Partial<{chosen: true}> |
||||
}; |
||||
|
||||
export type PollResults = { |
||||
_: 'pollResults', |
||||
flags: number, |
||||
results?: Array<PollResult>, |
||||
total_voters?: number, |
||||
recent_voters?: number[], |
||||
solution?: string, |
||||
solution_entities?: any[], |
||||
|
||||
pFlags: Partial<{ |
||||
min: true |
||||
}>, |
||||
}; |
||||
|
||||
export type Poll = { |
||||
_: 'poll', |
||||
flags: number, |
||||
question: string, |
||||
id: string, |
||||
answers: Array<PollAnswer>, |
||||
close_period?: number, |
||||
close_date?: number |
||||
|
||||
pFlags?: Partial<{ |
||||
closed: true, |
||||
public_voters: true, |
||||
multiple_choice: true, |
||||
quiz: true |
||||
}>, |
||||
rQuestion?: string, |
||||
rReply?: string, |
||||
}; |
||||
|
||||
class AppPollsManager { |
||||
private polls: {[id: string]: Poll} = {}; |
||||
private results: {[id: string]: PollResults} = {}; |
||||
|
||||
public savePoll(poll: Poll, results: PollResults) { |
||||
let id = poll.id; |
||||
if(this.polls[id]) { |
||||
this.results[id] = results; |
||||
return; |
||||
} |
||||
|
||||
this.polls[id] = poll; |
||||
this.results[id] = results; |
||||
|
||||
poll.rQuestion = RichTextProcessor.wrapEmojiText(poll.question); |
||||
poll.rReply = RichTextProcessor.wrapEmojiText('📊') + ' ' + (poll.rQuestion || 'poll'); |
||||
} |
||||
|
||||
public getPoll(pollID: string): {poll: Poll, results: PollResults} { |
||||
return { |
||||
poll: this.polls[pollID], |
||||
results: this.results[pollID] |
||||
}; |
||||
} |
||||
} |
||||
|
||||
export default new AppPollsManager(); |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue