From 38ed31469569ffb4b273b306f6ef73bbfd26ddab Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sun, 16 Jan 2022 06:45:41 +0400 Subject: [PATCH] some changes --- src/components/button.ts | 6 +- src/components/sidebarLeft/index.ts | 54 ++- .../sidebarLeft/tabs/generalSettings.ts | 3 +- src/components/sidebarLeft/tabs/newChannel.ts | 7 +- src/components/sidebarLeft/tabs/newGroup.ts | 41 ++- .../sidebarLeft/tabs/peopleNearby.ts | 328 +++++++++--------- src/components/sortedUserList.ts | 12 +- src/components/wrappers.ts | 6 +- src/environment/geolocationSupport.ts | 3 + src/lang.ts | 38 +- src/lib/appManagers/appChatsManager.ts | 37 +- src/lib/appManagers/appProfileManager.ts | 8 +- src/lib/appManagers/appUsersManager.ts | 25 +- src/lib/mtproto/tl_utils.ts | 2 +- src/lib/rlottie/rlottiePlayer.ts | 4 +- src/scss/partials/_chat.scss | 2 +- src/scss/partials/_leftSidebar.scss | 14 +- 17 files changed, 316 insertions(+), 274 deletions(-) create mode 100644 src/environment/geolocationSupport.ts diff --git a/src/components/button.ts b/src/components/button.ts index 39279449..75ba4eed 100644 --- a/src/components/button.ts +++ b/src/components/button.ts @@ -7,7 +7,7 @@ import { i18n, LangPackKey } from "../lib/langPack"; import { ripple } from "./ripple"; -const Button = (className: string, options: Partial<{ +export type ButtonOptions = Partial<{ noRipple: true, onlyMobile: true, icon: string, @@ -15,7 +15,9 @@ const Button = (className: string, options: Partial<{ text: LangPackKey, disabled: boolean, asDiv: boolean -}> = {}) => { +}>; + +const Button = (className: string, options: ButtonOptions = {}) => { const button: HTMLButtonElement = document.createElement(options.asDiv ? 'div' : 'button') as any; button.className = className + (options.icon ? ' tgico-' + options.icon : ''); diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index e5a97be6..b9f987e6 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -26,7 +26,7 @@ import AppContactsTab from "./tabs/contacts"; import AppArchivedTab from "./tabs/archivedTab"; import AppAddMembersTab from "./tabs/addMembers"; import { FormatterArguments, i18n_, LangPackKey } from "../../lib/langPack"; -import AppPeopleNearby from "./tabs/peopleNearby"; +import AppPeopleNearbyTab from "./tabs/peopleNearby"; import { ButtonMenuItemOptions } from "../buttonMenu"; import CheckboxField from "../checkboxField"; import { IS_MOBILE_SAFARI } from "../../environment/userAgent"; @@ -43,6 +43,9 @@ import { closeBtnMenu } from "../misc"; import { indexOfAndSplice } from "../../helpers/array"; import ButtonIcon from "../buttonIcon"; import confirmationPopup from "../confirmationPopup"; +import IS_GEOLOCATION_SUPPORTED from "../../environment/geolocationSupport"; +import type SortedUserList from "../sortedUserList"; +import Button, { ButtonOptions } from "../button"; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; @@ -132,13 +135,13 @@ export class AppSidebarLeft extends SidebarSlider { icon: 'user', text: 'Contacts', onClick: onContactsClick - }, { + }, IS_GEOLOCATION_SUPPORTED ? { icon: 'group', text: 'PeopleNearby', onClick: () => { - new AppPeopleNearby(this).open(); + new AppPeopleNearbyTab(this).open(); } - }, { + } : undefined, { icon: 'settings', text: 'Settings', onClick: () => { @@ -206,8 +209,10 @@ export class AppSidebarLeft extends SidebarSlider { verify: () => App.isMainDomain }]; - this.toolsBtn = ButtonMenuToggle({}, 'bottom-right', menuButtons, (e) => { - menuButtons.forEach(button => { + const filteredButtons = menuButtons.filter(Boolean); + + this.toolsBtn = ButtonMenuToggle({}, 'bottom-right', filteredButtons, (e) => { + filteredButtons.forEach(button => { if(button.verify) { button.element.classList.toggle('hide', !button.verify()); } @@ -614,6 +619,15 @@ export class AppSidebarLeft extends SidebarSlider { } } +export type SettingSectionOptions = { + name?: LangPackKey, + nameArgs?: FormatterArguments, + caption?: LangPackKey | true, + noDelimiter?: boolean, + fakeGradientDelimiter?: boolean, + noShadow?: boolean +}; + const className = 'sidebar-left-section'; export class SettingSection { public container: HTMLElement; @@ -622,14 +636,7 @@ export class SettingSection { public title: HTMLElement; public caption: HTMLElement; - constructor(options: { - name?: LangPackKey, - nameArgs?: FormatterArguments, - caption?: LangPackKey | true, - noDelimiter?: boolean, - fakeGradientDelimiter?: boolean, - noShadow?: boolean - } = {}) { + constructor(options: SettingSectionOptions = {}) { const container = this.container = document.createElement('div'); container.classList.add(className + '-container'); @@ -692,6 +699,25 @@ export const generateDelimiter = () => { return delimiter; }; +export class SettingChatListSection extends SettingSection { + public sortedList: SortedUserList; + + constructor(options: SettingSectionOptions & {sortedList: SortedUserList}) { + super(options); + + this.sortedList = options.sortedList; + + this.content.append(this.sortedList.list); + } + + public makeButton(options: ButtonOptions) { + const button = Button('folder-category-button btn btn-primary btn-transparent', options); + if(this.title) this.content.insertBefore(button, this.title.nextSibling); + else this.content.prepend(button); + return button; + } +} + const appSidebarLeft = new AppSidebarLeft(); MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft; export default appSidebarLeft; diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index 97dd657e..44abd378 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -25,6 +25,7 @@ import LazyLoadQueue from "../../lazyLoadQueue"; import PopupStickers from "../../popups/stickers"; import eachMinute from "../../../helpers/eachMinute"; import { SliderSuperTabEventable } from "../../sliderTab"; +import IS_GEOLOCATION_SUPPORTED from "../../../environment/geolocationSupport"; export class RangeSettingSelector { public container: HTMLDivElement; @@ -135,7 +136,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { container.append(form); } - { + if(IS_GEOLOCATION_SUPPORTED) { const container = section('DistanceUnitsTitle'); const form = document.createElement('form'); diff --git a/src/components/sidebarLeft/tabs/newChannel.ts b/src/components/sidebarLeft/tabs/newChannel.ts index 6bdedca4..830896af 100644 --- a/src/components/sidebarLeft/tabs/newChannel.ts +++ b/src/components/sidebarLeft/tabs/newChannel.ts @@ -7,7 +7,6 @@ import appSidebarLeft, { SettingSection } from ".."; import { InputFile } from "../../../layer"; import appChatsManager from "../../../lib/appManagers/appChatsManager"; -import Button from "../../button"; import InputField from "../../inputField"; import { SliderSuperTab } from "../../slider"; import AvatarEdit from "../../avatarEdit"; @@ -66,7 +65,11 @@ export default class AppNewChannelTab extends SliderSuperTab { const about = this.channelDescriptionInputField.value; this.nextBtn.disabled = true; - appChatsManager.createChannel(title, about).then((channelId) => { + appChatsManager.createChannel({ + title, + about, + broadcast: true + }).then((channelId) => { if(this.uploadAvatar) { this.uploadAvatar().then((inputFile) => { appChatsManager.editPhoto(channelId, inputFile); diff --git a/src/components/sidebarLeft/tabs/newGroup.ts b/src/components/sidebarLeft/tabs/newGroup.ts index dd92be92..54345c90 100644 --- a/src/components/sidebarLeft/tabs/newGroup.ts +++ b/src/components/sidebarLeft/tabs/newGroup.ts @@ -12,7 +12,7 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager"; import InputField from "../../inputField"; import { SliderSuperTab } from "../../slider"; import AvatarEdit from "../../avatarEdit"; -import I18n, { i18n } from "../../../lib/langPack"; +import I18n from "../../../lib/langPack"; import ButtonCorner from "../../buttonCorner"; interface OpenStreetMapInterface { @@ -36,7 +36,7 @@ export default class AppNewGroupTab extends SliderSuperTab { private groupNameInputField: InputField; private list: HTMLUListElement; private groupLocationInputField: InputField; - private userLocationCoords: { lat: number, long: number }; + private userLocationCoords: {lat: number, long: number}; private userLocationAddress: string; protected init() { @@ -82,7 +82,16 @@ export default class AppNewGroupTab extends SliderSuperTab { if(this.isGeoChat){ if(!this.userLocationAddress || !this.userLocationCoords) return; - appChatsManager.createGeoChat(title, '', this.userLocationCoords, this.userLocationAddress).then((chatId) => { + appChatsManager.createChannel({ + title, + about: '', + geo_point: { + _: 'inputGeoPoint', + ...this.userLocationCoords, + }, + address: this.userLocationAddress, + megagroup: true + }).then((chatId) => { if(this.uploadAvatar) { this.uploadAvatar().then((inputFile) => { appChatsManager.editPhoto(chatId, inputFile); @@ -96,7 +105,7 @@ export default class AppNewGroupTab extends SliderSuperTab { appSidebarLeft.removeTabFromHistory(this); appSidebarLeft.selectTab(0); }); - }else{ + } else { this.nextBtn.disabled = true; appChatsManager.createChat(title, this.peerIds.map(peerId => peerId.toUserId())).then((chatId) => { if(this.uploadAvatar) { @@ -141,18 +150,17 @@ export default class AppNewGroupTab extends SliderSuperTab { this.peerIds = peerIds; const result = super.open(); result.then(() => { - - if(isGeoChat){ + if(isGeoChat) { this.setTitle('NearbyCreateGroup'); this.groupLocationInputField.container.classList.remove('hide'); this.groupLocationInputField.setValueSilently(I18n.format('Loading', true)); this.startLocating(); - }else{ + } else { this.groupLocationInputField.container.classList.add('hide'); } this.peerIds.forEach(userId => { - let {dom} = appDialogsManager.addDialogNew({ + const {dom} = appDialogsManager.addDialogNew({ dialog: userId, container: this.list, drawStatus: false, @@ -181,18 +189,15 @@ export default class AppNewGroupTab extends SliderSuperTab { uri += "&addressdetails=1"; uri += "&accept-language=en"; fetch(uri) - // @ts-ignore - .then((response) => response.json() as OpenStreetMapInterface) - .then( - (response: OpenStreetMapInterface) => { - this.userLocationAddress = response.display_name; - this.groupLocationInputField.setValueSilently(response.display_name); - } - ); + .then((response) => response.json()) + .then((response: OpenStreetMapInterface) => { + this.userLocationAddress = response.display_name; + this.groupLocationInputField.setValueSilently(response.display_name); + }); }, (error) => { - if(error instanceof GeolocationPositionError){ + if(error instanceof GeolocationPositionError) { this.groupLocationInputField.setValueSilently('Location permission denied. Please retry later.'); - }else{ + } else { this.groupLocationInputField.setValueSilently('An error has occurred. Please retry later.'); } }); diff --git a/src/components/sidebarLeft/tabs/peopleNearby.ts b/src/components/sidebarLeft/tabs/peopleNearby.ts index 80537648..7bd330fd 100644 --- a/src/components/sidebarLeft/tabs/peopleNearby.ts +++ b/src/components/sidebarLeft/tabs/peopleNearby.ts @@ -5,31 +5,27 @@ */ import { SliderSuperTab } from "../../slider"; -import AvatarElement from "../../avatar"; import ButtonCorner from "../../buttonCorner"; -import { InputUser } from "../../../layer"; -import apiManager from "../../../lib/mtproto/mtprotoworker"; 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 { 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 { i18n, join, _i18n } from "../../../lib/langPack"; 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 { - private usersCategory = new SearchGroup(true, 'contacts', true, 'people-nearby-users', false); - private groupsCategory = new SearchGroup(true, 'contacts', true, 'people-nearby-groups', false); - private latestLocationSaved: { latitude: number, longitude: number, accuracy: number }; +export default class AppPeopleNearbyTab extends SliderSuperTab { + private latestLocationSaved: {latitude: number, longitude: number, accuracy: number}; private isLocationWatched: boolean = false; private errorCategory: HTMLElement; private retryBtn: HTMLButtonElement; @@ -37,102 +33,155 @@ export default class AppPeopleNearby extends SliderSuperTab { private menuButtons: (ButtonMenuItemOptions & {verify?: () => boolean})[]; protected lazyLoadQueue: LazyLoadQueueIntersector; + protected peopleSection: SettingChatListSection; + protected chatsSection: SettingChatListSection; + + protected locatedPeers: Map; protected init() { - this.container.classList.add('peoplenearby-container'); + this.container.classList.add('people-nearby-container'); this.setTitle('PeopleNearby'); - this.menuButtons = [{ - icon: 'tip', - text: 'MakeMyselfVisible', - onClick: () => this.startWatching(), - verify: () => !this.isLocationWatched - }, - { - icon: 'tip', - text: 'StopShowingMe', - onClick: () => this.stopWatching(), - verify: () => this.isLocationWatched - }, - { - icon: 'newgroup', - text: 'NearbyCreateGroup', - onClick: () => { - new AppNewGroupTab(this.slider).open([], true); - } - }]; + this.errorCategory = document.createElement('div'); + this.errorCategory.classList.add('text', 'hide', 'nearby-error'); + + this.retryBtn = ButtonCorner({icon: 'check'}); + + 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, + onUpdate: (element) => { + const peer = this.locatedPeers.get(element.id); + const elements: HTMLElement[] = [ + 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) => { + const peer = this.locatedPeers.get(element.id); + return 0x7FFFFFFF - peer.distance; + } + }); + + appDialogsManager.setListClickListener(sortedUserList.list, undefined, undefined, false); + + return sortedUserList; + }; - this.btnOptions = ButtonMenuToggle({}, 'bottom-left', this.menuButtons, () => this.verifyButtons()); + const peopleSection = this.peopleSection = new SettingChatListSection({ + name: 'PeopleNearbyHeader', + sortedList: m() + }); - this.header.append(this.btnOptions); + const chatsSection = this.chatsSection = new SettingChatListSection({ + name: 'ChatsNearbyHeader', + sortedList: m() + }); - const locatingIcon = document.createElement('span'); - locatingIcon.classList.add('tgico', 'tgico-location'); + const btnMakeVisible = peopleSection.makeButton({ + text: 'MakeMyselfVisible', + icon: 'location' + }); - const locatingAnimation = document.createElement('div'); - locatingAnimation.classList.add('locating-animation-container'); - locatingAnimation.appendChild(locatingIcon); + const btnMakeInvisible = peopleSection.makeButton({ + text: 'StopShowingMe', + icon: 'location' + }); - for(let i=1; i<=4; i++){ - let animatingWaves = document.createElement('div'); - animatingWaves.classList.add('locating-animation-waves', 'wave-'+i); - locatingAnimation.appendChild(animatingWaves); - } + const btnCreateGroup = chatsSection.makeButton({ + text: 'NearbyCreateGroup', + icon: 'newgroup' + }); - this.errorCategory = document.createElement('div'); - this.errorCategory.classList.add('text', 'hide', 'nearby-error'); + attachClickEvent(btnMakeVisible, () => { + confirmationPopup({ + titleLangKey: 'MakeMyselfVisibleTitle', + descriptionLangKey: 'MakeMyselfVisibleInfo', + button: { + langKey: 'OK' + } + }).then(() => { + this.startWatching(); + }); + }, {listenerSetter: this.listenerSetter}); - this.retryBtn = ButtonCorner({icon: 'check'}); + attachClickEvent(btnMakeInvisible, () => { + this.stopWatching(); + }, {listenerSetter: this.listenerSetter}); - const textContainer = document.createElement('div'); - textContainer.classList.add('text', 'nearby-description'); - textContainer.appendChild(i18n('PeopleNearbyInfo2')); + attachClickEvent(btnCreateGroup, () => { + new AppNewGroupTab(this.slider).open([], true); + }, {listenerSetter: this.listenerSetter}); - const chatsContainer = document.createElement('div'); - chatsContainer.classList.add('chatlist-container'); - chatsContainer.append(this.usersCategory.container); - chatsContainer.append(this.groupsCategory.container); + btnMakeVisible.classList.add('primary'); + btnMakeInvisible.classList.add('danger'); + btnCreateGroup.classList.add('primary'); this.content.append(this.retryBtn); this.scrollable.append( - locatingAnimation, - textContainer, - this.errorCategory, - chatsContainer + stickerContainer, + caption, + peopleSection.container, + chatsSection.container, + this.errorCategory ); } - 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){ - if(rootScope.settings.distanceUnit == 'miles'){ + private parseDistance(distance: number) { + if(rootScope.settings.distanceUnit === 'miles') { if(distance > 1609.34) { return i18n('MilesAway', [Math.round(distance / 1609)]); - }else{ + } else { return i18n('FootsAway', [Math.round(distance * 3.281)]); } - }else{ - if(distance >= 1000){ + } else { + if(distance >= 1000) { return i18n('KMetersAway2', [distance / 1000]); - }else{ + } else { return i18n('MetersAway2', [distance]); } } } - // @ts-ignore public open() { const result = super.open(); result.then(() => { @@ -151,69 +200,37 @@ export default class AppPeopleNearby extends SliderSuperTab { location.coords.longitude, location.coords.accuracy ).then((response) => { - - // @ts-ignore - const orderedPeers = response?.updates[0]?.peers.sort((a, b) => a.distance-b.distance); - // @ts-ignore - const groupsCounter = response?.updates[0]?.peers.filter((e) => e.peer._ == 'peerChannel').length; - // @ts-ignore - const usersCounter = response?.updates[0]?.peers.filter((e) => e.peer._ != 'peerChannel').length; - // @ts-ignore + const update = (response as Updates.updates).updates[0] as Update.updatePeerLocated; + const peers = update.peers as PeerLocated.peerLocated[]; + const orderedPeers = peers.sort((a, b) => a.distance - b.distance); + const groupsCounter = peers.filter((e) => e.peer._ == 'peerChannel').length; + const usersCounter = peers.filter((e) => e.peer._ != 'peerChannel').length; orderedPeers?.forEach(peer => { - const isChannel = peer.peer._ == 'peerChannel'; - const peerId = (isChannel ? -peer.peer.channel_id : peer.peer.user_id); - - let {dialog, dom} = appDialogsManager.addDialogNew({ - 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])); - } + const peerId = appPeersManager.getPeerId(peer.peer); + const section = peerId.isUser() ? this.peopleSection : this.chatsSection; + this.locatedPeers.set(peerId, peer); + section.sortedList.add(peerId); }); - this.usersCategory.nameEl.textContent = ''; - 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.classList.toggle('hide', !!(usersCounter || groupsCounter)); this.errorCategory.innerHTML = "No groups or channels found around you."; }); }, (error) => { this.errorCategory.classList.remove('hide'); this.retryBtn.classList.add('is-visible'); this.retryBtn.addEventListener('click', this.open); - if(error instanceof GeolocationPositionError){ + if(error instanceof GeolocationPositionError) { 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."; } }); }); + + return result; } - private startWatching(){ + private startWatching() { if(!this.latestLocationSaved || this.isLocationWatched) return; this.isLocationWatched = true; @@ -227,33 +244,32 @@ export default class AppPeopleNearby extends SliderSuperTab { 0x7fffffff // self_expires parameter ); - navigator.geolocation.watchPosition( - (result) => { - const isLongitudeDifferent = result.coords.longitude != this.latestLocationSaved.longitude; - const isLatitudeDifferent = result.coords.latitude != this.latestLocationSaved.latitude; - 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 - 0x7fffffff // self_expires parameter - ); - this.latestLocationSaved = { - latitude: result.coords.latitude, - longitude: result.coords.longitude, - accuracy: result.coords.accuracy - } + navigator.geolocation.watchPosition((result) => { + const isLongitudeDifferent = result.coords.longitude !== this.latestLocationSaved.longitude; + const isLatitudeDifferent = result.coords.latitude !== this.latestLocationSaved.latitude; + 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 + 0x7fffffff // self_expires parameter + ); + this.latestLocationSaved = { + latitude: result.coords.latitude, + longitude: result.coords.longitude, + accuracy: result.coords.accuracy } } - ); + }); } - private stopWatching(){ + 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.'); @@ -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 return ( 12742 * Math.asin( Math.sqrt( - (0.5 - Math.cos((lat2-lat1) * p)) + + (0.5 - Math.cos((lat2 - lat1) * p)) + ( Math.cos(lat1 * p) * Math.cos(lat2 * p) * (1 - Math.cos((long2 - long1) * p)/2) diff --git a/src/components/sortedUserList.ts b/src/components/sortedUserList.ts index fd1316fb..024b7f87 100644 --- a/src/components/sortedUserList.ts +++ b/src/components/sortedUserList.ts @@ -29,6 +29,8 @@ export default class SortedUserList extends SortedList { protected autonomous = true; protected createChatListOptions: Parameters[0]; protected onListLengthChange: () => void; + protected getIndex: (element: SortedUser) => number; + protected onUpdate: (element: SortedUser) => void; constructor(options: Partial<{ lazyLoadQueue: SortedUserList['lazyLoadQueue'], @@ -36,18 +38,20 @@ export default class SortedUserList extends SortedList { rippleEnabled: SortedUserList['rippleEnabled'], createChatListOptions: SortedUserList['createChatListOptions'], autonomous: SortedUserList['autonomous'], - onListLengthChange: SortedUserList['onListLengthChange'] + onListLengthChange: SortedUserList['onListLengthChange'], + getIndex: SortedUserList['getIndex'], + onUpdate: SortedUserList['onUpdate'] }> = {}) { super({ - getIndex: (element) => appUsersManager.getUserStatusForSort(element.id), + getIndex: options.getIndex || ((element) => appUsersManager.getUserStatusForSort(element.id)), onDelete: (element) => { element.dom.listEl.remove(); this.onListLengthChange && this.onListLengthChange(); }, - onUpdate: (element) => { + onUpdate: options.onUpdate || ((element) => { const status = appUsersManager.getUserStatusString(element.id); replaceContent(element.dom.lastMessageSpan, status); - }, + }), onSort: (element, idx) => { const willChangeLength = element.dom.listEl.parentElement !== this.list; positionElementByIndex(element.dom.listEl, this.list, idx); diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index f542f4b3..a92e13ca 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -1111,7 +1111,7 @@ export function renderImageWithFadeIn(container: HTMLElement, // }); // } -export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn}: { +export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn, needUpscale}: { doc: MyDocument, div: HTMLElement, middleware?: () => boolean, @@ -1126,6 +1126,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o loop?: boolean, loadPromises?: Promise[], needFadeIn?: boolean, + needUpscale?: boolean }) { const stickerType = doc.sticker; @@ -1281,7 +1282,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o animationData: json, width, height, - name: 'doc' + doc.id + name: 'doc' + doc.id, + needUpscale }, group, toneIndex); //const deferred = deferredPromise(); diff --git a/src/environment/geolocationSupport.ts b/src/environment/geolocationSupport.ts new file mode 100644 index 00000000..510c3c8a --- /dev/null +++ b/src/environment/geolocationSupport.ts @@ -0,0 +1,3 @@ +const IS_GEOLOCATION_SUPPORTED = !!navigator?.geolocation?.getCurrentPosition && false; + +export default IS_GEOLOCATION_SUPPORTED; diff --git a/src/lang.ts b/src/lang.ts index 159f1017..a9a95e33 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -1,22 +1,4 @@ const lang = { - "DistanceUnitsTitle":"Distance units", - "DistanceUnitsKilometers":"Kilometers", - "DistanceUnitsMiles":"Miles", - "PeopleNearby":"PeopleNearby", - "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", - "MessagePreview":"Message Preview", - "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", @@ -417,7 +399,6 @@ const lang = { "MegaPrivateInfo": "Private groups can only be joined if you were invited or have an invite link.", "ChannelPrivateLinkHelp": "People can join your channel by following this link. You can revoke the link any time.", "MegaPrivateLinkHelp": "People can join your group by following this link. You can revoke the link any time.", - "ChannelGeoGroupLocation":"Group location", "RevokeButton": "Revoke", "RevokeLink": "Revoke Link", "RevokeAlert": "Are you sure you want to revoke this link? Once the link is revoked, no one will be able to join using it.", @@ -634,6 +615,25 @@ const lang = { "BotRestart": "Restart bot", "ShareYouPhoneNumberTitle": "Share your phone number?", "AreYouSureShareMyContactInfoBot": "The bot will know your phone number. This can be useful for integration with other services.", + "DistanceUnitsTitle": "Distance units", + "DistanceUnitsKilometers": "Kilometers", + "DistanceUnitsMiles": "Miles", + "PeopleNearby": "People Nearby", + "MakeMyselfVisible": "Make Myself Visible", + "MakeMyselfVisibleTitle": "Show Your Profile?", + "MakeMyselfVisibleInfo": "Users nearby will be able to view your profile and send you messages. This may help you find new friends, but could also attract excessive attention. You can stop sharing your profile at any time.\n\nYour phone number will remain hidden.", + "StopShowingMe": "Stop Showing Me", + "PeopleNearbyInfo2": "Exchange contact info with people nearby and find new friends.", + "NearbyCreateGroup": "Create a Local group", + "AwayTo": "%1$s away", + "MessagePreview": "Message Preview", + "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", // * macos "AccountSettings.Filters": "Chat Folders", diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 2a189d49..2120bf5e 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -9,9 +9,9 @@ * https://github.com/zhukov/webogram/blob/master/LICENSE */ -import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug"; +import { MOUNT_CLASS_TO } from "../../config/debug"; import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object"; -import { InputGeoPoint, ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates } from "../../layer"; +import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates, ChannelsCreateChannel } from "../../layer"; import apiManagerProxy from "../mtproto/mtprotoworker"; import apiManager from '../mtproto/mtprotoworker'; import { RichTextProcessor } from "../richtextprocessor"; @@ -487,37 +487,8 @@ export class AppChatsManager { return participants; } */ - public createChannel(title: string, about: string): Promise { - return apiManager.invokeApi('channels.createChannel', { - broadcast: true, - title, - about - }).then((updates) => { - apiUpdatesManager.processUpdateMessage(updates); - - const channelId = (updates as any).chats[0].id; - rootScope.dispatchEvent('history_focus', {peerId: channelId.toPeerId(true)}); - - return channelId; - }); - } - - public createGeoChat(title: string, about: string, gpoint: { - lat: number, - long: number - }, address: string): Promise { - let geo_point = { - _: 'inputGeoPoint', - lat: gpoint['lat'], - long: gpoint['long'] - } as InputGeoPoint; - return apiManager.invokeApi('channels.createChannel', { - megagroup: true, - title, - about, - geo_point, - address - }).then((updates) => { + public createChannel(options: ChannelsCreateChannel): Promise { + return apiManager.invokeApi('channels.createChannel', options).then((updates) => { apiUpdatesManager.processUpdateMessage(updates); const channelId = (updates as any).chats[0].id; diff --git a/src/lib/appManagers/appProfileManager.ts b/src/lib/appManagers/appProfileManager.ts index 97aca664..333afd33 100644 --- a/src/lib/appManagers/appProfileManager.ts +++ b/src/lib/appManagers/appProfileManager.ts @@ -539,13 +539,13 @@ export class AppProfileManager { }); } - public getChatMembersString(id: ChatId) { - const chat: Chat = appChatsManager.getChat(id); + public getChatMembersString(chatId: ChatId) { + const chat: Chat = appChatsManager.getChat(chatId); if(chat._ === 'chatForbidden') { return i18n('YouWereKicked'); } - const chatFull = this.chatsFull[id]; + const chatFull = this.chatsFull[chatId]; let count: number; if(chatFull) { if(chatFull._ === 'channelFull') { @@ -557,7 +557,7 @@ export class AppProfileManager { count = (chat as Chat.chat).participants_count || (chat as any).participants?.participants.length; } - const isChannel = appChatsManager.isBroadcast(id); + const isChannel = appChatsManager.isBroadcast(chatId); count = count || 1; let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member'; diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index 1500e14e..d179cd49 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -863,28 +863,25 @@ export class AppUsersManager { } public getLocated( - lat: number, long: number, + lat: number, + long: number, accuracy_radius: number, background: boolean = false, self_expires: number = 0 ) { - const _globalThis = this; - const geo_point = { + const geo_point: InputGeoPoint = { _: 'inputGeoPoint', lat, long, accuracy_radius - } as InputGeoPoint; - - return apiManager.invokeApi( - 'contacts.getLocated', - {geo_point, background} - ).then((result) => { - // @ts-ignore - appUsersManager.saveApiUsers(result.users); - // @ts-ignore - appChatsManager.saveApiChats(result.chats); - return result; + }; + + return apiManager.invokeApi('contacts.getLocated', { + geo_point, + background + }).then((updates) => { + apiUpdatesManager.processUpdateMessage(updates); + return updates; }); } diff --git a/src/lib/mtproto/tl_utils.ts b/src/lib/mtproto/tl_utils.ts index 3d948b5b..69bba4a1 100644 --- a/src/lib/mtproto/tl_utils.ts +++ b/src/lib/mtproto/tl_utils.ts @@ -829,4 +829,4 @@ class TLDeserialization { MOUNT_CLASS_TO.TLDeserialization = TLDeserialization; MOUNT_CLASS_TO.TLSerialization = TLSerialization; -export { TLDeserialization, TLSerialization }; \ No newline at end of file +export { TLDeserialization, TLSerialization }; diff --git a/src/lib/rlottie/rlottiePlayer.ts b/src/lib/rlottie/rlottiePlayer.ts index 043dad86..3c0ffa7b 100644 --- a/src/lib/rlottie/rlottiePlayer.ts +++ b/src/lib/rlottie/rlottiePlayer.ts @@ -20,8 +20,8 @@ export type RLottieOptions = { width?: number, height?: number, group?: string, - noCache?: true, - needUpscale?: true, + noCache?: boolean, + needUpscale?: boolean, skipRatio?: number, initFrame?: number, // index color?: RLottieColor, diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index ad448a37..de2b9108 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -1493,4 +1493,4 @@ $background-transition-total-time: #{$input-transition-time - $background-transi margin-bottom: 1rem; } } -} \ No newline at end of file +} diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index 3d24cf83..4059392d 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -608,7 +608,9 @@ } } -.chat-folders-container, .edit-folder-container { +.chat-folders-container, +.edit-folder-container, +.people-nearby-container { user-select: none; .sticker-container { @@ -629,6 +631,16 @@ } } +.people-nearby-container { + .sticker-container { + margin: 1rem auto; + } + + .caption { + margin-bottom: 1rem; + } +} + .chat-folders-container { .sidebar-left-section { &:not(:last-child) {