Browse Source

some changes

master
Eduard Kuzmenko 3 years ago
parent
commit
38ed314695
  1. 6
      src/components/button.ts
  2. 54
      src/components/sidebarLeft/index.ts
  3. 3
      src/components/sidebarLeft/tabs/generalSettings.ts
  4. 7
      src/components/sidebarLeft/tabs/newChannel.ts
  5. 25
      src/components/sidebarLeft/tabs/newGroup.ts
  6. 266
      src/components/sidebarLeft/tabs/peopleNearby.ts
  7. 12
      src/components/sortedUserList.ts
  8. 6
      src/components/wrappers.ts
  9. 3
      src/environment/geolocationSupport.ts
  10. 38
      src/lang.ts
  11. 37
      src/lib/appManagers/appChatsManager.ts
  12. 8
      src/lib/appManagers/appProfileManager.ts
  13. 25
      src/lib/appManagers/appUsersManager.ts
  14. 4
      src/lib/rlottie/rlottiePlayer.ts
  15. 14
      src/scss/partials/_leftSidebar.scss

6
src/components/button.ts

@ -7,7 +7,7 @@
import { i18n, LangPackKey } from "../lib/langPack"; import { i18n, LangPackKey } from "../lib/langPack";
import { ripple } from "./ripple"; import { ripple } from "./ripple";
const Button = (className: string, options: Partial<{ export type ButtonOptions = Partial<{
noRipple: true, noRipple: true,
onlyMobile: true, onlyMobile: true,
icon: string, icon: string,
@ -15,7 +15,9 @@ const Button = (className: string, options: Partial<{
text: LangPackKey, text: LangPackKey,
disabled: boolean, disabled: boolean,
asDiv: boolean asDiv: boolean
}> = {}) => { }>;
const Button = (className: string, options: ButtonOptions = {}) => {
const button: HTMLButtonElement = document.createElement(options.asDiv ? 'div' : 'button') as any; const button: HTMLButtonElement = document.createElement(options.asDiv ? 'div' : 'button') as any;
button.className = className + (options.icon ? ' tgico-' + options.icon : ''); button.className = className + (options.icon ? ' tgico-' + options.icon : '');

54
src/components/sidebarLeft/index.ts

@ -26,7 +26,7 @@ import AppContactsTab from "./tabs/contacts";
import AppArchivedTab from "./tabs/archivedTab"; import AppArchivedTab from "./tabs/archivedTab";
import AppAddMembersTab from "./tabs/addMembers"; import AppAddMembersTab from "./tabs/addMembers";
import { FormatterArguments, i18n_, LangPackKey } from "../../lib/langPack"; import { FormatterArguments, i18n_, LangPackKey } from "../../lib/langPack";
import AppPeopleNearby from "./tabs/peopleNearby"; import AppPeopleNearbyTab from "./tabs/peopleNearby";
import { ButtonMenuItemOptions } from "../buttonMenu"; import { ButtonMenuItemOptions } from "../buttonMenu";
import CheckboxField from "../checkboxField"; import CheckboxField from "../checkboxField";
import { IS_MOBILE_SAFARI } from "../../environment/userAgent"; import { IS_MOBILE_SAFARI } from "../../environment/userAgent";
@ -43,6 +43,9 @@ import { closeBtnMenu } from "../misc";
import { indexOfAndSplice } from "../../helpers/array"; import { indexOfAndSplice } from "../../helpers/array";
import ButtonIcon from "../buttonIcon"; import ButtonIcon from "../buttonIcon";
import confirmationPopup from "../confirmationPopup"; 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'; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
@ -132,13 +135,13 @@ export class AppSidebarLeft extends SidebarSlider {
icon: 'user', icon: 'user',
text: 'Contacts', text: 'Contacts',
onClick: onContactsClick onClick: onContactsClick
}, { }, IS_GEOLOCATION_SUPPORTED ? {
icon: 'group', icon: 'group',
text: 'PeopleNearby', text: 'PeopleNearby',
onClick: () => { onClick: () => {
new AppPeopleNearby(this).open(); new AppPeopleNearbyTab(this).open();
} }
}, { } : undefined, {
icon: 'settings', icon: 'settings',
text: 'Settings', text: 'Settings',
onClick: () => { onClick: () => {
@ -206,8 +209,10 @@ export class AppSidebarLeft extends SidebarSlider {
verify: () => App.isMainDomain verify: () => App.isMainDomain
}]; }];
this.toolsBtn = ButtonMenuToggle({}, 'bottom-right', menuButtons, (e) => { const filteredButtons = menuButtons.filter(Boolean);
menuButtons.forEach(button => {
this.toolsBtn = ButtonMenuToggle({}, 'bottom-right', filteredButtons, (e) => {
filteredButtons.forEach(button => {
if(button.verify) { if(button.verify) {
button.element.classList.toggle('hide', !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'; const className = 'sidebar-left-section';
export class SettingSection { export class SettingSection {
public container: HTMLElement; public container: HTMLElement;
@ -622,14 +636,7 @@ export class SettingSection {
public title: HTMLElement; public title: HTMLElement;
public caption: HTMLElement; public caption: HTMLElement;
constructor(options: { constructor(options: SettingSectionOptions = {}) {
name?: LangPackKey,
nameArgs?: FormatterArguments,
caption?: LangPackKey | true,
noDelimiter?: boolean,
fakeGradientDelimiter?: boolean,
noShadow?: boolean
} = {}) {
const container = this.container = document.createElement('div'); const container = this.container = document.createElement('div');
container.classList.add(className + '-container'); container.classList.add(className + '-container');
@ -692,6 +699,25 @@ export const generateDelimiter = () => {
return delimiter; 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(); const appSidebarLeft = new AppSidebarLeft();
MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft; MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft;
export default appSidebarLeft; export default appSidebarLeft;

3
src/components/sidebarLeft/tabs/generalSettings.ts

@ -25,6 +25,7 @@ import LazyLoadQueue from "../../lazyLoadQueue";
import PopupStickers from "../../popups/stickers"; import PopupStickers from "../../popups/stickers";
import eachMinute from "../../../helpers/eachMinute"; import eachMinute from "../../../helpers/eachMinute";
import { SliderSuperTabEventable } from "../../sliderTab"; import { SliderSuperTabEventable } from "../../sliderTab";
import IS_GEOLOCATION_SUPPORTED from "../../../environment/geolocationSupport";
export class RangeSettingSelector { export class RangeSettingSelector {
public container: HTMLDivElement; public container: HTMLDivElement;
@ -135,7 +136,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
container.append(form); container.append(form);
} }
{ if(IS_GEOLOCATION_SUPPORTED) {
const container = section('DistanceUnitsTitle'); const container = section('DistanceUnitsTitle');
const form = document.createElement('form'); const form = document.createElement('form');

7
src/components/sidebarLeft/tabs/newChannel.ts

@ -7,7 +7,6 @@
import appSidebarLeft, { SettingSection } from ".."; import appSidebarLeft, { SettingSection } from "..";
import { InputFile } from "../../../layer"; import { InputFile } from "../../../layer";
import appChatsManager from "../../../lib/appManagers/appChatsManager"; import appChatsManager from "../../../lib/appManagers/appChatsManager";
import Button from "../../button";
import InputField from "../../inputField"; import InputField from "../../inputField";
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit"; import AvatarEdit from "../../avatarEdit";
@ -66,7 +65,11 @@ export default class AppNewChannelTab extends SliderSuperTab {
const about = this.channelDescriptionInputField.value; const about = this.channelDescriptionInputField.value;
this.nextBtn.disabled = true; this.nextBtn.disabled = true;
appChatsManager.createChannel(title, about).then((channelId) => { appChatsManager.createChannel({
title,
about,
broadcast: true
}).then((channelId) => {
if(this.uploadAvatar) { if(this.uploadAvatar) {
this.uploadAvatar().then((inputFile) => { this.uploadAvatar().then((inputFile) => {
appChatsManager.editPhoto(channelId, inputFile); appChatsManager.editPhoto(channelId, inputFile);

25
src/components/sidebarLeft/tabs/newGroup.ts

@ -12,7 +12,7 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager";
import InputField from "../../inputField"; import InputField from "../../inputField";
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit"; import AvatarEdit from "../../avatarEdit";
import I18n, { i18n } from "../../../lib/langPack"; import I18n from "../../../lib/langPack";
import ButtonCorner from "../../buttonCorner"; import ButtonCorner from "../../buttonCorner";
interface OpenStreetMapInterface { interface OpenStreetMapInterface {
@ -82,7 +82,16 @@ export default class AppNewGroupTab extends SliderSuperTab {
if(this.isGeoChat){ if(this.isGeoChat){
if(!this.userLocationAddress || !this.userLocationCoords) return; 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) { if(this.uploadAvatar) {
this.uploadAvatar().then((inputFile) => { this.uploadAvatar().then((inputFile) => {
appChatsManager.editPhoto(chatId, inputFile); appChatsManager.editPhoto(chatId, inputFile);
@ -141,7 +150,6 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.peerIds = peerIds; this.peerIds = peerIds;
const result = super.open(); const result = super.open();
result.then(() => { result.then(() => {
if(isGeoChat) { if(isGeoChat) {
this.setTitle('NearbyCreateGroup'); this.setTitle('NearbyCreateGroup');
this.groupLocationInputField.container.classList.remove('hide'); this.groupLocationInputField.container.classList.remove('hide');
@ -152,7 +160,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
} }
this.peerIds.forEach(userId => { this.peerIds.forEach(userId => {
let {dom} = appDialogsManager.addDialogNew({ const {dom} = appDialogsManager.addDialogNew({
dialog: userId, dialog: userId,
container: this.list, container: this.list,
drawStatus: false, drawStatus: false,
@ -181,14 +189,11 @@ export default class AppNewGroupTab extends SliderSuperTab {
uri += "&addressdetails=1"; uri += "&addressdetails=1";
uri += "&accept-language=en"; uri += "&accept-language=en";
fetch(uri) fetch(uri)
// @ts-ignore .then((response) => response.json())
.then((response) => response.json() as OpenStreetMapInterface) .then((response: OpenStreetMapInterface) => {
.then(
(response: OpenStreetMapInterface) => {
this.userLocationAddress = response.display_name; this.userLocationAddress = response.display_name;
this.groupLocationInputField.setValueSilently(response.display_name); this.groupLocationInputField.setValueSilently(response.display_name);
} });
);
}, (error) => { }, (error) => {
if(error instanceof GeolocationPositionError) { if(error instanceof GeolocationPositionError) {
this.groupLocationInputField.setValueSilently('Location permission denied. Please retry later.'); this.groupLocationInputField.setValueSilently('Location permission denied. Please retry later.');

266
src/components/sidebarLeft/tabs/peopleNearby.ts

@ -5,30 +5,26 @@
*/ */
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 groupsCategory = new SearchGroup(true, 'contacts', true, 'people-nearby-groups', false);
private latestLocationSaved: {latitude: number, longitude: number, accuracy: number}; private latestLocationSaved: {latitude: number, longitude: number, accuracy: number};
private isLocationWatched: boolean = false; private isLocationWatched: boolean = false;
private errorCategory: HTMLElement; private errorCategory: HTMLElement;
@ -37,87 +33,141 @@ 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() {
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) { private parseDistance(distance: number) {
if(rootScope.settings.distanceUnit == 'miles'){ 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 {
@ -132,7 +182,6 @@ export default class AppPeopleNearby extends SliderSuperTab {
} }
} }
// @ts-ignore
public open() { public open() {
const result = super.open(); const result = super.open();
result.then(() => { result.then(() => {
@ -151,53 +200,19 @@ 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) => {
@ -211,6 +226,8 @@ export default class AppPeopleNearby extends SliderSuperTab {
} }
}); });
}); });
return result;
} }
private startWatching() { private startWatching() {
@ -227,14 +244,14 @@ 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,
@ -249,8 +266,7 @@ export default class AppPeopleNearby extends SliderSuperTab {
accuracy: result.coords.accuracy accuracy: result.coords.accuracy
} }
} }
} });
);
} }
private stopWatching() { private stopWatching() {

12
src/components/sortedUserList.ts

@ -29,6 +29,8 @@ export default class SortedUserList extends SortedList<SortedUser> {
protected autonomous = true; protected autonomous = true;
protected createChatListOptions: Parameters<AppDialogsManager['createChatList']>[0]; protected createChatListOptions: Parameters<AppDialogsManager['createChatList']>[0];
protected onListLengthChange: () => void; protected onListLengthChange: () => void;
protected getIndex: (element: SortedUser) => number;
protected onUpdate: (element: SortedUser) => void;
constructor(options: Partial<{ constructor(options: Partial<{
lazyLoadQueue: SortedUserList['lazyLoadQueue'], lazyLoadQueue: SortedUserList['lazyLoadQueue'],
@ -36,18 +38,20 @@ export default class SortedUserList extends SortedList<SortedUser> {
rippleEnabled: SortedUserList['rippleEnabled'], rippleEnabled: SortedUserList['rippleEnabled'],
createChatListOptions: SortedUserList['createChatListOptions'], createChatListOptions: SortedUserList['createChatListOptions'],
autonomous: SortedUserList['autonomous'], autonomous: SortedUserList['autonomous'],
onListLengthChange: SortedUserList['onListLengthChange'] onListLengthChange: SortedUserList['onListLengthChange'],
getIndex: SortedUserList['getIndex'],
onUpdate: SortedUserList['onUpdate']
}> = {}) { }> = {}) {
super({ super({
getIndex: (element) => appUsersManager.getUserStatusForSort(element.id), getIndex: options.getIndex || ((element) => appUsersManager.getUserStatusForSort(element.id)),
onDelete: (element) => { onDelete: (element) => {
element.dom.listEl.remove(); element.dom.listEl.remove();
this.onListLengthChange && this.onListLengthChange(); this.onListLengthChange && this.onListLengthChange();
}, },
onUpdate: (element) => { onUpdate: options.onUpdate || ((element) => {
const status = appUsersManager.getUserStatusString(element.id); const status = appUsersManager.getUserStatusString(element.id);
replaceContent(element.dom.lastMessageSpan, status); replaceContent(element.dom.lastMessageSpan, status);
}, }),
onSort: (element, idx) => { onSort: (element, idx) => {
const willChangeLength = element.dom.listEl.parentElement !== this.list; const willChangeLength = element.dom.listEl.parentElement !== this.list;
positionElementByIndex(element.dom.listEl, this.list, idx); positionElementByIndex(element.dom.listEl, this.list, idx);

6
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, doc: MyDocument,
div: HTMLElement, div: HTMLElement,
middleware?: () => boolean, middleware?: () => boolean,
@ -1126,6 +1126,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
loop?: boolean, loop?: boolean,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
needFadeIn?: boolean, needFadeIn?: boolean,
needUpscale?: boolean
}) { }) {
const stickerType = doc.sticker; const stickerType = doc.sticker;
@ -1281,7 +1282,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
animationData: json, animationData: json,
width, width,
height, height,
name: 'doc' + doc.id name: 'doc' + doc.id,
needUpscale
}, group, toneIndex); }, group, toneIndex);
//const deferred = deferredPromise<void>(); //const deferred = deferredPromise<void>();

3
src/environment/geolocationSupport.ts

@ -0,0 +1,3 @@
const IS_GEOLOCATION_SUPPORTED = !!navigator?.geolocation?.getCurrentPosition && false;
export default IS_GEOLOCATION_SUPPORTED;

38
src/lang.ts

@ -1,22 +1,4 @@
const lang = { 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", "Animations": "Animations",
"AttachAlbum": "Album", "AttachAlbum": "Album",
"Appearance.Color.Hex": "HEX", "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.", "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.", "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.", "MegaPrivateLinkHelp": "People can join your group by following this link. You can revoke the link any time.",
"ChannelGeoGroupLocation":"Group location",
"RevokeButton": "Revoke", "RevokeButton": "Revoke",
"RevokeLink": "Revoke Link", "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.", "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", "BotRestart": "Restart bot",
"ShareYouPhoneNumberTitle": "Share your phone number?", "ShareYouPhoneNumberTitle": "Share your phone number?",
"AreYouSureShareMyContactInfoBot": "The bot will know your phone number. This can be useful for integration with other services.", "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 // * macos
"AccountSettings.Filters": "Chat Folders", "AccountSettings.Filters": "Chat Folders",

37
src/lib/appManagers/appChatsManager.ts

@ -9,9 +9,9 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE * 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 { 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 apiManagerProxy from "../mtproto/mtprotoworker";
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
@ -487,37 +487,8 @@ export class AppChatsManager {
return participants; return participants;
} */ } */
public createChannel(title: string, about: string): Promise<ChatId> { public createChannel(options: ChannelsCreateChannel): Promise<ChatId> {
return apiManager.invokeApi('channels.createChannel', { return apiManager.invokeApi('channels.createChannel', options).then((updates) => {
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<ChatId> {
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) => {
apiUpdatesManager.processUpdateMessage(updates); apiUpdatesManager.processUpdateMessage(updates);
const channelId = (updates as any).chats[0].id; const channelId = (updates as any).chats[0].id;

8
src/lib/appManagers/appProfileManager.ts

@ -539,13 +539,13 @@ export class AppProfileManager {
}); });
} }
public getChatMembersString(id: ChatId) { public getChatMembersString(chatId: ChatId) {
const chat: Chat = appChatsManager.getChat(id); const chat: Chat = appChatsManager.getChat(chatId);
if(chat._ === 'chatForbidden') { if(chat._ === 'chatForbidden') {
return i18n('YouWereKicked'); return i18n('YouWereKicked');
} }
const chatFull = this.chatsFull[id]; const chatFull = this.chatsFull[chatId];
let count: number; let count: number;
if(chatFull) { if(chatFull) {
if(chatFull._ === 'channelFull') { if(chatFull._ === 'channelFull') {
@ -557,7 +557,7 @@ export class AppProfileManager {
count = (chat as Chat.chat).participants_count || (chat as any).participants?.participants.length; 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; count = count || 1;
let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member'; let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member';

25
src/lib/appManagers/appUsersManager.ts

@ -863,28 +863,25 @@ export class AppUsersManager {
} }
public getLocated( public getLocated(
lat: number, long: number, lat: number,
long: number,
accuracy_radius: number, accuracy_radius: number,
background: boolean = false, background: boolean = false,
self_expires: number = 0 self_expires: number = 0
) { ) {
const _globalThis = this; const geo_point: InputGeoPoint = {
const geo_point = {
_: 'inputGeoPoint', _: 'inputGeoPoint',
lat, lat,
long, long,
accuracy_radius accuracy_radius
} as InputGeoPoint; };
return apiManager.invokeApi( return apiManager.invokeApi('contacts.getLocated', {
'contacts.getLocated', geo_point,
{geo_point, background} background
).then((result) => { }).then((updates) => {
// @ts-ignore apiUpdatesManager.processUpdateMessage(updates);
appUsersManager.saveApiUsers(result.users); return updates;
// @ts-ignore
appChatsManager.saveApiChats(result.chats);
return result;
}); });
} }

4
src/lib/rlottie/rlottiePlayer.ts

@ -20,8 +20,8 @@ export type RLottieOptions = {
width?: number, width?: number,
height?: number, height?: number,
group?: string, group?: string,
noCache?: true, noCache?: boolean,
needUpscale?: true, needUpscale?: boolean,
skipRatio?: number, skipRatio?: number,
initFrame?: number, // index initFrame?: number, // index
color?: RLottieColor, color?: RLottieColor,

14
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; user-select: none;
.sticker-container { .sticker-container {
@ -629,6 +631,16 @@
} }
} }
.people-nearby-container {
.sticker-container {
margin: 1rem auto;
}
.caption {
margin-bottom: 1rem;
}
}
.chat-folders-container { .chat-folders-container {
.sidebar-left-section { .sidebar-left-section {
&:not(:last-child) { &:not(:last-child) {

Loading…
Cancel
Save