@ -5,31 +5,27 @@
* /
* /
import { SliderSuperTab } from "../../slider" ;
import { SliderSuperTab } from "../../slider" ;
import AvatarElement from "../../avatar" ;
import ButtonCorner from "../../buttonCorner" ;
import ButtonCorner from "../../buttonCorner" ;
import { InputUser } from "../../../layer" ;
import apiManager from "../../../lib/mtproto/mtprotoworker" ;
import appUsersManager from "../../../lib/appManagers/appUsersManager" ;
import appUsersManager from "../../../lib/appManagers/appUsersManager" ;
import appDialogsManager from "../../../lib/appManagers/appDialogsManager" ;
import appImManager from "../../../lib/appManagers/appImManager" ;
import ButtonMenuToggle from "../../buttonMenuToggle" ;
import { SearchGroup } from "../../appSearch" ;
import Button from "../../button" ;
import PeerTitle from "../../peerTitle" ;
import lottieLoader from "../../../lib/rlottie/lottieLoader" ;
import PopupPeer from "../../popups/peer" ;
import AppNewGroupTab from "./newGroup" ;
import AppNewGroupTab from "./newGroup" ;
import { toast } from "../../toast" ;
import { toast } from "../../toast" ;
import { ButtonMenuItemOptions } from "../../buttonMenu" ;
import { ButtonMenuItemOptions } from "../../buttonMenu" ;
import { cancelEvent } from "../../../helpers/dom/cancelEvent" ;
import type { LazyLoadQueueIntersector } from "../../lazyLoadQueue" ;
import type { LazyLoadQueueIntersector } from "../../lazyLoadQueue" ;
import I18n , { i18n } from "../../../lib/langPack" ;
import { i18n , join , _i18n } from "../../../lib/langPack" ;
import rootScope from '../../../lib/rootScope' ;
import rootScope from '../../../lib/rootScope' ;
import { wrapSticker } from "../../wrappers" ;
import appStickersManager from "../../../lib/appManagers/appStickersManager" ;
import SortedUserList from "../../sortedUserList" ;
import { PeerLocated , Update , Updates } from "../../../layer" ;
import appPeersManager from "../../../lib/appManagers/appPeersManager" ;
import { SettingChatListSection } from ".." ;
import appProfileManager from "../../../lib/appManagers/appProfileManager" ;
import appDialogsManager from "../../../lib/appManagers/appDialogsManager" ;
import { attachClickEvent } from "../../../helpers/dom/clickEvent" ;
import confirmationPopup from "../../confirmationPopup" ;
export default class AppPeopleNearby extends SliderSuperTab {
export default class AppPeopleNearbyTab extends SliderSuperTab {
private usersCategory = new SearchGroup ( true , 'contacts' , true , 'people-nearby-users' , false ) ;
private latestLocationSaved : { latitude : number , longitude : number , accuracy : number } ;
private groupsCategory = new SearchGroup ( true , 'contacts' , true , 'people-nearby-groups' , false ) ;
private latestLocationSaved : { latitude : number , longitude : number , accuracy : number } ;
private isLocationWatched : boolean = false ;
private isLocationWatched : boolean = false ;
private errorCategory : HTMLElement ;
private errorCategory : HTMLElement ;
private retryBtn : HTMLButtonElement ;
private retryBtn : HTMLButtonElement ;
@ -37,102 +33,155 @@ export default class AppPeopleNearby extends SliderSuperTab {
private menuButtons : ( ButtonMenuItemOptions & { verify ? : ( ) = > boolean } ) [ ] ;
private menuButtons : ( ButtonMenuItemOptions & { verify ? : ( ) = > boolean } ) [ ] ;
protected lazyLoadQueue : LazyLoadQueueIntersector ;
protected lazyLoadQueue : LazyLoadQueueIntersector ;
protected peopleSection : SettingChatListSection ;
protected chatsSection : SettingChatListSection ;
protected locatedPeers : Map < PeerId , PeerLocated.peerLocated > ;
protected init() {
protected init() {
this . container . classList . add ( 'peoplenearby-container' ) ;
this . container . classList . add ( 'people- nearby-container' ) ;
this . setTitle ( 'PeopleNearby' ) ;
this . setTitle ( 'PeopleNearby' ) ;
this . menuButtons = [ {
this . errorCategory = document . createElement ( 'div' ) ;
icon : 'tip' ,
this . errorCategory . classList . add ( 'text' , 'hide' , 'nearby-error' ) ;
text : 'MakeMyselfVisible' ,
onClick : ( ) = > this . startWatching ( ) ,
this . retryBtn = ButtonCorner ( { icon : 'check' } ) ;
verify : ( ) = > ! this . isLocationWatched
const emoji = '🧭' ;
const doc = appStickersManager . getAnimatedEmojiSticker ( emoji ) ;
const stickerContainer = document . createElement ( 'div' ) ;
stickerContainer . classList . add ( 'sticker-container' ) ;
if ( doc ) {
wrapSticker ( {
doc ,
div : stickerContainer ,
loop : false ,
play : true ,
width : 86 ,
height : 86 ,
emoji ,
needUpscale : true
} ) . then ( ( ) = > {
// this.animation = player;
} ) ;
} else {
stickerContainer . classList . add ( 'media-sticker-wrapper' ) ;
}
const caption = document . createElement ( 'div' ) ;
caption . classList . add ( 'caption' ) ;
_i18n ( caption , 'PeopleNearbyInfo2' ) ;
this . locatedPeers = new Map ( ) ;
const m = ( ) = > {
const sortedUserList = new SortedUserList ( {
avatarSize : 42 ,
createChatListOptions : {
dialogSize : 48 ,
new : true
} ,
} ,
{
autonomous : false ,
icon : 'tip' ,
onUpdate : ( element ) = > {
text : 'StopShowingMe' ,
const peer = this . locatedPeers . get ( element . id ) ;
onClick : ( ) = > this . stopWatching ( ) ,
const elements : HTMLElement [ ] = [
verify : ( ) = > this . isLocationWatched
this . parseDistance ( peer . distance )
] ;
if ( ! element . id . isUser ( ) ) {
elements . push ( appProfileManager . getChatMembersString ( element . id . toChatId ( ) ) ) ;
}
element . dom . lastMessageSpan . textContent = '' ;
element . dom . lastMessageSpan . append ( . . . join ( elements , false ) ) ;
} ,
} ,
{
getIndex : ( element ) = > {
icon : 'newgroup' ,
const peer = this . locatedPeers . get ( element . id ) ;
text : 'NearbyCreateGroup' ,
return 0x7FFFFFFF - peer . distance ;
onClick : ( ) = > {
new AppNewGroupTab ( this . slider ) . open ( [ ] , true ) ;
}
}
} ] ;
} ) ;
this . btnOptions = ButtonMenuToggle ( { } , 'bottom-left' , this . menuButtons , ( ) = > this . verifyButtons ( ) ) ;
appDialogsManager . setListClickListener ( sortedUserList . list , undefined , undefined , false ) ;
this . header . append ( this . btnOptions ) ;
return sortedUserList ;
} ;
const locatingIcon = document . createElement ( 'span' ) ;
const peopleSection = this . peopleSection = new SettingChatListSection ( {
locatingIcon . classList . add ( 'tgico' , 'tgico-location' ) ;
name : 'PeopleNearbyHeader' ,
sortedList : m ( )
} ) ;
const locatingAnimation = document . createElement ( 'div' ) ;
const chatsSection = this . chatsSection = new SettingChatListSection ( {
locatingAnimation . classList . add ( 'locating-animation-container' ) ;
name : 'ChatsNearbyHeader' ,
locatingAnimation . appendChild ( locatingIcon ) ;
sortedList : m ( )
} ) ;
for ( let i = 1 ; i <= 4 ; i ++ ) {
const btnMakeVisible = peopleSection . makeButton ( {
let animatingWaves = document . createElement ( 'div' ) ;
text : 'MakeMyselfVisible' ,
animatingWaves . classList . add ( 'locating-animation-waves' , 'wave-' + i ) ;
icon : 'location'
locatingAnimation . appendChild ( animatingWaves ) ;
} ) ;
}
this . errorCategory = document . createElement ( 'div' ) ;
const btnMakeInvisible = peopleSection . makeButton ( {
this . errorCategory . classList . add ( 'text' , 'hide' , 'nearby-error' ) ;
text : 'StopShowingMe' ,
icon : 'location'
} ) ;
this . retryBtn = ButtonCorner ( { icon : 'check' } ) ;
const btnCreateGroup = chatsSection . makeButton ( {
text : 'NearbyCreateGroup' ,
icon : 'newgroup'
} ) ;
const textContainer = document . createElement ( 'div' ) ;
attachClickEvent ( btnMakeVisible , ( ) = > {
textContainer . classList . add ( 'text' , 'nearby-description' ) ;
confirmationPopup ( {
textContainer . appendChild ( i18n ( 'PeopleNearbyInfo2' ) ) ;
titleLangKey : 'MakeMyselfVisibleTitle' ,
descriptionLangKey : 'MakeMyselfVisibleInfo' ,
button : {
langKey : 'OK'
}
} ) . then ( ( ) = > {
this . startWatching ( ) ;
} ) ;
} , { listenerSetter : this.listenerSetter } ) ;
const chatsContainer = document . createElement ( 'div' ) ;
attachClickEvent ( btnMakeInvisible , ( ) = > {
chatsContainer . classList . add ( 'chatlist-container' ) ;
this . stopWatching ( ) ;
chatsContainer . append ( this . usersCategory . container ) ;
} , { listenerSetter : this.listenerSetter } ) ;
chatsContainer . append ( this . groupsCategory . container ) ;
attachClickEvent ( btnCreateGroup , ( ) = > {
new AppNewGroupTab ( this . slider ) . open ( [ ] , true ) ;
} , { listenerSetter : this.listenerSetter } ) ;
btnMakeVisible . classList . add ( 'primary' ) ;
btnMakeInvisible . classList . add ( 'danger' ) ;
btnCreateGroup . classList . add ( 'primary' ) ;
this . content . append ( this . retryBtn ) ;
this . content . append ( this . retryBtn ) ;
this . scrollable . append (
this . scrollable . append (
locatingAnimation ,
stickerContainer ,
textContainer ,
caption ,
this . errorCategory ,
peopleSection . container ,
chatsContainer
chatsSection . container ,
this . errorCategory
) ;
) ;
}
}
public onCloseAfterTimeout() {
private parseDistance ( distance : number ) {
this . usersCategory . clear ( ) ;
if ( rootScope . settings . distanceUnit === 'miles' ) {
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 ) {
if ( rootScope . settings . distanceUnit == 'miles' ) {
if ( distance > 1609.34 ) {
if ( distance > 1609.34 ) {
return i18n ( 'MilesAway' , [ Math . round ( distance / 1609 ) ] ) ;
return i18n ( 'MilesAway' , [ Math . round ( distance / 1609 ) ] ) ;
} else {
} else {
return i18n ( 'FootsAway' , [ Math . round ( distance * 3.281 ) ] ) ;
return i18n ( 'FootsAway' , [ Math . round ( distance * 3.281 ) ] ) ;
}
}
} else {
} else {
if ( distance >= 1000 ) {
if ( distance >= 1000 ) {
return i18n ( 'KMetersAway2' , [ distance / 1000 ] ) ;
return i18n ( 'KMetersAway2' , [ distance / 1000 ] ) ;
} else {
} else {
return i18n ( 'MetersAway2' , [ distance ] ) ;
return i18n ( 'MetersAway2' , [ distance ] ) ;
}
}
}
}
}
}
// @ts-ignore
public open() {
public open() {
const result = super . open ( ) ;
const result = super . open ( ) ;
result . then ( ( ) = > {
result . then ( ( ) = > {
@ -151,69 +200,37 @@ export default class AppPeopleNearby extends SliderSuperTab {
location . coords . longitude ,
location . coords . longitude ,
location . coords . accuracy
location . coords . accuracy
) . then ( ( response ) = > {
) . then ( ( response ) = > {
const update = ( response as Updates . updates ) . updates [ 0 ] as Update . updatePeerLocated ;
// @ts-ignore
const peers = update . peers as PeerLocated . peerLocated [ ] ;
const orderedPeers = response ? . updates [ 0 ] ? . peers . sort ( ( a , b ) = > a . distance - b . distance ) ;
const orderedPeers = peers . sort ( ( a , b ) = > a . distance - b . distance ) ;
// @ts-ignore
const groupsCounter = peers . filter ( ( e ) = > e . peer . _ == 'peerChannel' ) . length ;
const groupsCounter = response ? . updates [ 0 ] ? . peers . filter ( ( e ) = > e . peer . _ == 'peerChannel' ) . length ;
const usersCounter = peers . filter ( ( e ) = > e . peer . _ != 'peerChannel' ) . length ;
// @ts-ignore
const usersCounter = response ? . updates [ 0 ] ? . peers . filter ( ( e ) = > e . peer . _ != 'peerChannel' ) . length ;
// @ts-ignore
orderedPeers ? . forEach ( peer = > {
orderedPeers ? . forEach ( peer = > {
const isChannel = peer . peer . _ == 'peerChannel' ;
const peerId = appPeersManager . getPeerId ( peer . peer ) ;
const peerId = ( isChannel ? - peer.peer.channel_id : peer.peer.user_id ) ;
const section = peerId . isUser ( ) ? this . peopleSection : this.chatsSection ;
this . locatedPeers . set ( peerId , peer ) ;
let { dialog , dom } = appDialogsManager . addDialogNew ( {
section . sortedList . add ( peerId ) ;
dialog : peerId ,
container : ( isChannel ? this . groupsCategory : this.usersCategory ) . list ,
drawStatus : false ,
rippleEnabled : true ,
meAsSaved : true ,
avatarSize : 48 ,
lazyLoadQueue : this.lazyLoadQueue
} ) ;
dom . lastMessageSpan . append ( this . parseDistance ( peer . distance ) ) ;
dom . containerEl . onclick = ( ) = > appImManager . setPeer ( peerId ) ;
if ( isChannel ) {
let participantsCount = 0 ;
// @ts-ignore
for ( let chat of response . chats ) {
if ( chat . id == peer . peer . channel_id ) {
participantsCount = chat . participants_count ;
break ;
}
}
dom . lastMessageSpan . append ( ', ' , i18n ( 'Members' , [ participantsCount ] ) ) ;
}
} ) ;
} ) ;
this . usersCategory . nameEl . textContent = '' ;
this . errorCategory . classList . toggle ( 'hide' , ! ! ( usersCounter || groupsCounter ) ) ;
this . usersCategory . nameEl . append ( i18n ( 'PeopleNearbyHeader' ) ) ;
usersCounter && this . usersCategory . setActive ( ) ;
this . groupsCategory . nameEl . textContent = '' ;
this . groupsCategory . nameEl . append ( i18n ( 'ChatsNearbyHeader' ) ) ;
groupsCounter && this . groupsCategory . setActive ( ) ;
this . errorCategory . classList . toggle ( 'hide' , ( usersCounter || groupsCounter ) ) ;
this . errorCategory . innerHTML = "No groups or channels found around you." ;
this . errorCategory . innerHTML = "No groups or channels found around you." ;
} ) ;
} ) ;
} , ( error ) = > {
} , ( error ) = > {
this . errorCategory . classList . remove ( 'hide' ) ;
this . errorCategory . classList . remove ( 'hide' ) ;
this . retryBtn . classList . add ( 'is-visible' ) ;
this . retryBtn . classList . add ( 'is-visible' ) ;
this . retryBtn . addEventListener ( 'click' , this . open ) ;
this . retryBtn . addEventListener ( 'click' , this . open ) ;
if ( error instanceof GeolocationPositionError ) {
if ( error instanceof GeolocationPositionError ) {
this . errorCategory . innerHTML = "Location permission denied. Click below to retry." ;
this . errorCategory . innerHTML = "Location permission denied. Click below to retry." ;
} else {
} else {
this . errorCategory . innerHTML = "An error has occurred. Please retry later clicking the button below." ;
this . errorCategory . innerHTML = "An error has occurred. Please retry later clicking the button below." ;
}
}
} ) ;
} ) ;
} ) ;
} ) ;
return result ;
}
}
private startWatching ( ) {
private startWatching() {
if ( ! this . latestLocationSaved || this . isLocationWatched ) return ;
if ( ! this . latestLocationSaved || this . isLocationWatched ) return ;
this . isLocationWatched = true ;
this . isLocationWatched = true ;
@ -227,15 +244,15 @@ export default class AppPeopleNearby extends SliderSuperTab {
0x7fffffff // self_expires parameter
0x7fffffff // self_expires parameter
) ;
) ;
navigator . geolocation . watchPosition (
navigator . geolocation . watchPosition ( ( result ) = > {
( result ) = > {
const isLongitudeDifferent = result . coords . longitude !== this . latestLocationSaved . longitude ;
const isLongitudeDifferent = result . coords . longitude != this . latestLocationSaved . longitude ;
const isLatitudeDifferent = result . coords . latitude !== this . latestLocationSaved . latitude ;
const isLatitudeDifferent = result . coords . latitude != this . latestLocationSaved . latitude ;
const distanceCheck = this . calculateDistance (
const distanceCheck = this . calculateDistance (
result . coords . latitude , result . coords . longitude ,
result . coords . latitude , result . coords . longitude ,
this . latestLocationSaved . latitude , this . latestLocationSaved . longitude
this . latestLocationSaved . latitude , this . latestLocationSaved . longitude
) > 100 ;
) > 100 ;
if ( ( isLatitudeDifferent || isLongitudeDifferent ) && distanceCheck ) {
if ( ( isLatitudeDifferent || isLongitudeDifferent ) && distanceCheck ) {
appUsersManager . getLocated (
appUsersManager . getLocated (
result . coords . latitude ,
result . coords . latitude ,
result . coords . longitude ,
result . coords . longitude ,
@ -249,11 +266,10 @@ export default class AppPeopleNearby extends SliderSuperTab {
accuracy : result.coords.accuracy
accuracy : result.coords.accuracy
}
}
}
}
}
} ) ;
) ;
}
}
private stopWatching ( ) {
private stopWatching() {
if ( ! this . isLocationWatched ) return ;
if ( ! this . isLocationWatched ) return ;
this . isLocationWatched = false ;
this . isLocationWatched = false ;
toast ( 'The sharing of your position has been stopped. You will no longer be visible to other users.' ) ;
toast ( 'The sharing of your position has been stopped. You will no longer be visible to other users.' ) ;
@ -266,12 +282,12 @@ export default class AppPeopleNearby extends SliderSuperTab {
) ;
) ;
}
}
private calculateDistance ( lat1 : number , long1 : number , lat2 : number , long2 : number ) {
private calculateDistance ( lat1 : number , long1 : number , lat2 : number , long2 : number ) {
const p = 0.017453292519943295 ; // Math.PI/180
const p = 0.017453292519943295 ; // Math.PI/180
return (
return (
12742 * Math . asin (
12742 * Math . asin (
Math . sqrt (
Math . sqrt (
( 0.5 - Math . cos ( ( lat2 - lat1 ) * p ) ) +
( 0.5 - Math . cos ( ( lat2 - lat1 ) * p ) ) +
(
(
Math . cos ( lat1 * p ) * Math . cos ( lat2 * p )
Math . cos ( lat1 * p ) * Math . cos ( lat2 * p )
* ( 1 - Math . cos ( ( long2 - long1 ) * p ) / 2 )
* ( 1 - Math . cos ( ( long2 - long1 ) * p ) / 2 )