From 3e0fa8ec4663bc205d54fd836eaef3225941e113 Mon Sep 17 00:00:00 2001 From: Global Server Date: Sun, 2 Jan 2022 19:43:27 +0100 Subject: [PATCH] Support for people nearby --- src/components/peerProfile.ts | 2 +- src/components/sidebarLeft/index.ts | 5 - .../sidebarLeft/tabs/PeopleNearby.ts | 110 ++++++++++++++---- .../sidebarLeft/tabs/generalSettings.ts | 30 +++++ src/components/sidebarLeft/tabs/newGroup.ts | 18 ++- src/lang.ts | 18 ++- src/lib/appManagers/appStateManager.ts | 2 + src/scss/partials/_peopleNearby.scss | 6 + 8 files changed, 156 insertions(+), 35 deletions(-) diff --git a/src/components/peerProfile.ts b/src/components/peerProfile.ts index faa96203..cc9d0619 100644 --- a/src/components/peerProfile.ts +++ b/src/components/peerProfile.ts @@ -126,7 +126,7 @@ export default class PeerProfile { this.location = new Row({ title: ' ', - subtitleLangKey: 'AttachLocation', + subtitleLangKey: 'ChatLocation', icon: 'location' }); diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index fb8376dc..7b6eb553 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -43,7 +43,6 @@ import { closeBtnMenu } from "../misc"; import { indexOfAndSplice } from "../../helpers/array"; import ButtonIcon from "../buttonIcon"; import confirmationPopup from "../confirmationPopup"; -import AppUsersManager from "../../lib/appManagers/appUsersManager"; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; @@ -255,10 +254,6 @@ export class AppSidebarLeft extends SidebarSlider { icon: 'newgroup', text: 'NewGroup', onClick: onNewGroupClick - },{ - icon: 'newgroup', - text: 'NewGeoGroup', - onClick: onNewGeoGroupClick }, { icon: 'newprivate', text: 'NewPrivateChat', diff --git a/src/components/sidebarLeft/tabs/PeopleNearby.ts b/src/components/sidebarLeft/tabs/PeopleNearby.ts index 3f844ab8..3ea4e533 100644 --- a/src/components/sidebarLeft/tabs/PeopleNearby.ts +++ b/src/components/sidebarLeft/tabs/PeopleNearby.ts @@ -19,7 +19,12 @@ import PeerTitle from "../../peerTitle"; import lottieLoader from "../../../lib/rlottie/lottieLoader"; import PopupPeer from "../../popups/peer"; import AppNewGroupTab from "./newGroup"; +import { toast } from "../../toast"; +import { ButtonMenuItemOptions } from "../../buttonMenu"; +import { cancelEvent } from "../../../helpers/dom/cancelEvent"; import type { LazyLoadQueueIntersector } from "../../lazyLoadQueue"; +import I18n, { i18n } from "../../../lib/langPack"; +import rootScope from '../../../lib/rootScope'; export default class AppPeopleNearby extends SliderSuperTab { private usersCategory = new SearchGroup(true, 'contacts', true, 'people-nearby-users', false); @@ -28,25 +33,38 @@ export default class AppPeopleNearby extends SliderSuperTab { private isLocationWatched: boolean = false; private errorCategory: HTMLElement; private retryBtn: HTMLButtonElement; + private btnOptions: HTMLButtonElement; + private menuButtons: (ButtonMenuItemOptions & {verify?: () => boolean})[]; protected lazyLoadQueue: LazyLoadQueueIntersector; protected init() { this.container.classList.add('peoplenearby-container'); this.setTitle('PeopleNearby'); - - const btnMenu = ButtonMenuToggle({}, 'bottom-left', [{ + + this.menuButtons = [{ icon: 'tip', - text: 'PeopleNearby.VisibilityYes', - onClick: this.startWatching + text: 'MakeMyselfVisible', + onClick: () => this.startWatching(), + verify: () => !this.isLocationWatched }, { icon: 'tip', - text: 'PeopleNearby.VisibilityNo', - onClick: this.startWatching - }]); + text: 'StopShowingMe', + onClick: () => this.stopWatching(), + verify: () => this.isLocationWatched + }, + { + icon: 'newgroup', + text: 'NearbyCreateGroup', + onClick: () => { + new AppNewGroupTab(this.slider).open([], true); + } + }]; + + this.btnOptions = ButtonMenuToggle({}, 'bottom-left', this.menuButtons, () => this.verifyButtons()); - this.header.append(btnMenu); + this.header.append(this.btnOptions); const locatingIcon = document.createElement('span'); locatingIcon.classList.add('tgico', 'tgico-location'); @@ -65,7 +83,10 @@ export default class AppPeopleNearby extends SliderSuperTab { this.errorCategory.classList.add('text', 'hide', 'nearby-error'); this.retryBtn = ButtonCorner({icon: 'check'}); - //this.retryBtn.classList.remove('is-visible'); + + const textContainer = document.createElement('div'); + textContainer.classList.add('text', 'nearby-description'); + textContainer.appendChild(i18n('PeopleNearbyInfo2')); const chatsContainer = document.createElement('div'); chatsContainer.classList.add('chatlist-container'); @@ -73,24 +94,58 @@ export default class AppPeopleNearby extends SliderSuperTab { chatsContainer.append(this.groupsCategory.container); this.content.append(this.retryBtn); - this.scrollable.append(locatingAnimation, this.errorCategory, chatsContainer); + this.scrollable.append( + locatingAnimation, + textContainer, + this.errorCategory, + chatsContainer + ); + } + + public onCloseAfterTimeout() { + this.usersCategory.clear(); + this.groupsCategory.clear(); + } + + private verifyButtons(e?: Event){ + const isMenuOpen = !!e || !!(this.btnOptions && this.btnOptions.classList.contains('menu-open')); + e && cancelEvent(e); + + this.menuButtons.filter(button => button.verify).forEach(button => { + button.element.classList.toggle('hide', !button.verify()); + }); } private parseDistance(distance: number){ - return (distance >= 1000 ? String(distance/1000)+' km' : String(distance)+' m'); + if(rootScope.settings.distanceUnit == 'miles'){ + if(distance > 1609.34) { + return i18n('MilesAway', [Math.round(distance / 1609)]); + }else{ + return i18n('FootsAway', [Math.round(distance * 3.281)]); + } + }else{ + if(distance >= 1000){ + return i18n('KMetersAway2', [distance / 1000]); + }else{ + return i18n('MetersAway2', [distance]); + } + } } + // @ts-ignore public open() { const result = super.open(); result.then(() => { this.retryBtn.classList.remove('is-visible'); - navigator.geolocation.getCurrentPosition(location => { + navigator.geolocation.getCurrentPosition((location) => { this.latestLocationSaved = { latitude: location.coords.latitude, longitude: location.coords.longitude, accuracy: location.coords.accuracy }; + console.log(this.latestLocationSaved); + appUsersManager.getLocated( location.coords.latitude, location.coords.longitude, @@ -130,16 +185,16 @@ export default class AppPeopleNearby extends SliderSuperTab { break; } } - dom.lastMessageSpan.append(', '+String(participantsCount)+' members'); + dom.lastMessageSpan.append(', '+i18n('Members', [participantsCount])); } }); this.usersCategory.nameEl.textContent = ''; - this.usersCategory.nameEl.append('Users'); + this.usersCategory.nameEl.append(i18n('PeopleNearbyHeader')); usersCounter && this.usersCategory.setActive(); this.groupsCategory.nameEl.textContent = ''; - this.groupsCategory.nameEl.append('Groups'); + this.groupsCategory.nameEl.append(i18n('ChatsNearbyHeader')); groupsCounter && this.groupsCategory.setActive(); this.errorCategory.classList.toggle('hide', (usersCounter || groupsCounter)); @@ -148,7 +203,7 @@ export default class AppPeopleNearby extends SliderSuperTab { }, (error) => { this.errorCategory.classList.remove('hide'); this.retryBtn.classList.add('is-visible'); - this.retryBtn.addEventListener('click', this.opeddn); + this.retryBtn.addEventListener('click', this.open); if(error instanceof GeolocationPositionError){ this.errorCategory.innerHTML = "Location permission denied. Click below to retry."; }else{ @@ -162,12 +217,14 @@ export default class AppPeopleNearby extends SliderSuperTab { if(!this.latestLocationSaved || this.isLocationWatched) return; this.isLocationWatched = true; + toast('Your position is now being shared. Do not close the page or it will be suspended.'); + appUsersManager.getLocated( this.latestLocationSaved.latitude, this.latestLocationSaved.longitude, this.latestLocationSaved.accuracy, true, // background parameter - 3600 // self_expires parameter + 0x7fffffff // self_expires parameter ); navigator.geolocation.watchPosition( @@ -177,14 +234,14 @@ export default class AppPeopleNearby extends SliderSuperTab { const distanceCheck = this.calculateDistance( result.coords.latitude, result.coords.longitude, this.latestLocationSaved.latitude, this.latestLocationSaved.longitude - ) > 100; + ); if((isLatitudeDifferent || isLongitudeDifferent) && distanceCheck){ appUsersManager.getLocated( result.coords.latitude, result.coords.longitude, result.coords.accuracy, true, // background parameter - 3600 // self_expires parameter + 0x7fffffff // self_expires parameter ); this.latestLocationSaved = { latitude: result.coords.latitude, @@ -193,7 +250,20 @@ export default class AppPeopleNearby extends SliderSuperTab { } } } - ) + ); + } + + private stopWatching(){ + if(!this.isLocationWatched) return; + this.isLocationWatched = false; + toast('The sharing of your position has been stopped. You will no longer be visible to other users.'); + appUsersManager.getLocated( + 0, // latitude parameter + 0, // longitude parameter + 0, // accuracy parameter + false, // background parameter + 0 // self_expires parameter + ); } private calculateDistance(lat1: number, long1: number, lat2: number, long2: number){ diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index b7aec737..1ada653f 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -134,6 +134,36 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { container.append(form); } + { + const container = section('DistanceUnitsTitle'); + + const form = document.createElement('form'); + + const name = 'distance-unit'; + const stateKey = 'settings.distanceUnit'; + + const kilometersRow = new Row({ + radioField: new RadioField({ + langKey: 'DistanceUnitsKilometers', + name, + value: 'kilometers', + stateKey + }) + }); + + const milesRow = new Row({ + radioField: new RadioField({ + langKey: 'DistanceUnitsMiles', + name, + value: 'miles', + stateKey + }) + }); + + form.append(kilometersRow.container, milesRow.container); + container.append(form); + } + { const container = section('General.TimeFormat'); diff --git a/src/components/sidebarLeft/tabs/newGroup.ts b/src/components/sidebarLeft/tabs/newGroup.ts index 9a8773f4..6f5a5177 100644 --- a/src/components/sidebarLeft/tabs/newGroup.ts +++ b/src/components/sidebarLeft/tabs/newGroup.ts @@ -13,7 +13,7 @@ import { SearchGroup } from "../../appSearch"; import InputField from "../../inputField"; import { SliderSuperTab } from "../../slider"; import AvatarEdit from "../../avatarEdit"; -import { i18n } from "../../../lib/langPack"; +import I18n, { i18n } from "../../../lib/langPack"; import ButtonCorner from "../../buttonCorner"; interface OpenStreetMapInterface { @@ -57,7 +57,7 @@ export default class AppNewGroupTab extends SliderSuperTab { }); this.groupLocationInputField = new InputField({ - label: 'ChannelGeoGroupLocation', + label: 'ChatLocation', name: 'location', canBeEdited: false }); @@ -134,9 +134,9 @@ export default class AppNewGroupTab extends SliderSuperTab { this.peerIds = peerIds; if(isGeoChat){ - this.setTitle('NewGeoGroup'); + this.setTitle('NearbyCreateGroup'); this.groupLocationInputField.container.classList.remove('hide'); - this.groupLocationInputField.setValueSilently('Locating...'); + this.groupLocationInputField.setValueSilently(I18n.format('Loading', true)); this.startLocating(); }else{ this.groupLocationInputField.container.classList.add('hide'); @@ -156,14 +156,14 @@ export default class AppNewGroupTab extends SliderSuperTab { this.searchGroup.nameEl.textContent = ''; this.searchGroup.nameEl.append(i18n('Members', [this.peerIds.length])); - this.searchGroup.setActive(); + this.peerIds.length && this.searchGroup.setActive(); }); return result; } private startLocating(){ - navigator.geolocation.getCurrentPosition(location => { + navigator.geolocation.getCurrentPosition((location) => { this.userLocationCoords = { lat: location.coords.latitude, long: location.coords.longitude @@ -184,6 +184,12 @@ export default class AppNewGroupTab extends SliderSuperTab { this.groupLocationInputField.setValueSilently(response.display_name); } ); + }, (error) => { + if(error instanceof GeolocationPositionError){ + this.groupLocationInputField.setValueSilently('Location permission denied. Please retry later.'); + }else{ + this.groupLocationInputField.setValueSilently('An error has occurred. Please retry later.'); + } }); } } \ No newline at end of file diff --git a/src/lang.ts b/src/lang.ts index d7310bf9..b00f640b 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -1,8 +1,21 @@ const lang = { + "DistanceUnitsTitle":"Distance units", + "DistanceUnitsKilometers":"Kilometers", + "DistanceUnitsMiles":"Miles", "PeopleNearby":"PeopleNearby", - "PeopleNearby.VisibilityYes":"Make me visible", - "PeopleNearby.VisibilityNo":"Disable visibility", + "MakeMyselfVisible":"Make myself visible", + "StopShowingMe":"Stop showing me", + "PeopleNearbyInfo2":"Exchange contact info with people nearby and find new friends.", + "NearbyCreateGroup":"Create a Local group", "GrantPermissions":"Grant me permissions", + "AwayTo":"%1$s away", + "KMetersAway2":"%1$s km away", + "MetersAway2":"%1$s m away", + "MilesAway":"%1$s mi away", + "FootsAway":"%1$s ft away", + "PeopleNearbyHeader":"People nearby", + "ChatsNearbyHeader":"Groups nearby", + "ChatLocation":"Location", "Animations": "Animations", "AttachAlbum": "Album", "Appearance.Color.Hex": "HEX", @@ -235,7 +248,6 @@ const lang = { "UsernameHelpLink": "This link opens a chat with you:\n%1$s", "NewChannel": "New Channel", "NewGroup": "New Group", - "NewGeoGroup": "New Geo-Group", "Contacts": "Contacts", "SavedMessages": "Saved Messages", "Settings": "Settings", diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index 380d53c5..ad736399 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -70,6 +70,7 @@ export type State = { hiddenPinnedMessages: {[peerId: PeerId]: number}, settings: { messagesTextSize: number, + distanceUnit: 'kilometers' | 'miles', sendShortcut: 'enter' | 'ctrlEnter', animationsEnabled: boolean, autoDownload: { @@ -123,6 +124,7 @@ export const STATE_INIT: State = { hiddenPinnedMessages: {}, settings: { messagesTextSize: 16, + distanceUnit: 'kilometers', sendShortcut: 'enter', animationsEnabled: true, autoDownload: { diff --git a/src/scss/partials/_peopleNearby.scss b/src/scss/partials/_peopleNearby.scss index 76847182..1165025f 100644 --- a/src/scss/partials/_peopleNearby.scss +++ b/src/scss/partials/_peopleNearby.scss @@ -1,3 +1,9 @@ +div.text.nearby-description { + margin-top: 15px; + text-align: center; + color: var(--primary-text-color); +} + div.text.nearby-error { color: var(--gc-secondary-text-color); margin-top: 10px;