Browse Source

Support for people nearby

master
Global Server 3 years ago
parent
commit
3e0fa8ec46
  1. 2
      src/components/peerProfile.ts
  2. 5
      src/components/sidebarLeft/index.ts
  3. 110
      src/components/sidebarLeft/tabs/PeopleNearby.ts
  4. 30
      src/components/sidebarLeft/tabs/generalSettings.ts
  5. 18
      src/components/sidebarLeft/tabs/newGroup.ts
  6. 18
      src/lang.ts
  7. 2
      src/lib/appManagers/appStateManager.ts
  8. 6
      src/scss/partials/_peopleNearby.scss

2
src/components/peerProfile.ts

@ -126,7 +126,7 @@ export default class PeerProfile {
this.location = new Row({ this.location = new Row({
title: ' ', title: ' ',
subtitleLangKey: 'AttachLocation', subtitleLangKey: 'ChatLocation',
icon: 'location' icon: 'location'
}); });

5
src/components/sidebarLeft/index.ts

@ -43,7 +43,6 @@ 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 AppUsersManager from "../../lib/appManagers/appUsersManager";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
@ -255,10 +254,6 @@ export class AppSidebarLeft extends SidebarSlider {
icon: 'newgroup', icon: 'newgroup',
text: 'NewGroup', text: 'NewGroup',
onClick: onNewGroupClick onClick: onNewGroupClick
},{
icon: 'newgroup',
text: 'NewGeoGroup',
onClick: onNewGeoGroupClick
}, { }, {
icon: 'newprivate', icon: 'newprivate',
text: 'NewPrivateChat', text: 'NewPrivateChat',

110
src/components/sidebarLeft/tabs/PeopleNearby.ts

@ -19,7 +19,12 @@ import PeerTitle from "../../peerTitle";
import lottieLoader from "../../../lib/rlottie/lottieLoader"; import lottieLoader from "../../../lib/rlottie/lottieLoader";
import PopupPeer from "../../popups/peer"; import PopupPeer from "../../popups/peer";
import AppNewGroupTab from "./newGroup"; import AppNewGroupTab from "./newGroup";
import { toast } from "../../toast";
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 rootScope from '../../../lib/rootScope';
export default class AppPeopleNearby extends SliderSuperTab { export default class AppPeopleNearby extends SliderSuperTab {
private usersCategory = new SearchGroup(true, 'contacts', true, 'people-nearby-users', false); private usersCategory = new SearchGroup(true, 'contacts', true, 'people-nearby-users', false);
@ -28,25 +33,38 @@ export default class AppPeopleNearby extends SliderSuperTab {
private isLocationWatched: boolean = false; private isLocationWatched: boolean = false;
private errorCategory: HTMLElement; private errorCategory: HTMLElement;
private retryBtn: HTMLButtonElement; private retryBtn: HTMLButtonElement;
private btnOptions: HTMLButtonElement;
private menuButtons: (ButtonMenuItemOptions & {verify?: () => boolean})[];
protected lazyLoadQueue: LazyLoadQueueIntersector; protected lazyLoadQueue: LazyLoadQueueIntersector;
protected init() { protected init() {
this.container.classList.add('peoplenearby-container'); this.container.classList.add('peoplenearby-container');
this.setTitle('PeopleNearby'); this.setTitle('PeopleNearby');
const btnMenu = ButtonMenuToggle({}, 'bottom-left', [{ this.menuButtons = [{
icon: 'tip', icon: 'tip',
text: 'PeopleNearby.VisibilityYes', text: 'MakeMyselfVisible',
onClick: this.startWatching onClick: () => this.startWatching(),
verify: () => !this.isLocationWatched
}, },
{ {
icon: 'tip', icon: 'tip',
text: 'PeopleNearby.VisibilityNo', text: 'StopShowingMe',
onClick: this.startWatching onClick: () => this.stopWatching(),
}]); verify: () => this.isLocationWatched
},
{
icon: 'newgroup',
text: 'NearbyCreateGroup',
onClick: () => {
new AppNewGroupTab(this.slider).open([], true);
}
}];
this.btnOptions = ButtonMenuToggle({}, 'bottom-left', this.menuButtons, () => this.verifyButtons());
this.header.append(btnMenu); this.header.append(this.btnOptions);
const locatingIcon = document.createElement('span'); const locatingIcon = document.createElement('span');
locatingIcon.classList.add('tgico', 'tgico-location'); locatingIcon.classList.add('tgico', 'tgico-location');
@ -65,7 +83,10 @@ export default class AppPeopleNearby extends SliderSuperTab {
this.errorCategory.classList.add('text', 'hide', 'nearby-error'); this.errorCategory.classList.add('text', 'hide', 'nearby-error');
this.retryBtn = ButtonCorner({icon: 'check'}); this.retryBtn = ButtonCorner({icon: 'check'});
//this.retryBtn.classList.remove('is-visible');
const textContainer = document.createElement('div');
textContainer.classList.add('text', 'nearby-description');
textContainer.appendChild(i18n('PeopleNearbyInfo2'));
const chatsContainer = document.createElement('div'); const chatsContainer = document.createElement('div');
chatsContainer.classList.add('chatlist-container'); chatsContainer.classList.add('chatlist-container');
@ -73,24 +94,58 @@ export default class AppPeopleNearby extends SliderSuperTab {
chatsContainer.append(this.groupsCategory.container); chatsContainer.append(this.groupsCategory.container);
this.content.append(this.retryBtn); this.content.append(this.retryBtn);
this.scrollable.append(locatingAnimation, this.errorCategory, chatsContainer); this.scrollable.append(
locatingAnimation,
textContainer,
this.errorCategory,
chatsContainer
);
}
public onCloseAfterTimeout() {
this.usersCategory.clear();
this.groupsCategory.clear();
}
private verifyButtons(e?: Event){
const isMenuOpen = !!e || !!(this.btnOptions && this.btnOptions.classList.contains('menu-open'));
e && cancelEvent(e);
this.menuButtons.filter(button => button.verify).forEach(button => {
button.element.classList.toggle('hide', !button.verify());
});
} }
private parseDistance(distance: number){ private parseDistance(distance: number){
return (distance >= 1000 ? String(distance/1000)+' km' : String(distance)+' m'); if(rootScope.settings.distanceUnit == 'miles'){
if(distance > 1609.34) {
return i18n('MilesAway', [Math.round(distance / 1609)]);
}else{
return i18n('FootsAway', [Math.round(distance * 3.281)]);
}
}else{
if(distance >= 1000){
return i18n('KMetersAway2', [distance / 1000]);
}else{
return i18n('MetersAway2', [distance]);
}
}
} }
// @ts-ignore
public open() { public open() {
const result = super.open(); const result = super.open();
result.then(() => { result.then(() => {
this.retryBtn.classList.remove('is-visible'); this.retryBtn.classList.remove('is-visible');
navigator.geolocation.getCurrentPosition(location => { navigator.geolocation.getCurrentPosition((location) => {
this.latestLocationSaved = { this.latestLocationSaved = {
latitude: location.coords.latitude, latitude: location.coords.latitude,
longitude: location.coords.longitude, longitude: location.coords.longitude,
accuracy: location.coords.accuracy accuracy: location.coords.accuracy
}; };
console.log(this.latestLocationSaved);
appUsersManager.getLocated( appUsersManager.getLocated(
location.coords.latitude, location.coords.latitude,
location.coords.longitude, location.coords.longitude,
@ -130,16 +185,16 @@ export default class AppPeopleNearby extends SliderSuperTab {
break; break;
} }
} }
dom.lastMessageSpan.append(', '+String(participantsCount)+' members'); dom.lastMessageSpan.append(', '+i18n('Members', [participantsCount]));
} }
}); });
this.usersCategory.nameEl.textContent = ''; this.usersCategory.nameEl.textContent = '';
this.usersCategory.nameEl.append('Users'); this.usersCategory.nameEl.append(i18n('PeopleNearbyHeader'));
usersCounter && this.usersCategory.setActive(); usersCounter && this.usersCategory.setActive();
this.groupsCategory.nameEl.textContent = ''; this.groupsCategory.nameEl.textContent = '';
this.groupsCategory.nameEl.append('Groups'); this.groupsCategory.nameEl.append(i18n('ChatsNearbyHeader'));
groupsCounter && this.groupsCategory.setActive(); groupsCounter && this.groupsCategory.setActive();
this.errorCategory.classList.toggle('hide', (usersCounter || groupsCounter)); this.errorCategory.classList.toggle('hide', (usersCounter || groupsCounter));
@ -148,7 +203,7 @@ export default class AppPeopleNearby extends SliderSuperTab {
}, (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.opeddn); 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{
@ -162,12 +217,14 @@ export default class AppPeopleNearby extends SliderSuperTab {
if(!this.latestLocationSaved || this.isLocationWatched) return; if(!this.latestLocationSaved || this.isLocationWatched) return;
this.isLocationWatched = true; this.isLocationWatched = true;
toast('Your position is now being shared. Do not close the page or it will be suspended.');
appUsersManager.getLocated( appUsersManager.getLocated(
this.latestLocationSaved.latitude, this.latestLocationSaved.latitude,
this.latestLocationSaved.longitude, this.latestLocationSaved.longitude,
this.latestLocationSaved.accuracy, this.latestLocationSaved.accuracy,
true, // background parameter true, // background parameter
3600 // self_expires parameter 0x7fffffff // self_expires parameter
); );
navigator.geolocation.watchPosition( navigator.geolocation.watchPosition(
@ -177,14 +234,14 @@ export default class AppPeopleNearby extends SliderSuperTab {
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; );
if((isLatitudeDifferent || isLongitudeDifferent) && distanceCheck){ if((isLatitudeDifferent || isLongitudeDifferent) && distanceCheck){
appUsersManager.getLocated( appUsersManager.getLocated(
result.coords.latitude, result.coords.latitude,
result.coords.longitude, result.coords.longitude,
result.coords.accuracy, result.coords.accuracy,
true, // background parameter true, // background parameter
3600 // self_expires parameter 0x7fffffff // self_expires parameter
); );
this.latestLocationSaved = { this.latestLocationSaved = {
latitude: result.coords.latitude, latitude: result.coords.latitude,
@ -193,7 +250,20 @@ export default class AppPeopleNearby extends SliderSuperTab {
} }
} }
} }
) );
}
private stopWatching(){
if(!this.isLocationWatched) return;
this.isLocationWatched = false;
toast('The sharing of your position has been stopped. You will no longer be visible to other users.');
appUsersManager.getLocated(
0, // latitude parameter
0, // longitude parameter
0, // accuracy parameter
false, // background parameter
0 // self_expires parameter
);
} }
private calculateDistance(lat1: number, long1: number, lat2: number, long2: number){ private calculateDistance(lat1: number, long1: number, lat2: number, long2: number){

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

@ -134,6 +134,36 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
container.append(form); container.append(form);
} }
{
const container = section('DistanceUnitsTitle');
const form = document.createElement('form');
const name = 'distance-unit';
const stateKey = 'settings.distanceUnit';
const kilometersRow = new Row({
radioField: new RadioField({
langKey: 'DistanceUnitsKilometers',
name,
value: 'kilometers',
stateKey
})
});
const milesRow = new Row({
radioField: new RadioField({
langKey: 'DistanceUnitsMiles',
name,
value: 'miles',
stateKey
})
});
form.append(kilometersRow.container, milesRow.container);
container.append(form);
}
{ {
const container = section('General.TimeFormat'); const container = section('General.TimeFormat');

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

@ -13,7 +13,7 @@ import { SearchGroup } from "../../appSearch";
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 } from "../../../lib/langPack"; import I18n, { i18n } from "../../../lib/langPack";
import ButtonCorner from "../../buttonCorner"; import ButtonCorner from "../../buttonCorner";
interface OpenStreetMapInterface { interface OpenStreetMapInterface {
@ -57,7 +57,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
}); });
this.groupLocationInputField = new InputField({ this.groupLocationInputField = new InputField({
label: 'ChannelGeoGroupLocation', label: 'ChatLocation',
name: 'location', name: 'location',
canBeEdited: false canBeEdited: false
}); });
@ -134,9 +134,9 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.peerIds = peerIds; this.peerIds = peerIds;
if(isGeoChat){ if(isGeoChat){
this.setTitle('NewGeoGroup'); this.setTitle('NearbyCreateGroup');
this.groupLocationInputField.container.classList.remove('hide'); this.groupLocationInputField.container.classList.remove('hide');
this.groupLocationInputField.setValueSilently('Locating...'); this.groupLocationInputField.setValueSilently(I18n.format('Loading', true));
this.startLocating(); this.startLocating();
}else{ }else{
this.groupLocationInputField.container.classList.add('hide'); this.groupLocationInputField.container.classList.add('hide');
@ -156,14 +156,14 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.searchGroup.nameEl.textContent = ''; this.searchGroup.nameEl.textContent = '';
this.searchGroup.nameEl.append(i18n('Members', [this.peerIds.length])); this.searchGroup.nameEl.append(i18n('Members', [this.peerIds.length]));
this.searchGroup.setActive(); this.peerIds.length && this.searchGroup.setActive();
}); });
return result; return result;
} }
private startLocating(){ private startLocating(){
navigator.geolocation.getCurrentPosition(location => { navigator.geolocation.getCurrentPosition((location) => {
this.userLocationCoords = { this.userLocationCoords = {
lat: location.coords.latitude, lat: location.coords.latitude,
long: location.coords.longitude long: location.coords.longitude
@ -184,6 +184,12 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.groupLocationInputField.setValueSilently(response.display_name); this.groupLocationInputField.setValueSilently(response.display_name);
} }
); );
}, (error) => {
if(error instanceof GeolocationPositionError){
this.groupLocationInputField.setValueSilently('Location permission denied. Please retry later.');
}else{
this.groupLocationInputField.setValueSilently('An error has occurred. Please retry later.');
}
}); });
} }
} }

18
src/lang.ts

@ -1,8 +1,21 @@
const lang = { const lang = {
"DistanceUnitsTitle":"Distance units",
"DistanceUnitsKilometers":"Kilometers",
"DistanceUnitsMiles":"Miles",
"PeopleNearby":"PeopleNearby", "PeopleNearby":"PeopleNearby",
"PeopleNearby.VisibilityYes":"Make me visible", "MakeMyselfVisible":"Make myself visible",
"PeopleNearby.VisibilityNo":"Disable visibility", "StopShowingMe":"Stop showing me",
"PeopleNearbyInfo2":"Exchange contact info with people nearby and find new friends.",
"NearbyCreateGroup":"Create a Local group",
"GrantPermissions":"Grant me permissions", "GrantPermissions":"Grant me permissions",
"AwayTo":"%1$s away",
"KMetersAway2":"%1$s km away",
"MetersAway2":"%1$s m away",
"MilesAway":"%1$s mi away",
"FootsAway":"%1$s ft away",
"PeopleNearbyHeader":"People nearby",
"ChatsNearbyHeader":"Groups nearby",
"ChatLocation":"Location",
"Animations": "Animations", "Animations": "Animations",
"AttachAlbum": "Album", "AttachAlbum": "Album",
"Appearance.Color.Hex": "HEX", "Appearance.Color.Hex": "HEX",
@ -235,7 +248,6 @@ const lang = {
"UsernameHelpLink": "This link opens a chat with you:\n%1$s", "UsernameHelpLink": "This link opens a chat with you:\n%1$s",
"NewChannel": "New Channel", "NewChannel": "New Channel",
"NewGroup": "New Group", "NewGroup": "New Group",
"NewGeoGroup": "New Geo-Group",
"Contacts": "Contacts", "Contacts": "Contacts",
"SavedMessages": "Saved Messages", "SavedMessages": "Saved Messages",
"Settings": "Settings", "Settings": "Settings",

2
src/lib/appManagers/appStateManager.ts

@ -70,6 +70,7 @@ export type State = {
hiddenPinnedMessages: {[peerId: PeerId]: number}, hiddenPinnedMessages: {[peerId: PeerId]: number},
settings: { settings: {
messagesTextSize: number, messagesTextSize: number,
distanceUnit: 'kilometers' | 'miles',
sendShortcut: 'enter' | 'ctrlEnter', sendShortcut: 'enter' | 'ctrlEnter',
animationsEnabled: boolean, animationsEnabled: boolean,
autoDownload: { autoDownload: {
@ -123,6 +124,7 @@ export const STATE_INIT: State = {
hiddenPinnedMessages: {}, hiddenPinnedMessages: {},
settings: { settings: {
messagesTextSize: 16, messagesTextSize: 16,
distanceUnit: 'kilometers',
sendShortcut: 'enter', sendShortcut: 'enter',
animationsEnabled: true, animationsEnabled: true,
autoDownload: { autoDownload: {

6
src/scss/partials/_peopleNearby.scss

@ -1,3 +1,9 @@
div.text.nearby-description {
margin-top: 15px;
text-align: center;
color: var(--primary-text-color);
}
div.text.nearby-error { div.text.nearby-error {
color: var(--gc-secondary-text-color); color: var(--gc-secondary-text-color);
margin-top: 10px; margin-top: 10px;

Loading…
Cancel
Save