frontend: make animations optional

This commit is contained in:
hippoz 2022-05-05 15:33:22 +03:00
parent 97fa63199e
commit b084de7ffc
No known key found for this signature in database
GPG key ID: 7C52899193467641
13 changed files with 53 additions and 28 deletions

View 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);
}

View file

@ -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>

View file

@ -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)}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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" />

View file

@ -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 />

View file

@ -127,6 +127,9 @@ export default {
log("close"); log("close");
}; };
this.ws.onerror = (e) => {
log("websocket: onerror", e);
};
return true; return true;
}, },

View file

@ -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;
} }

View file

@ -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();