Skip to content

Commit

Permalink
a lot of comments
Browse files Browse the repository at this point in the history
  • Loading branch information
North-West-Wind committed Jan 19, 2025
1 parent b4862d5 commit 5b8c673
Show file tree
Hide file tree
Showing 12 changed files with 30 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/app/components/SlideMenuChild.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Home } from "../pages/client/home";
import { Inbox } from "../pages/client/inbox";
import { Space } from "../pages/client/space";

// A component to match path and return the corresponding slide menu parent.
export function SlideMenuChild() {
const location = useLocation();

Expand Down
1 change: 1 addition & 0 deletions src/app/components/page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export const PageContentCenter = as<'div'>(({ className, ...props }, ref) => (
<div className={classNames(css.PageContentCenter, className)} {...props} ref={ref} />
));

// Only used in mobile for slide menu
export function PageRootFloat({ children, style }: { children: ReactNode, style: CSSProperties }) {
return (
<Box grow="Yes" style={style} className={classNames(css.PageRootFloat, ContainerColor({ variant: 'Background' }))}>
Expand Down
1 change: 1 addition & 0 deletions src/app/features/room/Room.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function Room() {
</>
)}
</Box>
{/* Create a slide menu offscreen for mobile. Same for all other slide menus. */}
{screenSize === ScreenSize.Mobile && <PageRootFloat style={{
transform: `translateX(${offsetOverride ? 0 : (-window.innerWidth + offset[0])}px)`,
transition: offset[0] ? "none" : ""
Expand Down
1 change: 1 addition & 0 deletions src/app/features/room/RoomTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
[mx, room, editor]
);

// This is been changed to accept the replyId directly instead of a mouse event in order to be used for swipe left reply.
const handleReply = useCallback(
(replyId: string) => {
const replyEvt = room.findEventById(replyId);
Expand Down
2 changes: 2 additions & 0 deletions src/app/features/room/message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -709,11 +709,13 @@ export const Message = as<'div', MessageProps>(
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
const [emojiBoardAnchor, setEmojiBoardAnchor] = useState<RectCords>();
// Swipe left gesture that, if it is pulled to the left by 20% of screen width, trigger a reply
const { offset, onTouchStart, onTouchEnd, onTouchMove } = useTouchOffset({ offsetLimit: [-0.25 * window.innerWidth, 0, 0, 0], touchEndCallback: ([x]) => {
if (x < -0.2 * window.innerWidth)
onReply(mEvent.getId()!);
}});

// Wrapper of the new onReply for the old onReplyClick
const onReplyClick: MouseEventHandler<HTMLButtonElement> = useCallback((evt) => {
const replyId = evt.currentTarget.getAttribute('data-event-id');
if (!replyId) {
Expand Down
1 change: 1 addition & 0 deletions src/app/hooks/useGoBack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useNavigate, useLocation, matchPath } from "react-router-dom";
import { HOME_PATH, DIRECT_PATH, SPACE_PATH, EXPLORE_PATH, INBOX_PATH } from "../pages/paths";
import { getHomePath, getDirectPath, getSpacePath, getExplorePath, getInboxPath } from "../pages/pathUtils";

// Moved goBack from BackRouteHandler for reusabilitiy
export function useGoBack() {
const navigate = useNavigate();
const location = useLocation();
Expand Down
2 changes: 2 additions & 0 deletions src/app/hooks/useSlideMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState } from "react";
import { useGoBack } from "./useGoBack";
import { useTouchOffset } from "./useTouchOffset";

// Reusable slide menu gesture handler
export function useSlideMenu() {
const goBack = useGoBack();
const [offsetOverride, setOffsetOverride] = useState(false);
Expand All @@ -11,6 +12,7 @@ export function useSlideMenu() {
touchEndCallback: (offset, velocity, averageVelocity) => {
if (offset[0] > window.innerWidth * 0.5 || (velocity && velocity[0] > 0.9) || (averageVelocity && averageVelocity[0] > 0.8)) {
setOffsetOverride(true);
// Slide menu transition takes .25s so we wait for that.
setTimeout(() => goBack(), 250);
}
}
Expand Down
21 changes: 17 additions & 4 deletions src/app/hooks/useTouchOffset.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useEffect, useState } from "react";
import React, { useState } from "react";

type Vec2 = [number, number];
type Limit = [number | undefined, number | undefined, number | undefined, number | undefined]; // x+, x-, y+, y-
type Vec2 = [number, number]; // x, y
type Limit = [number | undefined, number | undefined, number | undefined, number | undefined]; // min x, max x, min y, max y

// Function for calculating average touch position from multiple touches.
function getTouchListAverageCoordinates(list: React.TouchList) {
return Array.from(list).map(touch => [touch.clientX, touch.clientY]).reduce((a, b) => {
a[0] += b[0];
Expand All @@ -11,27 +12,31 @@ function getTouchListAverageCoordinates(list: React.TouchList) {
}).map(coord => coord / list.length) as Vec2;
}

// Used for offset limits. It makes sure the offset value is within the range. Undefined means there is no limit for min/max.
function clamp(val: number, min?: number, max?: number) {
if (min === undefined && max === undefined) return val;
if (min === undefined) return Math.min(val, max!);
if (max === undefined) return Math.max(val, min!);
return Math.max(Math.min(val, max), min);
}

// Used for start limits. Returns a boolean indicating if a value is valid to be considered as a starting point.
function isBetween(val: number, min?: number, max?: number) {
if (min === undefined && max === undefined) return true;
if (min === undefined) return val <= max!;
if (max === undefined) return val >= min!;
return val >= min && val <= max;
}

// Returns the offset from start point to end point
export function useTouchOffset(options?: { startLimit?: Limit, offsetLimit?: Limit, touchEndCallback?: (offset: Vec2, velocity?: Vec2, averageVelocity?: Vec2) => any }) {
const [startPos, setStartPos] = useState<Vec2 | null>(null);
const [offset, setOffset] = useState<Vec2>([0, 0]);
const [velocity, setVelocity] = useState<Vec2>([0, 0]);
const [_, setLastTime] = useState(Date.now());
const [startTime, setStartTime] = useState(Date.now());

// Wrapper of setOffset and setVelocity, making sure offset is within limit
const limitedSetOffset = (coords: Vec2) => {
setLastTime(lt => {
const now = Date.now();
Expand All @@ -45,28 +50,36 @@ export function useTouchOffset(options?: { startLimit?: Limit, offsetLimit?: Lim
const onTouchStart = (event: React.TouchEvent) => {
const coords = getTouchListAverageCoordinates(event.touches);
if (!startPos) {
// Start if limit is not set, or the touch point is within limits
if (!options?.startLimit || coords.map((coord, ii) => isBetween(coord, options.startLimit![ii * 2], options.startLimit![ii * 2 + 1])).every(x => x)) {
setStartPos(coords);
setLastTime(Date.now());
setStartTime(Date.now());
}
} else limitedSetOffset(coords.map((coord, ii) => coord - startPos[ii]) as Vec2);
}
// Touches after the first one. Calculate offset using the average point.
else limitedSetOffset(coords.map((coord, ii) => coord - startPos[ii]) as Vec2);
};

const onTouchEnd = (event: React.TouchEvent) => {
// Length 0 means there are no more touching points
if (event.touches.length == 0) {
// A callback with offset, velocity and average velocity. Used to determine if the gesture is strong enough for actions.
if (options?.touchEndCallback) options.touchEndCallback(offset, velocity, !startPos ? undefined : offset.map(((coord, ii) => (coord - startPos[ii]) / (Date.now() - startTime))) as Vec2);
// Reset starting point, offset and velocity.
setStartPos(null);
setOffset([0, 0]);
setVelocity([0, 0]);
} else if (startPos) {
// There are still point(s) being touched. Use the average of them to calculate offset.
const coords = getTouchListAverageCoordinates(event.touches);
limitedSetOffset(coords.map((coord, ii) => coord - startPos[ii]) as Vec2);
}
};

const onTouchMove = (event: React.TouchEvent) => {
const coords = getTouchListAverageCoordinates(event.touches);
// Update offset according to touch point movement
if (startPos) limitedSetOffset(coords.map((coord, ii) => coord - startPos[ii]) as Vec2);
};

Expand Down
1 change: 1 addition & 0 deletions src/app/pages/client/explore/Featured.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export function FeaturedRooms() {
</PageContent>
</Scroll>
</Box>
{/* Create a slide menu offscreen for mobile. Same for all other slide menus. */}
{screenSize === ScreenSize.Mobile && <PageRootFloat style={{
transform: `translateX(${offsetOverride ? 0 : (-window.innerWidth + offset[0])}px)`,
transition: offset[0] ? "none" : ""
Expand Down
1 change: 1 addition & 0 deletions src/app/pages/client/explore/Server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ export function PublicRooms() {
</PageContent>
</Scroll>
</Box>
{/* Create a slide menu offscreen for mobile. Same for all other slide menus. */}
{screenSize === ScreenSize.Mobile && <PageRootFloat style={{
transform: `translateX(${offsetOverride ? 0 : (-window.innerWidth + offset[0])}px)`,
transition: offset[0] ? "none" : ""
Expand Down
1 change: 1 addition & 0 deletions src/app/pages/client/inbox/Invites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export function Invites() {
</PageContent>
</Scroll>
</Box>
{/* Create a slide menu offscreen for mobile. Same for all other slide menus. */}
{screenSize === ScreenSize.Mobile && <PageRootFloat style={{
transform: `translateX(${offsetOverride ? 0 : (-window.innerWidth + offset[0])}px)`,
transition: offset[0] ? "none" : ""
Expand Down
1 change: 1 addition & 0 deletions src/app/pages/client/inbox/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,7 @@ export function Notifications() {
</PageContent>
</Scroll>
</Box>
{/* Create a slide menu offscreen for mobile. Same for all other slide menus. */}
{screenSize === ScreenSize.Mobile && <PageRootFloat style={{
transform: `translateX(${offsetOverride ? 0 : (-window.innerWidth + offset[0])}px)`,
transition: offset[0] ? "none" : ""
Expand Down

0 comments on commit 5b8c673

Please sign in to comment.