Adapt to different device widths (#401)
authorLaurenz <ritters_werth@outlook.com>
Sun, 24 Apr 2022 10:23:10 +0000 (11:23 +0100)
committerGitHub <noreply@github.com>
Sun, 24 Apr 2022 10:23:10 +0000 (15:53 +0530)
* Now adapting to small screen sizes, needs improvements

* Fix that site only gets into mobile mode when resized

* - Added navigation event triggered if user requests to return to navigation on compact screens
- People drawer wont be shown on compact screens
  - Still accessible using settings
  - would be duplicated UI
- mobileSize is now compactSize

* Put threshold for collapsing the base UI in a shared file

* Switch to a more simple solution using CSS media queries over JS
- Move back button to the left a bit so it doesnt get in touch with room icon

* switch from component-individual-thresholds to device-type thresholds
- <750px: Mobile
- <900px: Tablet
- >900px: Desktop

* Make Settings drawer component collapse on mobile

* Fix EmojiBoard not showing up and messing up UI when screen is smaller than 360px

* Improve code quality; allow passing classNames to IconButton
- remove unnessesary div wrappers
- use dir.side where appropriate
- rename threshold and its mixins to more descriptive names
- Rename "OPEN_NAVIGATION" to "NAVIGATION_OPENED"

* - follow BEM methology
- remove ROOM_SELECTED listener
- rename NAVIGATION_OPENED to OPEN_NAVIGATION where appropriate
- this does NOT changes that ref should be used for changing visability

* Use ref to change visability to avoid re-rendering

* Use ref to change visability to avoid re-rendering

* Fix that room component is not hidden by default.
This resulted in a broken view when application is viewed in mobile size without having selected a room since loading.

* fix: leaving a room should bring one back to navigation

Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
19 files changed:
src/app/atoms/button/IconButton.jsx
src/app/atoms/header/Header.scss
src/app/molecules/popup-window/PopupWindow.scss
src/app/molecules/room-options/RoomOptions.jsx
src/app/organisms/emoji-board/EmojiBoard.scss
src/app/organisms/invite-list/InviteList.jsx
src/app/organisms/room/Room.jsx
src/app/organisms/room/Room.scss
src/app/organisms/room/RoomSettings.jsx
src/app/organisms/room/RoomViewCmdBar.jsx
src/app/organisms/room/RoomViewHeader.jsx
src/app/organisms/room/RoomViewHeader.scss
src/app/organisms/settings/Settings.scss
src/app/partials/screen.scss [new file with mode: 0644]
src/app/templates/client/Client.jsx
src/app/templates/client/Client.scss
src/client/action/navigation.js
src/client/state/cons.js
src/client/state/navigation.js

index a06c136c9ded9005f68e69d5ac64a24d02dba6ae..f6a8730e17ea5c75e9d8dc3c74aed8b9337b27ba 100644 (file)
@@ -11,11 +11,12 @@ const IconButton = React.forwardRef(({
   variant, size, type,
   tooltip, tooltipPlacement, src,
   onClick, tabIndex, disabled, isImage,
+  className,
 }, ref) => {
   const btn = (
     <button
       ref={ref}
-      className={`ic-btn ic-btn-${variant}`}
+      className={`ic-btn ic-btn-${variant} ${className}`}
       onMouseUp={(e) => blurOnBubbling(e, `.ic-btn-${variant}`)}
       onClick={onClick}
       // eslint-disable-next-line react/button-has-type
@@ -47,6 +48,7 @@ IconButton.defaultProps = {
   tabIndex: 0,
   disabled: false,
   isImage: false,
+  className: '',
 };
 
 IconButton.propTypes = {
@@ -60,6 +62,7 @@ IconButton.propTypes = {
   tabIndex: PropTypes.number,
   disabled: PropTypes.bool,
   isImage: PropTypes.bool,
+  className: PropTypes.string,
 };
 
 export default IconButton;
index 9e45f8246697eac98c7e0b5ead4a238ee0e58b2f..081b61e2439f3f840b305b5c074428d6926734f2 100644 (file)
@@ -1,5 +1,6 @@
 @use '../../partials/text';
 @use '../../partials/dir';
+@use '../../partials/screen';
 
 .header {
   @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight));
@@ -40,4 +41,8 @@
       display: -webkit-box;
     }
   }
+
+  @include screen.smallerThan(mobileBreakpoint) {
+    @include dir.side(padding, calc(var(--sp-normal) - 10px), var(--sp-extra-tight));
+  }
 }
\ No newline at end of file
index 2d72963f0026d2790e7f5bde1df9cb92e2a6ca0d..f03d1dcd908ed3f89bfa4c693d643a9f5f2471fa 100644 (file)
@@ -72,4 +72,4 @@
       @include dir.side(margin, 0, var(--sp-tight));
     }
   }
-}
\ No newline at end of file
+}
index 501788955f479456fe27d69a7701a814cc08b96a..ef824785f48f88d4b92605167639ed9ee06b93db 100644 (file)
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import { twemojify } from '../../../util/twemojify';
 
 import initMatrix from '../../../client/initMatrix';
-import { openInviteUser } from '../../../client/action/navigation';
+import { openInviteUser, openNavigation } from '../../../client/action/navigation';
 import * as roomActions from '../../../client/action/room';
 import { markAsRead } from '../../../client/action/notifications';
 
@@ -33,6 +33,7 @@ function RoomOptions({ roomId, afterOptionSelect }) {
     if (confirm('Are you sure that you want to leave this room?')) {
       roomActions.leave(roomId);
       afterOptionSelect();
+      openNavigation();
     }
   };
 
index 0db59d9633e48f1a91c9e42509c08c61fd1ffc3f..73f3ab3be9210fba22d68be484aca2316f300800 100644 (file)
@@ -6,6 +6,8 @@
   --emoji-board-height: 390px;
   --emoji-board-width: 286px;
   display: flex;
+  max-width: 90vw;
+  max-height: 90vh;
   
   &__content {
     @extend .cp-fx__item-one;
index 65920704a4a0abbcd18b236858995e8c006812e8..03a94d69070b8e2dd78a55f21db1559b432e9ae7 100644 (file)
@@ -5,7 +5,7 @@ import './InviteList.scss';
 import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import * as roomActions from '../../../client/action/room';
-import { selectRoom, selectTab } from '../../../client/action/navigation';
+import { selectRoom, selectTab, openNavigation } from '../../../client/action/navigation';
 
 import Text from '../../atoms/text/Text';
 import Button from '../../atoms/button/Button';
@@ -28,6 +28,7 @@ function InviteList({ isOpen, onRequestClose }) {
     procInvite.add(roomId);
     changeProcInvite(new Set(Array.from(procInvite)));
     roomActions.leave(roomId, isDM);
+    openNavigation();
   }
   function updateInviteList(roomId) {
     if (procInvite.has(roomId)) procInvite.delete(roomId);
index 7c199beae37e5e9bfe38566dff4a2ba3d353f0c5..4a1a969d6e7f737ce6718faa69424e6b6785b9a5 100644 (file)
@@ -3,9 +3,9 @@ import './Room.scss';
 
 import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
 import settings from '../../../client/state/settings';
 import RoomTimeline from '../../../client/state/RoomTimeline';
+import navigation from '../../../client/state/navigation';
 
 import Welcome from '../welcome/Welcome';
 import RoomView from './RoomView';
@@ -61,7 +61,7 @@ function Room() {
         <RoomSettings roomId={roomTimeline.roomId} />
         <RoomView roomTimeline={roomTimeline} eventId={eventId} />
       </div>
-      { isDrawer && <PeopleDrawer roomId={roomTimeline.roomId} />}
+      {isDrawer && <PeopleDrawer roomId={roomTimeline.roomId} />}
     </div>
   );
 }
index 975acbd71fc7bbc16999794d7f4c30dc6c4d1432..11a00074d596c8cfead35a4db75ba1fffe2bf4ce 100644 (file)
@@ -1,4 +1,5 @@
 @use '../../partials/flex';
+@use '../../partials/screen';
 
 .room {
   @extend .cp-fx__row;
@@ -9,4 +10,10 @@
     position: relative;
     overflow: hidden;
   }
-}
\ No newline at end of file
+}
+
+.room .people-drawer {
+  @include screen.smallerThan(tabletBreakpoint) {
+    display: none;
+  }
+}
index 8d14c18dbdd38ea9736ac4b36a0af4fb03a2417b..b75ca7fcb49dd5533440461ae245520abff4ccc9 100644 (file)
@@ -7,7 +7,7 @@ import { blurOnBubbling } from '../../atoms/button/script';
 import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
-import { openInviteUser, toggleRoomSettings } from '../../../client/action/navigation';
+import { openInviteUser, toggleRoomSettings, openNavigation } from '../../../client/action/navigation';
 import * as roomActions from '../../../client/action/room';
 
 import Text from '../../atoms/text/Text';
@@ -88,6 +88,7 @@ function GeneralSettings({ roomId }) {
           onClick={() => {
             if (confirm('Are you sure that you want to leave this room?')) {
               roomActions.leave(roomId);
+              openNavigation();
             }
           }}
           iconSrc={LeaveArrowIC}
index 4ccb20a41a20c3dc2debae9660b5c470ae3f1d9e..a20afdb706f9e8422af2d069f465eeadb210bac7 100644 (file)
@@ -14,6 +14,7 @@ import {
   openCreateRoom,
   openPublicRooms,
   openInviteUser,
+  openNavigation,
 } from '../../../client/action/navigation';
 import { getEmojiForCompletion } from '../emoji-board/custom-emoji';
 import AsyncSearch from '../../../util/AsyncSearch';
@@ -44,7 +45,10 @@ const commands = [{
 }, {
   name: 'leave',
   description: 'Leave current room',
-  exe: (roomId) => roomActions.leave(roomId),
+  exe: (roomId) => {
+    roomActions.leave(roomId);
+    openNavigation();
+  },
 }, {
   name: 'invite',
   isOptions: true,
index 669464aeb70bc04c5699e6fb1f2373c973895e96..db1df9d8f9898d8a8278d50daa085af79f11d7f2 100644 (file)
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import PropTypes from 'prop-types';
 import './RoomViewHeader.scss';
 
@@ -25,6 +25,7 @@ import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.s
 import SearchIC from '../../../../public/res/ic/outlined/search.svg';
 import UserIC from '../../../../public/res/ic/outlined/user.svg';
 import VerticalMenuIC from '../../../../public/res/ic/outlined/vertical-menu.svg';
+import BackArrowIC from '../../../../public/res/ic/outlined/chevron-left.svg';
 
 import { useForceUpdate } from '../../hooks/useForceUpdate';
 
@@ -73,6 +74,12 @@ function RoomViewHeader({ roomId }) {
 
   return (
     <Header>
+      <IconButton
+        src={BackArrowIC}
+        className="room-header__back-btn"
+        tooltip="Return to navigation"
+        onClick={() => navigation.emit(cons.events.navigation.NAVIGATION_OPENED)}
+      />
       <button
         ref={roomHeaderBtnRef}
         className="room-header__btn"
@@ -87,7 +94,7 @@ function RoomViewHeader({ roomId }) {
         <RawIcon src={ChevronBottomIC} />
       </button>
       <IconButton onClick={() => toggleRoomSettings(tabText.SEARCH)} tooltip="Search" src={SearchIC} />
-      <IconButton onClick={togglePeopleDrawer} tooltip="People" src={UserIC} />
+      <IconButton className="room-header__drawer-btn" onClick={togglePeopleDrawer} tooltip="People" src={UserIC} />
       <IconButton
         onClick={openRoomOptions}
         tooltip="Options"
index ddfab6c69efedeb0e0ce3b538de2edd932e3feec..79bb8dd2b309b367b414f5cccad1afb58acb5cef 100644 (file)
@@ -1,5 +1,6 @@
 @use '../../partials/flex';
 @use '../../partials/dir';
+@use '../../partials/screen';
 
 .room-header__btn {
   min-width: 0;
     box-shadow: var(--bs-surface-outline);
     outline: none;
   }
-}
\ No newline at end of file
+}
+
+.header .room-header__drawer-btn {
+  @include screen.smallerThan(tabletBreakpoint) {
+    display: none;
+  }
+}
+
+.header .room-header__back-btn {
+  @include dir.side(margin, 0, 5px);
+
+  @include screen.biggerThan(mobileBreakpoint) {
+    display: none;
+  }
+}
index 360efc5b55d1e68cf156bcdd90087c4721bf2857..ad48032139af5c251208fed7f5cc6f949c821ace 100644 (file)
@@ -1,5 +1,6 @@
 @use '../../partials/flex';
 @use '../../partials/dir';
+@use '../../partials/screen';
 
 .settings-window {
   & .pw {
       margin: var(--sp-extra-tight) 0;
     }
   }
-}
\ No newline at end of file
+}
+
+.settings-window {
+  @include screen.smallerThan(mobileBreakpoint) {
+    .pw__drawer p, .pw__drawer .header h4 {
+      display: none;
+      width: 0;
+    }
+
+    .pw__drawer {
+      width: fit-content;
+    }
+  }
+}
diff --git a/src/app/partials/screen.scss b/src/app/partials/screen.scss
new file mode 100644 (file)
index 0000000..6d524f2
--- /dev/null
@@ -0,0 +1,28 @@
+
+$breakpoint-tablet: 900px;
+$breakpoint-mobile: 750px;
+
+@mixin smallerThan($deviceBreakpoint) {
+  @if $deviceBreakpoint==mobileBreakpoint {
+    @media screen and (max-width: $breakpoint-mobile) {
+      @content;
+    }
+  }
+  @else if $deviceBreakpoint==tabletBreakpoint {
+    @media screen and (max-width: $breakpoint-tablet) {
+      @content;
+    }
+  }
+}
+
+@mixin biggerThan($deviceBreakpoint) {
+  @if $deviceBreakpoint==mobileBreakpoint {
+    @media screen and (min-width: $breakpoint-mobile) {
+      @content;
+    }
+  } @else if $deviceBreakpoint==tabletBreakpoint {
+    @media screen and (min-width: $breakpoint-tablet) {
+      @content;
+    }
+  }
+}
index 1342db55fce04e45b4f113361f56f0db89e56117..915a01f6639de4013072992ff04b86cc7423e331 100644 (file)
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
 import './Client.scss';
 
 import { initHotkeys } from '../../../client/event/hotkeys';
@@ -19,11 +19,45 @@ import navigation from '../../../client/state/navigation';
 import cons from '../../../client/state/cons';
 import DragDrop from '../../organisms/drag-drop/DragDrop';
 
+const classNameHidden = 'client__item-hidden';
+
 function Client() {
   const [isLoading, changeLoading] = useState(true);
   const [loadingMsg, setLoadingMsg] = useState('Heating up');
   const [dragCounter, setDragCounter] = useState(0);
 
+  /**
+   * @type {React.MutableRefObject<HTMLDivElement>}
+   */
+  const navWrapperRef = useRef(null);
+  /**
+   * @type {React.MutableRefObject<HTMLDivElement>}
+   */
+  const roomWrapperRef = useRef(null);
+
+  // #region Liston on events for compact screen sizes
+  function onRoomSelected() {
+    // setActiveView(viewPossibilities.room);
+    navWrapperRef.current.classList.add(classNameHidden);
+    roomWrapperRef.current.classList.remove(classNameHidden);
+  }
+  function onNavigationSelected() {
+    // setActiveView(viewPossibilities.nav);
+    navWrapperRef.current.classList.remove(classNameHidden);
+    roomWrapperRef.current.classList.add(classNameHidden);
+  }
+
+  useEffect(() => {
+    navigation.on(cons.events.navigation.ROOM_SELECTED, onRoomSelected);
+    navigation.on(cons.events.navigation.NAVIGATION_OPENED, onNavigationSelected);
+
+    return (() => {
+      navigation.removeListener(cons.events.navigation.ROOM_SELECTED, onRoomSelected);
+      navigation.removeListener(cons.events.navigation.NAVIGATION_OPENED, onNavigationSelected);
+    });
+  }, []);
+  // #endregion
+
   useEffect(() => {
     let counter = 0;
     const iId = setInterval(() => {
@@ -64,6 +98,7 @@ function Client() {
     );
   }
 
+  // #region drag and drop
   function dragContainsFiles(e) {
     if (!e.dataTransfer.types) return false;
 
@@ -119,6 +154,7 @@ function Client() {
     initMatrix.roomsInput.setAttachment(roomId, file);
     initMatrix.roomsInput.emit(cons.events.roomsInput.ATTACHMENT_SET, file);
   }
+  // #endregion
 
   return (
     <div
@@ -128,10 +164,10 @@ function Client() {
       onDragLeave={handleDragLeave}
       onDrop={handleDrop}
     >
-      <div className="navigation__wrapper">
+      <div className="navigation__wrapper" ref={navWrapperRef}>
         <Navigation />
       </div>
-      <div className="room__wrapper">
+      <div className={`room__wrapper ${classNameHidden}`} ref={roomWrapperRef}>
         <Room />
       </div>
       <Windows />
index 45a50527529e5040b9ff90c6f2c94503a0eefcd2..67d5dfd5288a643915bb35f8d85ec7d1ed7a5d59 100644 (file)
@@ -1,3 +1,5 @@
+@use '../../partials/screen';
+
 .client-container {
   display: flex;
   height: 100%;
@@ -5,12 +7,22 @@
 
 .navigation__wrapper {
   width: var(--navigation-width);
+  
+  @include screen.smallerThan(mobileBreakpoint) {
+    width: 100%;
+  }
 }
+
 .room__wrapper {
   flex: 1;
   min-width: 0;
 }
 
+@include screen.smallerThan(mobileBreakpoint) {
+  .client__item-hidden {
+    display: none;
+  }
+}
 
 .loading-display {
   position: absolute;
@@ -41,4 +53,4 @@
   .text {
     color: var(--tc-link);
   }
-}
\ No newline at end of file
+}
index e16d25d29daae1663909e3e17f4ae0b7f8a116ee..8876732ed6699bcbd06623aa15adab2958d5b4be 100644 (file)
@@ -23,6 +23,15 @@ export function selectRoom(roomId, eventId) {
   });
 }
 
+/**
+ * Open navigation on compact screen sizes
+ */
+export function openNavigation() {
+  appDispatcher.dispatch({
+    type: cons.actions.navigation.OPEN_NAVIGATION,
+  });
+}
+
 export function openSpaceSettings(roomId, tabText) {
   appDispatcher.dispatch({
     type: cons.actions.navigation.OPEN_SPACE_SETTINGS,
index 4e258ac3f80fe4e30cfae02d39ec85aa03e13f90..34a3a928bb04ce0da87a91756a5ea8f475075d97 100644 (file)
@@ -47,6 +47,7 @@ const cons = {
       CLICK_REPLY_TO: 'CLICK_REPLY_TO',
       OPEN_SEARCH: 'OPEN_SEARCH',
       OPEN_REUSABLE_CONTEXT_MENU: 'OPEN_REUSABLE_CONTEXT_MENU',
+      OPEN_NAVIGATION: 'OPEN_NAVIGATION',
       OPEN_REUSABLE_DIALOG: 'OPEN_REUSABLE_DIALOG',
     },
     room: {
@@ -93,6 +94,7 @@ const cons = {
       REPLY_TO_CLICKED: 'REPLY_TO_CLICKED',
       SEARCH_OPENED: 'SEARCH_OPENED',
       REUSABLE_CONTEXT_MENU_OPENED: 'REUSABLE_CONTEXT_MENU_OPENED',
+      NAVIGATION_OPENED: 'NAVIGATION_OPENED',
       REUSABLE_DIALOG_OPENED: 'REUSABLE_DIALOG_OPENED',
     },
     roomList: {
index eb59e7a228ff670667b9f991329a5b9da7ed15aa..bbecb2e13cc92174d31cce4d7ceeaaa7f6b3ed1c 100644 (file)
@@ -131,6 +131,9 @@ class Navigation extends EventEmitter {
       [cons.actions.navigation.OPEN_SETTINGS]: () => {
         this.emit(cons.events.navigation.SETTINGS_OPENED, action.tabText);
       },
+      [cons.actions.navigation.OPEN_NAVIGATION]: () => {
+        this.emit(cons.events.navigation.NAVIGATION_OPENED);
+      },
       [cons.actions.navigation.OPEN_EMOJIBOARD]: () => {
         this.emit(
           cons.events.navigation.EMOJIBOARD_OPENED,