frontend: make animations optional
This commit is contained in:
parent
97fa63199e
commit
b084de7ffc
13 changed files with 53 additions and 28 deletions
12
frontend/src/animations.js
Normal file
12
frontend/src/animations.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { fade, fly } from "svelte/transition";
|
||||||
|
import { getItem } from "./storage";
|
||||||
|
|
||||||
|
export function maybeFly(...e) {
|
||||||
|
if (getItem("doAnimations"))
|
||||||
|
return fly(...e);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function maybeFade(...e) {
|
||||||
|
if (getItem("doAnimations"))
|
||||||
|
return fade(...e);
|
||||||
|
}
|
|
@ -49,7 +49,7 @@
|
||||||
<div class="message">
|
<div class="message">
|
||||||
<span class="author">{ message.author_username }</span>
|
<span class="author">{ message.author_username }</span>
|
||||||
<span class="message-content" class:pending={ message._isPending }>{ message.content }</span>
|
<span class="message-content" class:pending={ message._isPending }>{ message.content }</span>
|
||||||
{#if $userInfoStore && message.author_id === $userInfoStore.id}
|
{#if userInfoStore.value && message.author_id === userInfoStore.value.id}
|
||||||
<button class="icon-button icon-button-auto edit-message" on:click="{ () => overlayStore.open('editMessage', { message }) }">
|
<button class="icon-button icon-button-auto edit-message" on:click="{ () => overlayStore.open('editMessage', { message }) }">
|
||||||
<MoreVerticalIcon />
|
<MoreVerticalIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { HashIcon, PlusIcon, MoreVerticalIcon, SettingsIcon } from "svelte-feather-icons";
|
import { HashIcon, PlusIcon, MoreVerticalIcon, SettingsIcon } from "svelte-feather-icons";
|
||||||
import { quadInOut } from "svelte/easing";
|
import { quadInOut } from "svelte/easing";
|
||||||
import { fly } from "svelte/transition";
|
import { fly } from "svelte/transition";
|
||||||
|
import { maybeFade } from "../animations";
|
||||||
import { channels, overlayStore, selectedChannel, showChannelView, showSidebar, smallViewport, userInfoStore } from "../stores";
|
import { channels, overlayStore, selectedChannel, showChannelView, showSidebar, smallViewport, userInfoStore } from "../stores";
|
||||||
import UserTopBar from "./UserTopBar.svelte";
|
import UserTopBar from "./UserTopBar.svelte";
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="sidebar-container" transition:fly="{{ duration: 200, easing: quadInOut, x: -10 }}" on:outroend="{ outroEnd }">
|
<div class="sidebar-container" transition:maybeFade="{{ duration: 200, easing: quadInOut, x: -10 }}" on:outroend="{ outroEnd }">
|
||||||
<UserTopBar />
|
<UserTopBar />
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
{#each $channels as channel (channel.id)}
|
{#each $channels as channel (channel.id)}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import request from "../../request";
|
import request from "../../request";
|
||||||
import { apiRoute } from "../../storage";
|
import { apiRoute } from "../../storage";
|
||||||
|
import { maybeFly } from "../../animations";
|
||||||
|
|
||||||
let username = "";
|
let username = "";
|
||||||
let password = "";
|
let password = "";
|
||||||
|
@ -52,7 +53,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop modal-backdrop-opaque">
|
<div class="modal-backdrop modal-backdrop-opaque">
|
||||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation on:outroend="{ outroEnd }">
|
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation on:outroend="{ outroEnd }">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="h4">Create an Account</span>
|
<span class="h4">Create an Account</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import request from "../../request";
|
import request from "../../request";
|
||||||
import { apiRoute } from "../../storage";
|
import { apiRoute } from "../../storage";
|
||||||
|
import { maybeFade, maybeFly } from "../../animations";
|
||||||
|
|
||||||
let channelName = "";
|
let channelName = "";
|
||||||
let createButtonEnabled = true;
|
let createButtonEnabled = true;
|
||||||
|
@ -29,8 +30,8 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="h4">Create Channel</span>
|
<span class="h4">Create Channel</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { fade, fly } from "svelte/transition";
|
import { fade, fly } from "svelte/transition";
|
||||||
|
import { maybeFade, maybeFly } from "../../animations";
|
||||||
import { quintInOut } from "svelte/easing";
|
import { quintInOut } from "svelte/easing";
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import request from "../../request";
|
import request from "../../request";
|
||||||
|
@ -45,8 +46,8 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="h4">Edit Channel</span>
|
<span class="h4">Edit Channel</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import request from "../../request";
|
import request from "../../request";
|
||||||
import { apiRoute } from "../../storage";
|
import { apiRoute } from "../../storage";
|
||||||
|
import { maybeFade, maybeFly } from "../../animations";
|
||||||
|
|
||||||
export let message;
|
export let message;
|
||||||
|
|
||||||
|
@ -45,8 +46,8 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="h4">Edit Message</span>
|
<span class="h4">Edit Message</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import request from "../../request";
|
import request from "../../request";
|
||||||
import { apiRoute } from "../../storage";
|
import { apiRoute } from "../../storage";
|
||||||
import { authWithToken } from "../../auth";
|
import { authWithToken } from "../../auth";
|
||||||
|
import { maybeFly } from "../../animations";
|
||||||
|
|
||||||
let username = "";
|
let username = "";
|
||||||
let password = "";
|
let password = "";
|
||||||
|
@ -56,7 +57,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop modal-backdrop-opaque">
|
<div class="modal-backdrop modal-backdrop-opaque">
|
||||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation on:outroend="{ outroEnd }">
|
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation on:outroend="{ outroEnd }">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="h4">Welcome back!</span>
|
<span class="h4">Welcome back!</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { AtSignIcon } from "svelte-feather-icons";
|
import { AtSignIcon } from "svelte-feather-icons";
|
||||||
import { overlayStore, userInfoStore, smallViewport } from "../../stores";
|
import { overlayStore, userInfoStore, smallViewport } from "../../stores";
|
||||||
import { logOut } from "../../auth";
|
import { logOut } from "../../auth";
|
||||||
|
import { maybeFade, maybeFly } from "../../animations";
|
||||||
|
|
||||||
const close = () => overlayStore.close("settings");
|
const close = () => overlayStore.close("settings");
|
||||||
|
|
||||||
|
@ -44,8 +45,8 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||||
<div class="modal" class:large-settings="{ !$smallViewport }" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
<div class="modal" class:large-settings="{ !$smallViewport }" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||||
<span class="h4">Account</span>
|
<span class="h4">Account</span>
|
||||||
|
|
||||||
<div class="separator" />
|
<div class="separator" />
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { XIcon } from "svelte-feather-icons";
|
import { XIcon } from "svelte-feather-icons";
|
||||||
import { quintInOut } from "svelte/easing";
|
import { quintInOut } from "svelte/easing";
|
||||||
import { fly } from "svelte/transition";
|
import { fly } from "svelte/transition";
|
||||||
|
import { maybeFly } from "../../animations";
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
|
|
||||||
export let message;
|
export let message;
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{#key message}
|
{#key message}
|
||||||
<div class="toast" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}">
|
<div class="toast" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}">
|
||||||
<span>{ message }</span>
|
<span>{ message }</span>
|
||||||
<button class="icon-button icon-button-auto" on:click="{ () => overlayStore.close('toast') }">
|
<button class="icon-button icon-button-auto" on:click="{ () => overlayStore.close('toast') }">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
|
|
|
@ -127,6 +127,9 @@ export default {
|
||||||
|
|
||||||
log("close");
|
log("close");
|
||||||
};
|
};
|
||||||
|
this.ws.onerror = (e) => {
|
||||||
|
log("websocket: onerror", e);
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const defaults = {
|
const defaults = {
|
||||||
apiBase: `${window.location.origin}/api/v1`,
|
apiBase: `${window.location.origin}/api/v1`,
|
||||||
gatewayBase: `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/gateway`
|
gatewayBase: `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/gateway`,
|
||||||
|
doAnimations: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const dummyProvider = {
|
const dummyProvider = {
|
||||||
|
@ -39,7 +40,7 @@ export function apiRoute(fragment) {
|
||||||
|
|
||||||
export function setItemIfNull(key, value) {
|
export function setItemIfNull(key, value) {
|
||||||
const provider = getProvider();
|
const provider = getProvider();
|
||||||
if (!provider.getItem(key)) {
|
if (provider.getItem(key) === undefined) {
|
||||||
provider.setItem(key, value);
|
provider.setItem(key, value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,20 @@ import logging from "./logging";
|
||||||
import request from "./request";
|
import request from "./request";
|
||||||
import { apiRoute } from "./storage";
|
import { apiRoute } from "./storage";
|
||||||
|
|
||||||
const storeLog = logging.logger("Store");
|
const storeLog = logging.logger("Store", true);
|
||||||
|
|
||||||
class Store {
|
class Store {
|
||||||
constructor(value=null) {
|
constructor(value=null, name="[unnamed]") {
|
||||||
this._handlers = [];
|
this._handlers = [];
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(handler) {
|
subscribe(handler) {
|
||||||
const newLength = this._handlers.push(handler);
|
const newLength = this._handlers.push(handler);
|
||||||
const handlerIndex = newLength - 1;
|
const handlerIndex = newLength - 1;
|
||||||
|
storeLog(`(${this.name}) Calling handler (initial)`, this.value);
|
||||||
handler(this.value);
|
handler(this.value);
|
||||||
storeLog("Subscription initialized with value", this.value);
|
|
||||||
return () => {
|
return () => {
|
||||||
this._handlers.splice(handlerIndex, 1);
|
this._handlers.splice(handlerIndex, 1);
|
||||||
};
|
};
|
||||||
|
@ -27,7 +28,7 @@ class Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
updated() {
|
updated() {
|
||||||
storeLog(`updated(): calling ${this._handlers.length} handlers - value changed`, this.value);
|
storeLog(`(${this.name}) Calling all (${this._handlers.length}) handlers (updated)`, this.value);
|
||||||
this._handlers.forEach(e => {
|
this._handlers.forEach(e => {
|
||||||
e(this.value);
|
e(this.value);
|
||||||
});
|
});
|
||||||
|
@ -36,7 +37,7 @@ class Store {
|
||||||
|
|
||||||
class ChannelsStore extends Store {
|
class ChannelsStore extends Store {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(gateway.channels || []);
|
super(gateway.channels || [], "ChannelsStore");
|
||||||
|
|
||||||
gateway.subscribe(GatewayEventType.Ready, ({ channels }) => {
|
gateway.subscribe(GatewayEventType.Ready, ({ channels }) => {
|
||||||
this.value = channels;
|
this.value = channels;
|
||||||
|
@ -88,7 +89,7 @@ class ChannelsStore extends Store {
|
||||||
|
|
||||||
class GatewayStatusStore extends Store {
|
class GatewayStatusStore extends Store {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ open: gateway.open, ready: gateway.authenticated });
|
super({ open: gateway.open, ready: gateway.authenticated }, "GatewayStatusStore");
|
||||||
|
|
||||||
gateway.subscribe(GatewayEventType.Open, () => {
|
gateway.subscribe(GatewayEventType.Open, () => {
|
||||||
this.value.open = true;
|
this.value.open = true;
|
||||||
|
@ -110,7 +111,7 @@ class GatewayStatusStore extends Store {
|
||||||
|
|
||||||
class UserInfoStore extends Store {
|
class UserInfoStore extends Store {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(null);
|
super(null, "UserInfoStore");
|
||||||
|
|
||||||
gateway.subscribe(GatewayEventType.Ready, ({ user }) => {
|
gateway.subscribe(GatewayEventType.Ready, ({ user }) => {
|
||||||
this.value = user;
|
this.value = user;
|
||||||
|
@ -121,7 +122,7 @@ class UserInfoStore extends Store {
|
||||||
|
|
||||||
class MessageStore extends Store {
|
class MessageStore extends Store {
|
||||||
constructor(channelId) {
|
constructor(channelId) {
|
||||||
super([]);
|
super([], `MessageStore[channelId=${channelId}]`);
|
||||||
this.channelId = channelId;
|
this.channelId = channelId;
|
||||||
this.isCollectingOldMessages = true;
|
this.isCollectingOldMessages = true;
|
||||||
this.didDoInitialLoad = false;
|
this.didDoInitialLoad = false;
|
||||||
|
@ -271,7 +272,7 @@ class OverlayStore extends Store {
|
||||||
login: null,
|
login: null,
|
||||||
createAccount: null,
|
createAccount: null,
|
||||||
settings: null
|
settings: null
|
||||||
});
|
}, "OverlayStore");
|
||||||
}
|
}
|
||||||
|
|
||||||
open(name, props={}) {
|
open(name, props={}) {
|
||||||
|
@ -285,10 +286,10 @@ class OverlayStore extends Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectedChannel = new Store({ id: -1, name: "none", creator_id: -1 });
|
export const selectedChannel = new Store({ id: -1, name: "none", creator_id: -1 }, "selectedChannel");
|
||||||
export const showSidebar = new Store(false);
|
export const showSidebar = new Store(false, "showSidebar");
|
||||||
export const showChannelView = new Store(true);
|
export const showChannelView = new Store(true, "showChannelView");
|
||||||
export const smallViewport = new Store(false);
|
export const smallViewport = new Store(false, "smallViewport");
|
||||||
export const channels = new ChannelsStore();
|
export const channels = new ChannelsStore();
|
||||||
export const gatewayStatus = new GatewayStatusStore();
|
export const gatewayStatus = new GatewayStatusStore();
|
||||||
export const messagesStoreProvider = new MessagesStoreProvider();
|
export const messagesStoreProvider = new MessagesStoreProvider();
|
||||||
|
|
Loading…
Reference in a new issue