phone number editing controls are functional

This commit is contained in:
or-else
2023-01-22 18:57:19 -08:00
parent b766e827c9
commit b0bf93a4d9
13 changed files with 724 additions and 636 deletions
+46 -2
View File
@@ -185,6 +185,7 @@ input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="tel"],
textarea {
height: 2.5rem;
border-bottom: 1px solid #ccc;
@@ -195,6 +196,7 @@ textarea {
input[type="text"],
input[type="email"],
input[type="password"],
input[type="tel"],
textarea {
width: 100%;
}
@@ -203,7 +205,8 @@ textarea:focus,
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="search"]:focus {
input[type="search"]:focus,
input[type="tel"]:focus {
border-bottom: 1px solid #2196f3;
box-shadow: 0 1px 0 rgba(33, 150, 243, 0.25);
}
@@ -212,7 +215,8 @@ textarea.invalid,
input[type="text"].invalid,
input[type="email"].invalid,
input[type="password"].invalid,
input[type="search"].invalid {
input[type="search"].invalid,
input[type="tel"].invalid {
border-bottom: 1px solid #e57373;
box-shadow: 0 1px 0 rgba(229, 115, 115, 0.25);
}
@@ -221,6 +225,7 @@ input[type="text"]::placeholder,
input[type="email"]::placeholder,
input[type="password"]::placeholder,
input[type="search"]::placeholder,
input[type="tel"]::placeholder,
textarea::placeholder {
color: #bbb;
font-weight: lighter;
@@ -3019,6 +3024,45 @@ ul.tabbar li.active > a {
outline: 0.5rem solid rgba(255,255,255,.95);
}
.country-flag {
display: inline-block;
font-size: larger;
vertical-align: middle;
}
.dial-code {
white-space: nowrap;
display: inline-block;
cursor: pointer;
margin-top: 0.1rem;
}
ul.phone-country-selector {
list-style: none;
overflow-x: hidden;
}
ul.phone-country-selector > li {
position: relative;
font-weight: lighter;
padding: 0.25rem 0.5rem 0.15rem 0.5rem;
cursor: pointer;
}
ul.phone-country-selector > li:hover {
background-color: #eceff1; /* blue-gray 50 highlight */
}
ul.phone-country-selector > li.selected {
background-color: #cfd8dc; /* blue-gray 100 highlight */
}
ul.phone-country-selector .dial-code {
position: absolute;
right: 0.5rem;
color: #1e88e5;
font-weight: normal;
}
/*
Very wide monitor.
*/
+1 -1
View File
File diff suppressed because one or more lines are too long
+468 -462
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -65,6 +65,7 @@
"license": "Apache-2.0",
"dependencies": {
"firebase": "^9.14.0",
"libphonenumber-js": "^1.10.18",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-intl": "^6.2.1",
+9
View File
@@ -99,3 +99,12 @@ export function letterTileColorId(userId) {
export function idToColorClass(id, light, fg) {
return (light ? 'lt-' : 'dk-') + (fg ? 'fg-' : 'bg-') + letterTileColorId(id);
}
// Takes ISO Alpha-2 country code, like 'US' and converts it to a flag emoji.
export function flagEmoji(countryCode) {
const codePoints = countryCode
.toUpperCase()
.split('')
.map(char => 0x1F1A5 + char.charCodeAt());
return String.fromCodePoint(...codePoints);
}
+1 -1
View File
@@ -30,7 +30,7 @@ export default class SearchContacts extends React.PureComponent {
handleSearch(e) {
e.preventDefault();
var query = this.state.search.trim();
const query = this.state.search.trim();
this.setState({edited: (query.length > 0)});
this.props.onSearchContacts(query.length > 0 ? query : Tinode.DEL_CHAR);
}
-4
View File
@@ -2,10 +2,6 @@
import React from 'react';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import InPlaceEdit from './in-place-edit.jsx';
import { NO_ACCESS_MODE } from '../config.js';
const messages = defineMessages({
clear_messages: {
id: 'action_clear_messages',
+192 -160
View File
@@ -38,6 +38,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ "getDefaultAppConfig": () => (/* binding */ getDefaultAppConfig),
/* harmony export */ "getDefaultEmulatorHost": () => (/* binding */ getDefaultEmulatorHost),
/* harmony export */ "getDefaultEmulatorHostnameAndPort": () => (/* binding */ getDefaultEmulatorHostnameAndPort),
/* harmony export */ "getDefaults": () => (/* binding */ getDefaults),
/* harmony export */ "getExperimentalSetting": () => (/* binding */ getExperimentalSetting),
/* harmony export */ "getGlobal": () => (/* binding */ getGlobal),
/* harmony export */ "getModularInstance": () => (/* binding */ getModularInstance),
@@ -526,7 +527,7 @@ function isValidKey(key) {
/**
* @license
* Copyright 2017 Google LLC
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -540,156 +541,10 @@ function isValidKey(key) {
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Returns navigator.userAgent string or '' if it's not defined.
* @return user agent string
*/
function getUA() {
if (typeof navigator !== 'undefined' &&
typeof navigator['userAgent'] === 'string') {
return navigator['userAgent'];
}
else {
return '';
}
}
/**
* Detect Cordova / PhoneGap / Ionic frameworks on a mobile device.
*
* Deliberately does not rely on checking `file://` URLs (as this fails PhoneGap
* in the Ripple emulator) nor Cordova `onDeviceReady`, which would normally
* wait for a callback.
*/
function isMobileCordova() {
return (typeof window !== 'undefined' &&
// @ts-ignore Setting up an broadly applicable index signature for Window
// just to deal with this case would probably be a bad idea.
!!(window['cordova'] || window['phonegap'] || window['PhoneGap']) &&
/ios|iphone|ipod|ipad|android|blackberry|iemobile/i.test(getUA()));
}
/**
* Detect Node.js.
*
* @return true if Node.js environment is detected.
*/
// Node detection logic from: https://github.com/iliakan/detect-node/
function isNode() {
try {
return (Object.prototype.toString.call(__webpack_require__.g.process) === '[object process]');
}
catch (e) {
return false;
}
}
/**
* Detect Browser Environment
*/
function isBrowser() {
return typeof self === 'object' && self.self === self;
}
function isBrowserExtension() {
const runtime = typeof chrome === 'object'
? chrome.runtime
: typeof browser === 'object'
? browser.runtime
: undefined;
return typeof runtime === 'object' && runtime.id !== undefined;
}
/**
* Detect React Native.
*
* @return true if ReactNative environment is detected.
*/
function isReactNative() {
return (typeof navigator === 'object' && navigator['product'] === 'ReactNative');
}
/** Detects Electron apps. */
function isElectron() {
return getUA().indexOf('Electron/') >= 0;
}
/** Detects Internet Explorer. */
function isIE() {
const ua = getUA();
return ua.indexOf('MSIE ') >= 0 || ua.indexOf('Trident/') >= 0;
}
/** Detects Universal Windows Platform apps. */
function isUWP() {
return getUA().indexOf('MSAppHost/') >= 0;
}
/**
* Detect whether the current SDK build is the Node version.
*
* @return true if it's the Node SDK build.
*/
function isNodeSdk() {
return CONSTANTS.NODE_CLIENT === true || CONSTANTS.NODE_ADMIN === true;
}
/** Returns true if we are running in Safari. */
function isSafari() {
return (!isNode() &&
navigator.userAgent.includes('Safari') &&
!navigator.userAgent.includes('Chrome'));
}
/**
* This method checks if indexedDB is supported by current browser/service worker context
* @return true if indexedDB is supported by current browser/service worker context
*/
function isIndexedDBAvailable() {
try {
return typeof indexedDB === 'object';
}
catch (e) {
return false;
}
}
/**
* This method validates browser/sw context for indexedDB by opening a dummy indexedDB database and reject
* if errors occur during the database open operation.
*
* @throws exception if current browser/sw context can't run idb.open (ex: Safari iframe, Firefox
* private browsing)
*/
function validateIndexedDBOpenable() {
return new Promise((resolve, reject) => {
try {
let preExist = true;
const DB_CHECK_NAME = 'validate-browser-context-for-indexeddb-analytics-module';
const request = self.indexedDB.open(DB_CHECK_NAME);
request.onsuccess = () => {
request.result.close();
// delete database only when it doesn't pre-exist
if (!preExist) {
self.indexedDB.deleteDatabase(DB_CHECK_NAME);
}
resolve(true);
};
request.onupgradeneeded = () => {
preExist = false;
};
request.onerror = () => {
var _a;
reject(((_a = request.error) === null || _a === void 0 ? void 0 : _a.message) || '');
};
}
catch (error) {
reject(error);
}
});
}
/**
*
* This method checks whether cookie is enabled within current browser
* @return true if cookie is enabled within current browser
*/
function areCookiesEnabled() {
if (typeof navigator === 'undefined' || !navigator.cookieEnabled) {
return false;
}
return true;
}
/**
* Polyfill for `globalThis` object.
* @returns the `globalThis` object for the given environment.
* @public
*/
function getGlobal() {
if (typeof self !== 'undefined') {
@@ -723,8 +578,11 @@ function getGlobal() {
const getDefaultsFromGlobal = () => getGlobal().__FIREBASE_DEFAULTS__;
/**
* Attempt to read defaults from a JSON string provided to
* process.env.__FIREBASE_DEFAULTS__ or a JSON file whose path is in
* process.env.__FIREBASE_DEFAULTS_PATH__
* process(.)env(.)__FIREBASE_DEFAULTS__ or a JSON file whose path is in
* process(.)env(.)__FIREBASE_DEFAULTS_PATH__
* The dots are in parens because certain compilers (Vite?) cannot
* handle seeing that variable in comments.
* See https://github.com/firebase/firebase-js-sdk/issues/6838
*/
const getDefaultsFromEnvVariable = () => {
if (typeof process === 'undefined' || typeof process.env === 'undefined') {
@@ -756,6 +614,7 @@ const getDefaultsFromCookie = () => {
* (1) if such an object exists as a property of `globalThis`
* (2) if such an object was provided on a shell environment variable
* (3) if such an object exists in a cookie
* @public
*/
const getDefaults = () => {
try {
@@ -919,6 +778,178 @@ function createMockUserToken(token, projectId) {
].join('.');
}
/**
* @license
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Returns navigator.userAgent string or '' if it's not defined.
* @return user agent string
*/
function getUA() {
if (typeof navigator !== 'undefined' &&
typeof navigator['userAgent'] === 'string') {
return navigator['userAgent'];
}
else {
return '';
}
}
/**
* Detect Cordova / PhoneGap / Ionic frameworks on a mobile device.
*
* Deliberately does not rely on checking `file://` URLs (as this fails PhoneGap
* in the Ripple emulator) nor Cordova `onDeviceReady`, which would normally
* wait for a callback.
*/
function isMobileCordova() {
return (typeof window !== 'undefined' &&
// @ts-ignore Setting up an broadly applicable index signature for Window
// just to deal with this case would probably be a bad idea.
!!(window['cordova'] || window['phonegap'] || window['PhoneGap']) &&
/ios|iphone|ipod|ipad|android|blackberry|iemobile/i.test(getUA()));
}
/**
* Detect Node.js.
*
* @return true if Node.js environment is detected or specified.
*/
// Node detection logic from: https://github.com/iliakan/detect-node/
function isNode() {
var _a;
const forceEnvironment = (_a = getDefaults()) === null || _a === void 0 ? void 0 : _a.forceEnvironment;
if (forceEnvironment === 'node') {
return true;
}
else if (forceEnvironment === 'browser') {
return false;
}
try {
return (Object.prototype.toString.call(__webpack_require__.g.process) === '[object process]');
}
catch (e) {
return false;
}
}
/**
* Detect Browser Environment
*/
function isBrowser() {
return typeof self === 'object' && self.self === self;
}
function isBrowserExtension() {
const runtime = typeof chrome === 'object'
? chrome.runtime
: typeof browser === 'object'
? browser.runtime
: undefined;
return typeof runtime === 'object' && runtime.id !== undefined;
}
/**
* Detect React Native.
*
* @return true if ReactNative environment is detected.
*/
function isReactNative() {
return (typeof navigator === 'object' && navigator['product'] === 'ReactNative');
}
/** Detects Electron apps. */
function isElectron() {
return getUA().indexOf('Electron/') >= 0;
}
/** Detects Internet Explorer. */
function isIE() {
const ua = getUA();
return ua.indexOf('MSIE ') >= 0 || ua.indexOf('Trident/') >= 0;
}
/** Detects Universal Windows Platform apps. */
function isUWP() {
return getUA().indexOf('MSAppHost/') >= 0;
}
/**
* Detect whether the current SDK build is the Node version.
*
* @return true if it's the Node SDK build.
*/
function isNodeSdk() {
return CONSTANTS.NODE_CLIENT === true || CONSTANTS.NODE_ADMIN === true;
}
/** Returns true if we are running in Safari. */
function isSafari() {
return (!isNode() &&
navigator.userAgent.includes('Safari') &&
!navigator.userAgent.includes('Chrome'));
}
/**
* This method checks if indexedDB is supported by current browser/service worker context
* @return true if indexedDB is supported by current browser/service worker context
*/
function isIndexedDBAvailable() {
try {
return typeof indexedDB === 'object';
}
catch (e) {
return false;
}
}
/**
* This method validates browser/sw context for indexedDB by opening a dummy indexedDB database and reject
* if errors occur during the database open operation.
*
* @throws exception if current browser/sw context can't run idb.open (ex: Safari iframe, Firefox
* private browsing)
*/
function validateIndexedDBOpenable() {
return new Promise((resolve, reject) => {
try {
let preExist = true;
const DB_CHECK_NAME = 'validate-browser-context-for-indexeddb-analytics-module';
const request = self.indexedDB.open(DB_CHECK_NAME);
request.onsuccess = () => {
request.result.close();
// delete database only when it doesn't pre-exist
if (!preExist) {
self.indexedDB.deleteDatabase(DB_CHECK_NAME);
}
resolve(true);
};
request.onupgradeneeded = () => {
preExist = false;
};
request.onerror = () => {
var _a;
reject(((_a = request.error) === null || _a === void 0 ? void 0 : _a.message) || '');
};
}
catch (error) {
reject(error);
}
});
}
/**
*
* This method checks whether cookie is enabled within current browser
* @return true if cookie is enabled within current browser
*/
function areCookiesEnabled() {
if (typeof navigator === 'undefined' || !navigator.cookieEnabled) {
return false;
}
return true;
}
/**
* @license
* Copyright 2017 Google LLC
@@ -3251,6 +3282,7 @@ class HashNavigation {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "bytesToHumanSize": () => (/* binding */ bytesToHumanSize),
/* harmony export */ "flagEmoji": () => (/* binding */ flagEmoji),
/* harmony export */ "idToColorClass": () => (/* binding */ idToColorClass),
/* harmony export */ "letterTileColorId": () => (/* binding */ letterTileColorId),
/* harmony export */ "relativeDateFormat": () => (/* binding */ relativeDateFormat),
@@ -3343,6 +3375,10 @@ function letterTileColorId(userId) {
function idToColorClass(id, light, fg) {
return (light ? 'lt-' : 'dk-') + (fg ? 'fg-' : 'bg-') + letterTileColorId(id);
}
function flagEmoji(countryCode) {
const codePoints = countryCode.toUpperCase().split('').map(char => 0x1F1A5 + char.charCodeAt());
return String.fromCodePoint(...codePoints);
}
/***/ }),
@@ -15127,7 +15163,7 @@ class SearchContacts extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
}
handleSearch(e) {
e.preventDefault();
var query = this.state.search.trim();
const query = this.state.search.trim();
this.setState({
edited: query.length > 0
});
@@ -16339,10 +16375,6 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _in_place_edit_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./in-place-edit.jsx */ "./src/widgets/in-place-edit.jsx");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
@@ -17050,7 +17082,7 @@ function isVersionServiceProvider(provider) {
}
const name$o = "@firebase/app";
const version$1 = "0.9.0";
const version$1 = "0.9.1";
/**
* @license
@@ -17117,7 +17149,7 @@ const name$2 = "@firebase/firestore";
const name$1 = "@firebase/firestore-compat";
const name = "firebase";
const version = "9.15.0";
const version = "9.16.0";
/**
* @license
@@ -18377,7 +18409,7 @@ __webpack_require__.r(__webpack_exports__);
const name = "@firebase/installations";
const version = "0.6.0";
const version = "0.6.1";
/**
* @license
@@ -20782,7 +20814,7 @@ async function messageEventListener(messaging, event) {
}
const name = "@firebase/messaging";
const version = "0.12.0";
const version = "0.12.1";
/**
* @license
@@ -21054,7 +21086,7 @@ __webpack_require__.r(__webpack_exports__);
var name = "firebase";
var version = "9.15.0";
var version = "9.16.0";
/**
* @license
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long