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">
|
||||
<span class="author">{ message.author_username }</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 }) }">
|
||||
<MoreVerticalIcon />
|
||||
</button>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { HashIcon, PlusIcon, MoreVerticalIcon, SettingsIcon } from "svelte-feather-icons";
|
||||
import { quadInOut } from "svelte/easing";
|
||||
import { fly } from "svelte/transition";
|
||||
import { maybeFade } from "../animations";
|
||||
import { channels, overlayStore, selectedChannel, showChannelView, showSidebar, smallViewport, userInfoStore } from "../stores";
|
||||
import UserTopBar from "./UserTopBar.svelte";
|
||||
|
||||
|
@ -30,7 +31,7 @@
|
|||
};
|
||||
</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 />
|
||||
<div class="sidebar">
|
||||
{#each $channels as channel (channel.id)}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { overlayStore } from "../../stores";
|
||||
import request from "../../request";
|
||||
import { apiRoute } from "../../storage";
|
||||
import { maybeFly } from "../../animations";
|
||||
|
||||
let username = "";
|
||||
let password = "";
|
||||
|
@ -52,7 +53,7 @@
|
|||
</style>
|
||||
|
||||
<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">
|
||||
<span class="h4">Create an Account</span>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { overlayStore } from "../../stores";
|
||||
import request from "../../request";
|
||||
import { apiRoute } from "../../storage";
|
||||
import { maybeFade, maybeFly } from "../../animations";
|
||||
|
||||
let channelName = "";
|
||||
let createButtonEnabled = true;
|
||||
|
@ -29,8 +30,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<div class="modal-header">
|
||||
<span class="h4">Create Channel</span>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { fade, fly } from "svelte/transition";
|
||||
import { maybeFade, maybeFly } from "../../animations";
|
||||
import { quintInOut } from "svelte/easing";
|
||||
import { overlayStore } from "../../stores";
|
||||
import request from "../../request";
|
||||
|
@ -45,8 +46,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<div class="modal-header">
|
||||
<span class="h4">Edit Channel</span>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { overlayStore } from "../../stores";
|
||||
import request from "../../request";
|
||||
import { apiRoute } from "../../storage";
|
||||
import { maybeFade, maybeFly } from "../../animations";
|
||||
|
||||
export let message;
|
||||
|
||||
|
@ -45,8 +46,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<div class="modal-backdrop" transition:fade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" transition:fly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<div class="modal-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<div class="modal-header">
|
||||
<span class="h4">Edit Message</span>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import request from "../../request";
|
||||
import { apiRoute } from "../../storage";
|
||||
import { authWithToken } from "../../auth";
|
||||
import { maybeFly } from "../../animations";
|
||||
|
||||
let username = "";
|
||||
let password = "";
|
||||
|
@ -56,7 +57,7 @@
|
|||
</style>
|
||||
|
||||
<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">
|
||||
<span class="h4">Welcome back!</span>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { AtSignIcon } from "svelte-feather-icons";
|
||||
import { overlayStore, userInfoStore, smallViewport } from "../../stores";
|
||||
import { logOut } from "../../auth";
|
||||
import { maybeFade, maybeFly } from "../../animations";
|
||||
|
||||
const close = () => overlayStore.close("settings");
|
||||
|
||||
|
@ -44,8 +45,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<div class="modal-backdrop" transition:fade="{{ 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-backdrop" transition:maybeFade="{{ duration: 300, easing: quintInOut }}" on:click="{ close }">
|
||||
<div class="modal" class:large-settings="{ !$smallViewport }" transition:maybeFly="{{ duration: 300, easing: quintInOut, y: 10 }}" on:click|stopPropagation>
|
||||
<span class="h4">Account</span>
|
||||
|
||||
<div class="separator" />
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { XIcon } from "svelte-feather-icons";
|
||||
import { quintInOut } from "svelte/easing";
|
||||
import { fly } from "svelte/transition";
|
||||
import { maybeFly } from "../../animations";
|
||||
import { overlayStore } from "../../stores";
|
||||
|
||||
export let message;
|
||||
|
@ -24,7 +25,7 @@
|
|||
</style>
|
||||
|
||||
{#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>
|
||||
<button class="icon-button icon-button-auto" on:click="{ () => overlayStore.close('toast') }">
|
||||
<XIcon />
|
||||
|
|
|
@ -127,6 +127,9 @@ export default {
|
|||
|
||||
log("close");
|
||||
};
|
||||
this.ws.onerror = (e) => {
|
||||
log("websocket: onerror", e);
|
||||
};
|
||||
|
||||
return true;
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const defaults = {
|
||||
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 = {
|
||||
|
@ -39,7 +40,7 @@ export function apiRoute(fragment) {
|
|||
|
||||
export function setItemIfNull(key, value) {
|
||||
const provider = getProvider();
|
||||
if (!provider.getItem(key)) {
|
||||
if (provider.getItem(key) === undefined) {
|
||||
provider.setItem(key, value);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,19 +3,20 @@ import logging from "./logging";
|
|||
import request from "./request";
|
||||
import { apiRoute } from "./storage";
|
||||
|
||||
const storeLog = logging.logger("Store");
|
||||
const storeLog = logging.logger("Store", true);
|
||||
|
||||
class Store {
|
||||
constructor(value=null) {
|
||||
constructor(value=null, name="[unnamed]") {
|
||||
this._handlers = [];
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
subscribe(handler) {
|
||||
const newLength = this._handlers.push(handler);
|
||||
const handlerIndex = newLength - 1;
|
||||
storeLog(`(${this.name}) Calling handler (initial)`, this.value);
|
||||
handler(this.value);
|
||||
storeLog("Subscription initialized with value", this.value);
|
||||
return () => {
|
||||
this._handlers.splice(handlerIndex, 1);
|
||||
};
|
||||
|
@ -27,7 +28,7 @@ class Store {
|
|||
}
|
||||
|
||||
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 => {
|
||||
e(this.value);
|
||||
});
|
||||
|
@ -36,7 +37,7 @@ class Store {
|
|||
|
||||
class ChannelsStore extends Store {
|
||||
constructor() {
|
||||
super(gateway.channels || []);
|
||||
super(gateway.channels || [], "ChannelsStore");
|
||||
|
||||
gateway.subscribe(GatewayEventType.Ready, ({ channels }) => {
|
||||
this.value = channels;
|
||||
|
@ -88,7 +89,7 @@ class ChannelsStore extends Store {
|
|||
|
||||
class GatewayStatusStore extends Store {
|
||||
constructor() {
|
||||
super({ open: gateway.open, ready: gateway.authenticated });
|
||||
super({ open: gateway.open, ready: gateway.authenticated }, "GatewayStatusStore");
|
||||
|
||||
gateway.subscribe(GatewayEventType.Open, () => {
|
||||
this.value.open = true;
|
||||
|
@ -110,7 +111,7 @@ class GatewayStatusStore extends Store {
|
|||
|
||||
class UserInfoStore extends Store {
|
||||
constructor() {
|
||||
super(null);
|
||||
super(null, "UserInfoStore");
|
||||
|
||||
gateway.subscribe(GatewayEventType.Ready, ({ user }) => {
|
||||
this.value = user;
|
||||
|
@ -121,7 +122,7 @@ class UserInfoStore extends Store {
|
|||
|
||||
class MessageStore extends Store {
|
||||
constructor(channelId) {
|
||||
super([]);
|
||||
super([], `MessageStore[channelId=${channelId}]`);
|
||||
this.channelId = channelId;
|
||||
this.isCollectingOldMessages = true;
|
||||
this.didDoInitialLoad = false;
|
||||
|
@ -271,7 +272,7 @@ class OverlayStore extends Store {
|
|||
login: null,
|
||||
createAccount: null,
|
||||
settings: null
|
||||
});
|
||||
}, "OverlayStore");
|
||||
}
|
||||
|
||||
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 showSidebar = new Store(false);
|
||||
export const showChannelView = new Store(true);
|
||||
export const smallViewport = new Store(false);
|
||||
export const selectedChannel = new Store({ id: -1, name: "none", creator_id: -1 }, "selectedChannel");
|
||||
export const showSidebar = new Store(false, "showSidebar");
|
||||
export const showChannelView = new Store(true, "showChannelView");
|
||||
export const smallViewport = new Store(false, "smallViewport");
|
||||
export const channels = new ChannelsStore();
|
||||
export const gatewayStatus = new GatewayStatusStore();
|
||||
export const messagesStoreProvider = new MessagesStoreProvider();
|
||||
|
|
Loading…
Reference in a new issue