diff --git a/src/App.vue b/src/App.vue index 2af3623..5ecda15 100644 --- a/src/App.vue +++ b/src/App.vue @@ -266,7 +266,7 @@ body background-color: $backColor color: $foreColor -a +a, .osef text-decoration: none color: $noneColor @@ -294,10 +294,13 @@ input, select, button .colored color: orange +.danger + color: #fdd + #errors position: absolute .error - @include tile + @include main-tile #content display: flex @@ -310,7 +313,7 @@ input, select, button #manager background-color: $tileColor - border-radius: $borderRadius + @include rounded padding-left: 1em height: 1.3em display: flex @@ -335,7 +338,7 @@ input, select, button flex-direction: column .service-header .title, .settings - @include tile + @include main-tile .title font-size: large text-align: center diff --git a/src/common.sass b/src/common.sass index ee8c2a8..c4c396f 100644 --- a/src/common.sass +++ b/src/common.sass @@ -3,13 +3,31 @@ $borderRadius: .3em $backColor: #333 $tileColor: #222 -$darkColor: #111 +$darkColor: #151515 $halfColor: #999 $noneColor: #aaa $foreColor: #eee +@mixin rounded + border-radius: $borderRadius + @mixin tile + @include rounded margin: $borderRadius background-color: $tileColor - border-radius: $borderRadius - padding: $borderRadius \ No newline at end of file + +@mixin main-tile + @include tile + padding: $borderRadius + +@mixin group-tile + @include main-tile + padding: $borderRadius / 2 + & > div + @include rounded + padding: $borderRadius + margin: $borderRadius / 2 + background-color: $darkColor + border: 1px solid $backColor + &.selected + border-color: $halfColor diff --git a/src/components/loadable/Loadable.ts b/src/components/loadable/Loadable.ts new file mode 100644 index 0000000..6923379 --- /dev/null +++ b/src/components/loadable/Loadable.ts @@ -0,0 +1,14 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' + +import ErrorLoadable from '@/helpers/loadable/ErrorLoadable' + +@Component +export default class Loadable extends Vue { + + @Prop() + readonly loadable!: ErrorLoadable + + get get() { + return this.loadable.get() + } +} \ No newline at end of file diff --git a/src/components/loadable/LoadableBlock.vue b/src/components/loadable/LoadableBlock.vue index cca7266..27d4976 100644 --- a/src/components/loadable/LoadableBlock.vue +++ b/src/components/loadable/LoadableBlock.vue @@ -7,18 +7,10 @@ div.loadable-block diff --git a/src/components/loadable/LoadableInline.vue b/src/components/loadable/LoadableInline.vue index 4715209..16c4b68 100644 --- a/src/components/loadable/LoadableInline.vue +++ b/src/components/loadable/LoadableInline.vue @@ -6,18 +6,10 @@ span.loadable-inline diff --git a/src/helpers/lists/Selectable.ts b/src/helpers/lists/Selectable.ts index d760242..0600794 100644 --- a/src/helpers/lists/Selectable.ts +++ b/src/helpers/lists/Selectable.ts @@ -26,4 +26,10 @@ export class Selectable { return this } + with(call: (selected: T) => void) { + if (this.selected) { + call(this.selected) + } + } + } \ No newline at end of file diff --git a/src/helpers/loadable/AxiosLoadable.ts b/src/helpers/loadable/AxiosLoadable.ts index 7ff44c7..99fca5f 100644 --- a/src/helpers/loadable/AxiosLoadable.ts +++ b/src/helpers/loadable/AxiosLoadable.ts @@ -9,7 +9,7 @@ export default class AxiosLoadable extends ErrorLoadable { this.reset() } - promise + return promise .then(res => this.success(then(res))) .catch(err => { this.fail(err) diff --git a/src/helpers/loadable/AxiosLoadableMore.ts b/src/helpers/loadable/AxiosLoadableMore.ts index 0788fe2..1689c53 100644 --- a/src/helpers/loadable/AxiosLoadableMore.ts +++ b/src/helpers/loadable/AxiosLoadableMore.ts @@ -16,11 +16,28 @@ export default class AxiosLoadableMore extends AxiosLoadable { loadMore(promise: AxiosPromise, then: (res: AxiosResponse, data: T) => void) { this.loadingMore = true - promise.then(res => { + return promise.then(res => { if (this.data) { then(res, this.data) } this.loadingMore = false }) } + + handleScroll(target: ScrollTarget, promiser: (data: T) => AxiosPromise, then: (res: AxiosResponse, data: T) => void, + reseter?: (data: T) => void, bottom: number = 100, top: number = 20) { + if (this.data && this.loaded) { + if (!this.isLoadingMore && target.scrollHeight - target.clientHeight - target.scrollTop - bottom < 0) { + this.loadMore(promiser(this.data), then) + } else if (reseter && target.scrollTop < top) { + reseter(this.data) + } + } + } +} + +interface ScrollTarget { + scrollHeight: number + clientHeight: number + scrollTop: number } \ No newline at end of file diff --git a/src/services/discord/ChannelTag.vue b/src/services/discord/ChannelTag.vue new file mode 100644 index 0000000..d0ec8ff --- /dev/null +++ b/src/services/discord/ChannelTag.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/services/discord/Client.vue b/src/services/discord/Client.vue index 95d30f4..585a1a9 100644 --- a/src/services/discord/Client.vue +++ b/src/services/discord/Client.vue @@ -1,26 +1,42 @@ \ No newline at end of file + + + \ No newline at end of file diff --git a/src/services/discord/Discord.vue b/src/services/discord/Discord.vue index 98ca2da..6df1412 100644 --- a/src/services/discord/Discord.vue +++ b/src/services/discord/Discord.vue @@ -22,11 +22,11 @@ import axios, { AxiosResponse } from 'axios' import { Component, Mixins } from 'vue-property-decorator' -import AccountService from '../../components/service/AccountService'; -import ServiceHeaderVue from '../../components/ServiceHeader.vue' -import { Auth } from '../../types/App' +import AccountService from '@/components/service/AccountService'; +import ServiceHeaderVue from '@/components/ServiceHeader.vue' +import { Auth } from '@/types/App' import ClientVue from './Client.vue' -import { Account, Options } from './Types' +import { Options, User } from './Types' export const AUTH = { TOKEN: 'token' } export const CDN = 'https://cdn.discordapp.com' @@ -43,7 +43,7 @@ export function getRest(auth: Auth, timeout: number) { 'service-header': ServiceHeaderVue } }) -export default class Discord extends Mixins>(AccountService) { // TODO: Use oauth +export default class Discord extends Mixins>(AccountService) { // TODO: Use oauth get params(): Options { return { timeout: 5000, reconnect: false, buffer: 20, showMedia: true, ...this.options } @@ -61,7 +61,7 @@ export default class Discord extends Mixins) { + mapAccount(res: AxiosResponse) { return res.data.username } } diff --git a/src/services/discord/Guild.vue b/src/services/discord/Guild.vue deleted file mode 100644 index 51a07ed..0000000 --- a/src/services/discord/Guild.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/src/services/discord/GuildTag.vue b/src/services/discord/GuildTag.vue new file mode 100644 index 0000000..3c9a07a --- /dev/null +++ b/src/services/discord/GuildTag.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/services/discord/MessageTag.vue b/src/services/discord/MessageTag.vue new file mode 100644 index 0000000..4904d8c --- /dev/null +++ b/src/services/discord/MessageTag.vue @@ -0,0 +1,51 @@ + + + + + + diff --git a/src/services/discord/Types.ts b/src/services/discord/Types.ts index 3125370..5fb176d 100644 --- a/src/services/discord/Types.ts +++ b/src/services/discord/Types.ts @@ -1,11 +1,115 @@ -export interface Account { - username: string +export type snowflake = string +interface Entity { + id: snowflake } -export interface Guild { - id: string +export interface User extends Entity { + username: string + discriminator: string + avatar?: string + email?: string +} + +export interface PartialGuild extends Entity { name: string icon: string + owner: boolean +} + +export enum ChannelType { + GUILD_TEXT = 0, + DM = 1, + GUILD_VOICE = 2, + GROUP_DM = 3, + GUILD_CATEGORY = 4, + GUILD_NEWS = 5, + GUILD_STORE = 6 +} +export const TextChannelTypes = [ChannelType.DM, ChannelType.GROUP_DM, ChannelType.GUILD_TEXT] + +export interface Channel extends Entity { + type: ChannelType + guild_id?: snowflake + name?: string + topic?: string + nsfw?: boolean + last_message_id?: snowflake + recipients? :User[] + icon?: string + owner_id?: snowflake + position: number + parent_id?: snowflake +} +export function getChannelOrder(a: MappedChannel, b: MappedChannel) { + function getPosition(c: MappedChannel) { + return (c.parent ? c.parent.position : 0) * 100 + c.position + } + return getPosition(a) - getPosition(b) +} +export interface MappedChannel extends Channel { + parent?: Channel +} + +export interface GuildMember { + user: User + nick?: string +} + +export enum MessageType { + DEFAULT = 0, + RECIPIENT_ADD = 1, + RECIPIENT_REMOVE = 2, + CALL = 3, + CHANNEL_NAME_CHANGE = 4, + CHANNEL_ICON_CHANGE = 5, + CHANNEL_PINNED_MESSAGE = 6, + GUILD_MEMBER_JOIN = 7 +} + +export interface Message extends Entity { + channel_id: snowflake + guild_id?: snowflake + author: User + member?: GuildMember + content: string + timestamp: string + edited_timestamp?: string + tts: boolean + mention_everyone: boolean + mentions: User[] + mention_roles: Role[] + attachments: Attachment[] + reactions?: Reaction[] + pinned: boolean + webhook_id?: snowflake + type: MessageType +} + +export interface Role extends Entity { + name: string + color: number // hexa + hoist: boolean + mentionable: boolean +} + +export interface Attachment extends Entity { + filename: string + size: string // bytes + url: string + proxy_url: string + height?: number + width?: number +} + +export interface Reaction { + count: number + me: boolean + emoji: ReactionEmoji +} + +export interface ReactionEmoji { + id?: snowflake + name: string } export interface Options { diff --git a/src/services/mastodon/Account.vue b/src/services/mastodon/Account.vue index ad79e17..fcd31a7 100644 --- a/src/services/mastodon/Account.vue +++ b/src/services/mastodon/Account.vue @@ -8,7 +8,7 @@ a.account(target="_blank" :href="account.url") diff --git a/src/services/openweathermap/OpenWeatherMap.vue b/src/services/openweathermap/OpenWeatherMap.vue index 1c8595a..5d33aac 100644 --- a/src/services/openweathermap/OpenWeatherMap.vue +++ b/src/services/openweathermap/OpenWeatherMap.vue @@ -10,10 +10,10 @@ loadable-block(:loadable="weathers") template(#success) .list - weather(v-for="(city, id) in weathers.get().data" :key="id" :selected="weathers.get().selectedId == id" + weather(v-for="(city, id) in weathers.get().data" :key="id" :class="{ selected: weathers.get().isSelected(id) }" :city="city" @select="makeSelect(id)" @remove="removeCity(id)") input.weather(v-show="showAdd" placeholder="city id" @keyup.enter="addCity(parseInt($event.target.value))") - loadable-block(:loadable="forecast").forecast + loadable-block.forecast(:loadable="forecast") template(#success) chart.chart(:chartData="forecastChart") template(#error) @@ -29,12 +29,12 @@ import axios, { AxiosInstance, AxiosResponse } from 'axios' import { Component } from 'vue-property-decorator' -import ConnectedService from '../../components/service/ConnectedService' -import ServiceHeaderVue from '../../components/ServiceHeader.vue' -import Lists from '../../helpers/lists/Lists' -import { Selectable } from '../../helpers/lists/Selectable' -import AxiosLoadable from '../../helpers/loadable/AxiosLoadable' -import { Auth } from '../../types/App' +import ConnectedService from '@/components/service/ConnectedService' +import ServiceHeaderVue from '@/components/ServiceHeader.vue' +import Lists from '@/helpers/lists/Lists' +import { Selectable } from '@/helpers/lists/Selectable' +import AxiosLoadable from '@/helpers/loadable/AxiosLoadable' +import { Auth } from '@/types/App' import Chart from './Chart' import WeatherVue, { IWeather } from './Weather.vue' @@ -207,7 +207,7 @@ export default class OpenWeatherMap extends ConnectedService {