Compare commits

..

2 commits

8 changed files with 60 additions and 31 deletions

View file

@ -13,7 +13,7 @@
<div class="top-bar"> <div class="top-bar">
{#if !$showSidebar} {#if !$showSidebar}
<button class="icon-button menu-button" on:click="{ () => selectedChannel.set({ id: -1, name: "none", creator_id: -1 }) }"> <button class="icon-button menu-button" on:click="{ () => showSidebar.set(true) }">
<MenuIcon /> <MenuIcon />
</button> </button>
{/if} {/if}

View file

@ -1,4 +1,5 @@
<script> <script>
import { showChannelView } from "../stores";
import ChannelTopBar from "./ChannelTopBar.svelte"; import ChannelTopBar from "./ChannelTopBar.svelte";
import MessageInput from "./MessageInput.svelte"; import MessageInput from "./MessageInput.svelte";
import Messages from "./Messages.svelte"; import Messages from "./Messages.svelte";
@ -15,9 +16,13 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.hidden {
display: none;
}
</style> </style>
<div class="main-container"> <div class="main-container" class:hidden={ !$showChannelView }>
<ChannelTopBar channel={ channel } /> <ChannelTopBar channel={ channel } />
<Messages channelId="{ channel.id }" /> <Messages channelId="{ channel.id }" />
<MessageInput channel={ channel } /> <MessageInput channel={ channel } />

View file

@ -1,6 +1,6 @@
<script> <script>
import { CloudIcon } from "svelte-feather-icons"; import { CloudIcon } from "svelte-feather-icons";
import { gatewayStatus, showSidebar, selectedChannel } from "../stores"; import { gatewayStatus, showSidebar, selectedChannel, smallViewport, showChannelView } from "../stores";
import ChannelView from "./ChannelView.svelte"; import ChannelView from "./ChannelView.svelte";
import OverlayProvider from "./overlays/OverlayProvider.svelte"; import OverlayProvider from "./overlays/OverlayProvider.svelte";
import Sidebar from "./Sidebar.svelte"; import Sidebar from "./Sidebar.svelte";
@ -26,17 +26,10 @@
{/if} {/if}
<div class="flex-container"> <div class="flex-container">
{#if $selectedChannel.id === -1} {#if $showSidebar}
<Sidebar /> <Sidebar />
{#if $showSidebar} {/if}
<div class="fullscreen-message"> {#if !($smallViewport && $showSidebar)}
no channel selected.
</div>
{/if}
{:else}
{#if $showSidebar}
<Sidebar />
{/if}
<ChannelView channel={$selectedChannel} /> <ChannelView channel={$selectedChannel} />
{/if} {/if}
</div> </div>

View file

@ -1,7 +1,7 @@
<script> <script>
import request from "../request"; import request from "../request";
import { apiRoute } from "../storage"; import { apiRoute } from "../storage";
import { messageInputFocusStatus, messagesStoreProvider, overlayStore, userInfoStore } from "../stores"; import { messagesStoreProvider, overlayStore, userInfoStore } from "../stores";
export let channel; export let channel;
let messageInput = ""; let messageInput = "";
@ -76,7 +76,5 @@
class="message-input" class="message-input"
on:keydown={ onKeydown } on:keydown={ onKeydown }
bind:value={ messageInput } bind:value={ messageInput }
on:focus="{ () => messageInputFocusStatus.set(true) }"
on:blur="{ () => messageInputFocusStatus.set(false) }"
> >
</div> </div>

View file

@ -1,6 +1,6 @@
<script> <script>
import { afterUpdate, beforeUpdate, onMount } from "svelte"; import { afterUpdate, beforeUpdate, onMount } from "svelte";
import { messageInputFocusStatus, messagesStoreProvider } from "../stores.js"; import { messagesStoreProvider, showSidebar } from "../stores.js";
import Message from "./Message.svelte"; import Message from "./Message.svelte";
export let channelId; export let channelId;
@ -8,17 +8,9 @@
let scrollAnchor; let scrollAnchor;
let shouldAutoscroll = true; let shouldAutoscroll = true;
let lastScrollHeight = null; let lastScrollHeight = null;
let isScrolledToBottom = false; let isScrolledToBottom = true;
$: messages = messagesStoreProvider.getStore(channelId); $: messages = messagesStoreProvider.getStore(channelId);
$: {
console.log($messageInputFocusStatus, isScrolledToBottom);
if ($messageInputFocusStatus && isScrolledToBottom) {
setTimeout(() => {
scrollTarget.scrollTop = scrollTarget.scrollHeight;
}, 0);
}
}
afterUpdate(() => { afterUpdate(() => {
// hacky way to preserve scroll position when messages are pushed back // hacky way to preserve scroll position when messages are pushed back
@ -55,8 +47,19 @@
} }
}; };
const windowDidResize = () => {
// TODO: hack
// showSidebar is false on small viewports
// scrolling to bottom when the virtual keyboard pops up does not work on chromium purely based on isScrolledToBottom for some reason
const isSmallViewport = !$showSidebar;
if (isScrolledToBottom || isSmallViewport) {
scrollAnchor.scrollIntoView(false);
}
};
</script> </script>
<svelte:window on:resize={ windowDidResize } />
<style> <style>
.messages-container { .messages-container {
height: 100%; height: 100%;

View file

@ -1,14 +1,41 @@
<script> <script>
import { HashIcon, PlusIcon, MoreVerticalIcon } from "svelte-feather-icons"; import { HashIcon, PlusIcon, MoreVerticalIcon } from "svelte-feather-icons";
import { channels, overlayStore, selectedChannel } from "../stores"; import { quadInOut } from "svelte/easing";
import { fly } from "svelte/transition";
import { channels, overlayStore, selectedChannel, showChannelView, showSidebar, smallViewport } from "../stores";
import UserTopBar from "./UserTopBar.svelte"; import UserTopBar from "./UserTopBar.svelte";
let pendingSelectChannel = null;
const selectChannel = (channel) => $selectedChannel = channel;
// janky code to hide the channel view during animation
// this will make a smooth sidebar animation on smaller viewports (such as a phone, where you switch between sidebar being active)
const scheduleSelectChannel = (channel) => {
if ($smallViewport) {
$showChannelView = false;
pendingSelectChannel = channel;
$showSidebar = false;
} else {
selectChannel(channel);
}
};
const outroEnd = () => {
if (pendingSelectChannel) {
selectChannel(pendingSelectChannel);
pendingSelectChannel = null;
$showChannelView = true;
}
};
</script> </script>
<div class="sidebar-container"> <div class="sidebar-container" transition:fly="{{ 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)}
<button on:click="{ () => $selectedChannel = channel }" class="sidebar-button" class:selected={ channel.id === $selectedChannel.id }> <button on:click="{ scheduleSelectChannel(channel) }" class="sidebar-button" class:selected={ channel.id === $selectedChannel.id }>
<div> <div>
<HashIcon /> <HashIcon />
</div> </div>

View file

@ -1,10 +1,11 @@
import { showSidebar } from "./stores"; import { showSidebar, smallViewport } from "./stores";
export function initResponsiveHandlers() { export function initResponsiveHandlers() {
const mediaQuery = window.matchMedia('(min-width: 768px)'); const mediaQuery = window.matchMedia('(min-width: 768px)');
const update = ({ matches }) => { const update = ({ matches }) => {
showSidebar.set(matches); showSidebar.set(matches);
smallViewport.set(!matches);
}; };
mediaQuery.addEventListener("change", update); mediaQuery.addEventListener("change", update);

View file

@ -36,6 +36,7 @@ class ChannelsStore extends Store {
gateway.subscribe(GatewayEventType.Ready, ({ channels }) => { gateway.subscribe(GatewayEventType.Ready, ({ channels }) => {
this.value = channels; this.value = channels;
selectedChannel.set(channels.length ? channels[0] : null);
this.updated(); this.updated();
}); });
gateway.subscribe(GatewayEventType.ChannelCreate, (channel) => { gateway.subscribe(GatewayEventType.ChannelCreate, (channel) => {
@ -265,4 +266,5 @@ export const userInfoStore = new UserInfoStore();
export const overlayStore = new OverlayStore(); export const overlayStore = new OverlayStore();
export const selectedChannel = writable({ id: -1, name: "none", creator_id: -1 }); export const selectedChannel = writable({ id: -1, name: "none", creator_id: -1 });
export const showSidebar = writable(false); export const showSidebar = writable(false);
export const messageInputFocusStatus = writable(false); export const showChannelView = writable(true);
export const smallViewport = writable(false);