Skip to content

Commit

Permalink
Merge branch 'dev' into chatbar-payment-ui-update
Browse files Browse the repository at this point in the history
  • Loading branch information
phillsatellite authored Dec 5, 2024
2 parents cb0648c + 10a028c commit 5693c7c
Show file tree
Hide file tree
Showing 15 changed files with 310 additions and 30 deletions.
3 changes: 2 additions & 1 deletion android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ dependencies {
implementation project(':capacitor-clipboard')
implementation project(':capacitor-device')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-screen-orientation')
implementation project(':capacitor-splash-screen')

}


if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
3 changes: 3 additions & 0 deletions android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ project(':capacitor-device').projectDir = new File('../node_modules/@capacitor/d
include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')

include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')

include ':capacitor-screen-orientation'
project(':capacitor-screen-orientation').projectDir = new File('../node_modules/@capacitor/screen-orientation/android')

Expand Down
1 change: 1 addition & 0 deletions ios/App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def capacitor_pods
pod 'CapacitorClipboard', :path => '../../node_modules/@capacitor/clipboard'
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorScreenOrientation', :path => '../../node_modules/@capacitor/screen-orientation'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
end
Expand Down
14 changes: 10 additions & 4 deletions ios/App/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ PODS:
- Capacitor
- CapacitorFilesystem (6.0.2):
- Capacitor
- CapacitorKeyboard (6.0.3):
- Capacitor
- CapacitorScreenOrientation (6.0.3):
- Capacitor
- CapacitorSplashScreen (6.0.3):
- CapacitorSplashScreen (6.0.2):
- Capacitor

DEPENDENCIES:
Expand All @@ -22,6 +24,7 @@ DEPENDENCIES:
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
- "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
- "CapacitorScreenOrientation (from `../../node_modules/@capacitor/screen-orientation`)"
- "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"

Expand All @@ -38,6 +41,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@capacitor/device"
CapacitorFilesystem:
:path: "../../node_modules/@capacitor/filesystem"
CapacitorKeyboard:
:path: "../../node_modules/@capacitor/keyboard"
CapacitorScreenOrientation:
:path: "../../node_modules/@capacitor/screen-orientation"
CapacitorSplashScreen:
Expand All @@ -50,9 +55,10 @@ SPEC CHECKSUMS:
CapacitorCordova: b33e7f4aa4ed105dd43283acdd940964374a87d9
CapacitorDevice: 1a215717f0b5061503b21a03508b0ec458a57d78
CapacitorFilesystem: c832a3f6d4870c3872688e782ae8e33665e6ecbf
CapacitorKeyboard: 460c6f9ec5e52c84f2742d5ce2e67bbc7ab0ebb0
CapacitorScreenOrientation: 3bb823f5d265190301cdc5d58a568a287d98972a
CapacitorSplashScreen: 68893659d77b5f82d753b3a70475082845e3039c
CapacitorSplashScreen: 250df9ef8014fac5c7c1fd231f0f8b1d8f0b5624

PODFILE CHECKSUM: 80366870d5c5081f271e0ddeab86b283217ebd9d
PODFILE CHECKSUM: 0bfaa008b5f31bb57606a8c6259197a6af507ba4

COCOAPODS: 1.16.1
COCOAPODS: 1.16.2
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@capacitor/device": "^6.0.2",
"@capacitor/filesystem": "^6.0.1",
"@capacitor/ios": "^6.2.0",
"@capacitor/keyboard": "^6.0.3",
"@capacitor/screen-orientation": "^6.0.3",
"@capacitor/splash-screen": "^6.0.2",
"@dicebear/collection": "^9.0.1",
Expand Down
104 changes: 85 additions & 19 deletions src/lib/components/ui/ContextMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
import { clickoutside } from "@svelte-put/clickoutside"
import { Appearance } from "$lib/enums"
import type { ContextItem } from "$lib/types"
import { createEventDispatcher, tick } from "svelte"
import { createEventDispatcher, onDestroy, onMount, tick } from "svelte"
import { log } from "$lib/utils/Logger"
let visible: boolean = false
let coords: [number, number] = [0, 0]
let context: HTMLElement
let touchTimeout: number | undefined
let slotContainer: HTMLElement
export let items: ContextItem[] = []
export let hook: string = ""
Expand All @@ -25,38 +27,89 @@
close_context = undefined
}
function calculatePos(evt: MouseEvent): [number, number] {
if (context === undefined) return [evt.clientX, evt.clientY]
function calculatePos(evt: MouseEvent | TouchEvent): [number, number] {
if (!context) {
if (evt instanceof MouseEvent) {
return [evt.clientX, evt.clientY]
} else if (evt instanceof TouchEvent) {
const touch = evt.touches[0]
return [touch.clientX, touch.clientY]
}
return [0, 0]
}
const { width, height } = context.getBoundingClientRect()
let offsetX = evt.pageX
let offsetY = evt.pageY
let screenWidth = evt.view!.innerWidth
let screenHeight = evt.view!.innerHeight
let overFlowX = screenWidth < width + offsetX
let overFlowY = screenHeight < height + offsetY
let topX = overFlowX ? Math.max(5, screenWidth - width - 5) : Math.max(5, offsetX)
if (screenHeight - offsetY < height + 30) {
let adjustedY = offsetY - height
let topY = Math.max(5, adjustedY)
return [topX, topY]
let offsetX: number, offsetY: number, screenWidth: number, screenHeight: number
if (evt instanceof MouseEvent) {
offsetX = evt.pageX
offsetY = evt.pageY
screenWidth = evt.view!.innerWidth
screenHeight = evt.view!.innerHeight
} else if (evt instanceof TouchEvent) {
const touch = evt.touches[0]
const targetElement = touch.target as HTMLElement
const doc = targetElement.ownerDocument!
const win = doc.defaultView!
offsetX = touch.pageX
offsetY = touch.pageY
screenWidth = win.innerWidth
screenHeight = win.innerHeight
} else {
let topY = Math.max(5, overFlowY ? offsetY - height : offsetY)
return [topX, topY]
return [0, 0]
}
// Calculate overflow
const overFlowX = screenWidth < width + offsetX
const overFlowY = screenHeight < height + offsetY
// Adjust X position
const topX = overFlowX ? Math.max(5, screenWidth - width - 5) : Math.max(5, offsetX)
// Adjust Y position
const topY = screenHeight - offsetY < height + 30 ? Math.max(5, offsetY - height) : Math.max(5, overFlowY ? offsetY - height : offsetY)
return [topX, topY]
}
async function openContext(evt: MouseEvent) {
async function openContext(evt: MouseEvent | TouchEvent) {
if (close_context !== undefined) {
close_context()
}
close_context = () => (visible = false)
evt.preventDefault()
visible = true
coords = [evt.clientX, evt.clientY]
if (evt instanceof MouseEvent) {
coords = [evt.clientX, evt.clientY]
} else if (evt instanceof TouchEvent) {
const touch = evt.touches[0]
coords = [touch.clientX, touch.clientY]
}
await tick()
coords = calculatePos(evt)
}
function handleTouchStart(evt: TouchEvent) {
document.body.style.userSelect = "none"
touchTimeout = window.setTimeout(() => {
openContext(evt)
}, 350)
}
function handleTouchEnd() {
if (touchTimeout !== undefined) {
clearTimeout(touchTimeout)
touchTimeout = undefined
}
}
function handleItemClick(e: MouseEvent, item: ContextItem) {
e.stopPropagation()
log.info(`Clicked ${item.text}`)
Expand All @@ -66,9 +119,22 @@
})
onClose(customEvent)
}
onMount(() => {
// Add event listeners for mobile
slotContainer.addEventListener("touchstart", handleTouchStart)
slotContainer.addEventListener("touchend", handleTouchEnd)
})
onDestroy(() => {
slotContainer.removeEventListener("touchstart", handleTouchStart)
slotContainer.removeEventListener("touchend", handleTouchEnd)
})
</script>

<slot name="content" open={openContext} />
<div bind:this={slotContainer}>
<slot name="content" open={openContext} />
</div>
{#if visible}
<div id="context-menu" data-cy={hook} bind:this={context} use:clickoutside on:clickoutside={onClose} style={`left: ${coords[0]}px; top: ${coords[1]}px;`}>
<slot name="items" close={onClose}></slot>
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/ui/InstallBanner.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
text={platformButtonProps[platform].text}
on:click={() => {
window.open(platformButtonProps[platform].download)
closeBanner()
}}>
<Icon icon={platformButtonProps[platform].icon} highlight={Appearance.Success} />
</Button>
Expand Down
154 changes: 154 additions & 0 deletions src/lib/layouts/BottomNavBarMobile.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<script lang="ts">
import { Button, Icon, Text } from "$lib/elements"
import { Appearance, CommunitySettingsRoute, Route, SettingsRoute } from "$lib/enums"
import { VoiceRTCInstance } from "$lib/media/Voice"
import { SettingsStore } from "$lib/state"
import { Store } from "$lib/state/Store"
import { UIStore } from "$lib/state/ui"
import type { FriendRequest, NavRoute } from "$lib/types"
import { checkMobile, isAndroidOriOS } from "$lib/utils/Mobile"
import { createEventDispatcher, onDestroy } from "svelte"
import { get } from "svelte/store"
export let routes: NavRoute[] = []
export let activeRoute: Route | SettingsRoute | CommunitySettingsRoute = Route.Home
export let icons: boolean = false
export let vertical: boolean = false
let incomingRequests: FriendRequest[] = []
let totalUnreads: number = 0
let badgeCounts: Record<string, number> = {}
const dispatch = createEventDispatcher()
// Subscribe to stores and update local state
const unsubscribeStore = Store.state.activeRequests.subscribe(r => {
incomingRequests = Store.inboundRequests(r)
updateBadgeCounts()
})
const unsubscribeUIStore = UIStore.state.chats.subscribe(() => {
totalUnreads = UIStore.getTotalNotifications()
updateBadgeCounts()
})
function updateBadgeCounts() {
badgeCounts = {}
for (const route of routes) {
badgeCounts[route.to] = calculateBadgeCount(route.to)
}
}
function calculateBadgeCount(route: string): number {
if (route === "/friends") {
return incomingRequests.length
} else if (route === "/chat") {
return totalUnreads
}
return 0
}
function handleNavigate(route: NavRoute) {
if (checkMobile() && !overrides(route)) UIStore.state.sidebarOpen.set(false)
if (isAndroidOriOS() && (route.to === Route.Settings || (route.to === Route.Chat && get(UIStore.state.chats).length > 0))) {
if (get(UIStore.state.sidebarOpen) === false) {
UIStore.toggleSidebar()
// Avoid keyboard when navigate to chat preview list
}
}
dispatch("navigate", route.to.toString())
}
function overrides(route: NavRoute) {
if (route.to === Route.Chat && $settings.messaging.quick) {
return true
}
if (route.to === Route.Settings) return true
}
// Clean up subscriptions when component is destroyed
onDestroy(() => {
setTimeout(() => {
if (get(Store.state.activeCall)) {
Store.setActiveCall(Store.getCallingChat(VoiceRTCInstance.channel!)!)
}
}, 100)
unsubscribeStore()
unsubscribeUIStore()
})
$: settings = SettingsStore.state
</script>

<div class="content"></div>

<div class="navigation {vertical ? 'vertical' : 'horizontal'} {icons ? 'icons' : ''}">
{#each routes as route}
<div class="navigation-control {!icons ? 'fill' : ''}">
<Button
hook="button-{route.name}"
badge={badgeCounts[route.to]}
fill={!icons}
tooltip={route.name}
icon={icons}
outline={activeRoute !== route.to && !icons}
appearance={activeRoute === route.to ? Appearance.Primary : Appearance.Alt}
on:click={() => handleNavigate(route)}>
<Icon alt={activeRoute === route.to} icon={route.icon} />
{#if !icons}
<Text appearance={activeRoute !== route.to ? Appearance.Default : Appearance.Alt}>{route.name}</Text>
{/if}
</Button>
</div>
{/each}
</div>

<style lang="scss">
.content {
height: 60px;
pointer-events: none;
background: transparent;
border: none;
}
.navigation {
display: inline-flex;
gap: var(--gap);
justify-content: space-evenly;
width: 100%;
border-top: 1px solid var(--border-color, #ccc);
box-shadow: 0px -4px 10px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
z-index: 1000;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: var(--padding, 10px);
&.vertical {
flex-direction: column;
}
.navigation-control {
display: inline-flex;
flex-direction: column;
align-items: center;
&.fill {
flex: 1;
:global(.text) {
justify-content: space-between;
}
}
}
}
@media only screen and (max-width: 600px) {
.navigation {
padding-bottom: var(--padding, 10px);
}
}
</style>
Loading

0 comments on commit 5693c7c

Please sign in to comment.