[Dev] Fix eslint; Update dependencies (#1754)
This commit is contained in:
parent
0d4ab658fc
commit
07ffad4425
34
.eslintrc
34
.eslintrc
@ -2,6 +2,7 @@
|
|||||||
"extends": [
|
"extends": [
|
||||||
"react-app",
|
"react-app",
|
||||||
"plugin:teactn/recommended",
|
"plugin:teactn/recommended",
|
||||||
|
"airbnb",
|
||||||
"airbnb-typescript"
|
"airbnb-typescript"
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
@ -34,11 +35,31 @@
|
|||||||
"no-else-return": "off",
|
"no-else-return": "off",
|
||||||
"no-plusplus": "off",
|
"no-plusplus": "off",
|
||||||
"no-void": "off",
|
"no-void": "off",
|
||||||
|
"no-continue": "off",
|
||||||
"default-case": "off",
|
"default-case": "off",
|
||||||
"no-param-reassign": "off",
|
"no-param-reassign": "off",
|
||||||
"no-prototype-builtins": "off",
|
"no-prototype-builtins": "off",
|
||||||
"no-await-in-loop": "off",
|
"no-await-in-loop": "off",
|
||||||
"no-nested-ternary": "off",
|
"no-nested-ternary": "off",
|
||||||
|
"function-paren-newline": ["error", "consistent"],
|
||||||
|
"prefer-destructuring": "off",
|
||||||
|
// Allow for...of. Edited from:
|
||||||
|
// https://github.com/airbnb/javascript/blob/b4377fb03089dd7f08955242695860d47f9caab4/packages/eslint-config-airbnb-base/rules/style.js#L333
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "ForInStatement",
|
||||||
|
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "LabeledStatement",
|
||||||
|
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "WithStatement",
|
||||||
|
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
|
||||||
|
}
|
||||||
|
],
|
||||||
"import/no-extraneous-dependencies": "off",
|
"import/no-extraneous-dependencies": "off",
|
||||||
"import/prefer-default-export": "off",
|
"import/prefer-default-export": "off",
|
||||||
"import/named": "off",
|
"import/named": "off",
|
||||||
@ -47,13 +68,22 @@
|
|||||||
"react/jsx-one-expression-per-line": "off",
|
"react/jsx-one-expression-per-line": "off",
|
||||||
"react/button-has-type": "off",
|
"react/button-has-type": "off",
|
||||||
"react/require-default-props": "off",
|
"react/require-default-props": "off",
|
||||||
|
"react/function-component-definition": "off",
|
||||||
// Teact feature
|
// Teact feature
|
||||||
"react/style-prop-object": "off",
|
"react/style-prop-object": "off",
|
||||||
|
"react/jsx-no-bind": ["error", {
|
||||||
|
"ignoreRefs": true,
|
||||||
|
"allowArrowFunctions": true,
|
||||||
|
"allowFunctions": true,
|
||||||
|
"allowBind": false,
|
||||||
|
"ignoreDOMComponents": true
|
||||||
|
}],
|
||||||
"jsx-a11y/click-events-have-key-events": "off",
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
"jsx-a11y/no-static-element-interactions": "off",
|
"jsx-a11y/no-static-element-interactions": "off",
|
||||||
"jsx-a11y/label-has-associated-control": "off",
|
"jsx-a11y/label-has-associated-control": "off",
|
||||||
"jsx-a11y/anchor-is-valid": "off",
|
"jsx-a11y/anchor-is-valid": "off",
|
||||||
"jsx-a11y/no-noninteractive-element-to-interactive-role": "off",
|
"jsx-a11y/no-noninteractive-element-to-interactive-role": "off",
|
||||||
|
"jsx-a11y/media-has-caption": "off",
|
||||||
"no-async-without-await/no-async-without-await": 1,
|
"no-async-without-await/no-async-without-await": 1,
|
||||||
"@typescript-eslint/no-use-before-define": [
|
"@typescript-eslint/no-use-before-define": [
|
||||||
"error",
|
"error",
|
||||||
@ -64,6 +94,7 @@
|
|||||||
"@typescript-eslint/camelcase": "off",
|
"@typescript-eslint/camelcase": "off",
|
||||||
"@typescript-eslint/member-delimiter-style": "error",
|
"@typescript-eslint/member-delimiter-style": "error",
|
||||||
"@typescript-eslint/default-param-last": "off",
|
"@typescript-eslint/default-param-last": "off",
|
||||||
|
"@typescript-eslint/return-await": ["error", "in-try-catch"],
|
||||||
"teactn/prefer-separate-component-file": "off"
|
"teactn/prefer-separate-component-file": "off"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
@ -71,8 +102,5 @@
|
|||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"project": "./tsconfig.json"
|
"project": "./tsconfig.json"
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"APP_REVISION": "readonly"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3443
package-lock.json
generated
3443
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
44
package.json
44
package.json
@ -34,36 +34,34 @@
|
|||||||
"author": "Alexander Zinchuk (alexander@zinchuk.com)",
|
"author": "Alexander Zinchuk (alexander@zinchuk.com)",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.0",
|
"@babel/core": "^7.17.5",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||||
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
|
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"@babel/preset-react": "^7.16.7",
|
"@babel/preset-react": "^7.16.7",
|
||||||
"@babel/preset-typescript": "^7.16.7",
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
"@peculiar/webcrypto": "^1.2.3",
|
"@peculiar/webcrypto": "^1.3.2",
|
||||||
"@testing-library/jest-dom": "^5.16.2",
|
"@testing-library/jest-dom": "^5.16.2",
|
||||||
"@types/croppie": "^2.6.1",
|
"@types/croppie": "^2.6.1",
|
||||||
"@types/css-font-loading-module": "0.0.7",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/dom-mediacapture-record": "^1.0.11",
|
"@types/react": "^17.0.40",
|
||||||
"@types/jest": "^27.4.0",
|
"@types/react-dom": "^17.0.13",
|
||||||
"@types/react": "^17.0.39",
|
|
||||||
"@types/react-dom": "^17.0.11",
|
|
||||||
"@types/resize-observer-browser": "^0.1.7",
|
|
||||||
"@types/wicg-mediasession": "^1.1.3",
|
"@types/wicg-mediasession": "^1.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.11.0",
|
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||||
"@typescript-eslint/parser": "^5.11.0",
|
"@typescript-eslint/parser": "^5.14.0",
|
||||||
"@webpack-cli/serve": "^1.6.1",
|
"@webpack-cli/serve": "^1.6.1",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"babel-loader": "^8.2.3",
|
"babel-loader": "^8.2.3",
|
||||||
"browserlist": "^1.0.1",
|
"browserlist": "^1.0.1",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^6.6.0",
|
"css-loader": "^6.7.1",
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"eslint": "^8.8.0",
|
"eslint": "^8.10.0",
|
||||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
|
"eslint-config-airbnb-typescript": "^16.1.1",
|
||||||
"eslint-config-react-app": "^7.0.0",
|
"eslint-config-react-app": "^7.0.0",
|
||||||
"eslint-import-resolver-webpack": "^0.13.2",
|
"eslint-import-resolver-webpack": "^0.13.2",
|
||||||
"eslint-plugin-flowtype": "^8.0.3",
|
"eslint-plugin-flowtype": "^8.0.3",
|
||||||
@ -71,30 +69,30 @@
|
|||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-no-async-without-await": "^1.2.0",
|
"eslint-plugin-no-async-without-await": "^1.2.0",
|
||||||
"eslint-plugin-no-null": "^1.0.2",
|
"eslint-plugin-no-null": "^1.0.2",
|
||||||
"eslint-plugin-react": "^7.28.0",
|
"eslint-plugin-react": "^7.29.3",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"eslint-plugin-teactn": "git+https://github.com/korenskoy/eslint-plugin-teactn#c2c39dd005d58c07c24c4361de804dce1c6261b5",
|
"eslint-plugin-teactn": "git+https://github.com/korenskoy/eslint-plugin-teactn#c2c39dd005d58c07c24c4361de804dce1c6261b5",
|
||||||
"git-revision-webpack-plugin": "^5.0.0",
|
"git-revision-webpack-plugin": "^5.0.0",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"jest": "^27.5.0",
|
"jest": "^27.5.1",
|
||||||
"jest-raw-loader": "^1.0.1",
|
"jest-raw-loader": "^1.0.1",
|
||||||
"lint-staged": "^12.3.3",
|
"lint-staged": "^12.3.5",
|
||||||
"mini-css-extract-plugin": "^2.5.3",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"postcss-loader": "^6.2.1",
|
"postcss-loader": "^6.2.1",
|
||||||
"postcss-modules": "^4.3.0",
|
"postcss-modules": "^4.3.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"replace-in-file": "^6.3.2",
|
"replace-in-file": "^6.3.2",
|
||||||
"sass": "^1.49.7",
|
"sass": "^1.49.9",
|
||||||
"sass-loader": "^12.4.0",
|
"sass-loader": "^12.6.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"stylelint": "^14.3.0",
|
"stylelint": "^14.5.3",
|
||||||
"stylelint-config-recommended-scss": "^5.0.2",
|
"stylelint-config-recommended-scss": "^5.0.2",
|
||||||
"stylelint-declaration-block-no-ignored-properties": "^2.5.0",
|
"stylelint-declaration-block-no-ignored-properties": "^2.5.0",
|
||||||
"stylelint-group-selectors": "^1.0.8",
|
"stylelint-group-selectors": "^1.0.8",
|
||||||
"stylelint-high-performance-animation": "^1.6.0",
|
"stylelint-high-performance-animation": "^1.6.0",
|
||||||
"typescript": "^4.5.5",
|
"typescript": "^4.6.2",
|
||||||
"webpack": "^5.68.0",
|
"webpack": "^5.70.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
"webpack-cli": "^4.9.2",
|
"webpack-cli": "^4.9.2",
|
||||||
"webpack-dev-server": "^4.7.4",
|
"webpack-dev-server": "^4.7.4",
|
||||||
|
|||||||
2
src/@types/global.d.ts
vendored
2
src/@types/global.d.ts
vendored
@ -1,6 +1,6 @@
|
|||||||
declare const process: NodeJS.Process;
|
declare const process: NodeJS.Process;
|
||||||
|
|
||||||
declare var APP_REVISION: string;
|
declare const APP_REVISION: string;
|
||||||
|
|
||||||
declare namespace React {
|
declare namespace React {
|
||||||
interface HTMLAttributes {
|
interface HTMLAttributes {
|
||||||
|
|||||||
@ -598,7 +598,9 @@ function buildGeo(media: GramJs.MessageMediaGeo): ApiLocation | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildVenue(media: GramJs.MessageMediaVenue): ApiLocation | undefined {
|
function buildVenue(media: GramJs.MessageMediaVenue): ApiLocation | undefined {
|
||||||
const { geo, title, provider, address, venueId, venueType } = media;
|
const {
|
||||||
|
geo, title, provider, address, venueId, venueType,
|
||||||
|
} = media;
|
||||||
const point = buildGeoPoint(geo);
|
const point = buildGeoPoint(geo);
|
||||||
return point && {
|
return point && {
|
||||||
type: 'venue',
|
type: 'venue',
|
||||||
@ -624,7 +626,9 @@ function buildGeoLive(media: GramJs.MessageMediaGeoLive): ApiLocation | undefine
|
|||||||
|
|
||||||
function buildGeoPoint(geo: GramJs.TypeGeoPoint): ApiLocation['geo'] | undefined {
|
function buildGeoPoint(geo: GramJs.TypeGeoPoint): ApiLocation['geo'] | undefined {
|
||||||
if (geo instanceof GramJs.GeoPointEmpty) return undefined;
|
if (geo instanceof GramJs.GeoPointEmpty) return undefined;
|
||||||
const { long, lat, accuracyRadius, accessHash } = geo;
|
const {
|
||||||
|
long, lat, accuracyRadius, accessHash,
|
||||||
|
} = geo;
|
||||||
return {
|
return {
|
||||||
long,
|
long,
|
||||||
lat,
|
lat,
|
||||||
|
|||||||
@ -229,8 +229,19 @@ export function sendMessage(
|
|||||||
onProgress?: ApiOnProgress,
|
onProgress?: ApiOnProgress,
|
||||||
) {
|
) {
|
||||||
const localMessage = buildLocalMessage(
|
const localMessage = buildLocalMessage(
|
||||||
chat, text, entities, replyingTo, attachment, sticker, gif, poll, contact, groupedId, scheduledAt,
|
chat,
|
||||||
sendAs, serverTimeOffset,
|
text,
|
||||||
|
entities,
|
||||||
|
replyingTo,
|
||||||
|
attachment,
|
||||||
|
sticker,
|
||||||
|
gif,
|
||||||
|
poll,
|
||||||
|
contact,
|
||||||
|
groupedId,
|
||||||
|
scheduledAt,
|
||||||
|
sendAs,
|
||||||
|
serverTimeOffset,
|
||||||
);
|
);
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
|
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
|
||||||
|
|||||||
@ -776,7 +776,6 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_entities?.length) {
|
if (_entities?.length) {
|
||||||
_entities
|
_entities
|
||||||
.filter((e) => e instanceof GramJs.User && !e.contact)
|
.filter((e) => e instanceof GramJs.User && !e.contact)
|
||||||
|
|||||||
@ -81,7 +81,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
|
|||||||
updateFilter(target.value);
|
updateFilter(target.value);
|
||||||
}, [filter, updateFilter, value]);
|
}, [filter, updateFilter, value]);
|
||||||
|
|
||||||
const CodeInput: FC<{ onTrigger: () => void; isOpen?: boolean }> = ({ onTrigger, isOpen }) => {
|
const CodeInput: FC<{ onTrigger: () => void; isOpen?: boolean }> = useCallback(({ onTrigger, isOpen }) => {
|
||||||
const handleTrigger = () => {
|
const handleTrigger = () => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
return;
|
return;
|
||||||
@ -126,7 +126,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}, [filter, handleInput, handleInputKeyDown, id, isLoading, lang, value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
|
|||||||
@ -280,34 +280,32 @@ const Audio: FC<OwnProps> = ({
|
|||||||
|
|
||||||
function renderWithTitle() {
|
function renderWithTitle() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={contentClassName}>
|
||||||
<div className={contentClassName}>
|
<div className="content-row">
|
||||||
<div className="content-row">
|
<p className="title" dir="auto" title={renderFirstLine()}>{renderText(renderFirstLine())}</p>
|
||||||
<p className="title" dir="auto" title={renderFirstLine()}>{renderText(renderFirstLine())}</p>
|
|
||||||
|
|
||||||
<div className="message-date">
|
<div className="message-date">
|
||||||
{date && (
|
{date && (
|
||||||
<Link
|
<Link
|
||||||
className="date"
|
className="date"
|
||||||
onClick={handleDateClick}
|
onClick={handleDateClick}
|
||||||
>
|
>
|
||||||
{formatPastTimeShort(lang, date * 1000)}
|
{formatPastTimeShort(lang, date * 1000)}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{withSeekline && (
|
|
||||||
<div className="meta search-result" dir={isRtl ? 'rtl' : undefined}>
|
|
||||||
<span className="duration with-seekline" dir="auto">
|
|
||||||
{playProgress < 1 && `${formatMediaDuration(duration * playProgress, duration)}`}
|
|
||||||
</span>
|
|
||||||
{renderSeekline(playProgress, bufferedProgress, seekerRef)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!withSeekline && renderSecondLine()}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
|
{withSeekline && (
|
||||||
|
<div className="meta search-result" dir={isRtl ? 'rtl' : undefined}>
|
||||||
|
<span className="duration with-seekline" dir="auto">
|
||||||
|
{playProgress < 1 && `${formatMediaDuration(duration * playProgress, duration)}`}
|
||||||
|
</span>
|
||||||
|
{renderSeekline(playProgress, bufferedProgress, seekerRef)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!withSeekline && renderSecondLine()}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,8 +354,17 @@ const Audio: FC<OwnProps> = ({
|
|||||||
)}
|
)}
|
||||||
{origin === AudioOrigin.Search && renderWithTitle()}
|
{origin === AudioOrigin.Search && renderWithTitle()}
|
||||||
{origin !== AudioOrigin.Search && audio && renderAudio(
|
{origin !== AudioOrigin.Search && audio && renderAudio(
|
||||||
lang, audio, duration, isPlaying, playProgress, bufferedProgress, seekerRef,
|
lang,
|
||||||
(isDownloading || isUploading), date, transferProgress, onDateClick ? handleDateClick : undefined,
|
audio,
|
||||||
|
duration,
|
||||||
|
isPlaying,
|
||||||
|
playProgress,
|
||||||
|
bufferedProgress,
|
||||||
|
seekerRef,
|
||||||
|
(isDownloading || isUploading),
|
||||||
|
date,
|
||||||
|
transferProgress,
|
||||||
|
onDateClick ? handleDateClick : undefined,
|
||||||
)}
|
)}
|
||||||
{origin === AudioOrigin.SharedMedia && (voice || video) && renderWithTitle()}
|
{origin === AudioOrigin.SharedMedia && (voice || video) && renderWithTitle()}
|
||||||
{origin === AudioOrigin.Inline && voice && (
|
{origin === AudioOrigin.Inline && voice && (
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import React, { FC, memo, useCallback, useRef } from '../../lib/teact/teact';
|
import React, {
|
||||||
|
FC, memo, useCallback, useRef,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import { ApiMessage } from '../../api/types';
|
import { ApiMessage } from '../../api/types';
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export function renderMessageSummary(
|
|||||||
highlight?: string,
|
highlight?: string,
|
||||||
truncateLength = TRUNCATED_SUMMARY_LENGTH,
|
truncateLength = TRUNCATED_SUMMARY_LENGTH,
|
||||||
): TextPart[] {
|
): TextPart[] {
|
||||||
let { entities } = message.content.text || {};
|
const { entities } = message.content.text || {};
|
||||||
|
|
||||||
const hasSpoilers = entities?.some((e) => e.type === ApiMessageEntityTypes.Spoiler);
|
const hasSpoilers = entities?.some((e) => e.type === ApiMessageEntityTypes.Spoiler);
|
||||||
if (!hasSpoilers) {
|
if (!hasSpoilers) {
|
||||||
|
|||||||
@ -104,7 +104,9 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
|||||||
}, [mode, selectedChatIds, dispatch]);
|
}, [mode, selectedChatIds, dispatch]);
|
||||||
|
|
||||||
useHistoryBack(
|
useHistoryBack(
|
||||||
isActive, onReset, onScreenSelect,
|
isActive,
|
||||||
|
onReset,
|
||||||
|
onScreenSelect,
|
||||||
mode === 'included' ? SettingsScreens.FoldersIncludedChats : SettingsScreens.FoldersExcludedChats,
|
mode === 'included' ? SettingsScreens.FoldersIncludedChats : SettingsScreens.FoldersExcludedChats,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -163,17 +163,15 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="MediaViewerActions">
|
<div className="MediaViewerActions">
|
||||||
{!isAvatar && !isProtected && (
|
{!isAvatar && !isProtected && (
|
||||||
<>
|
<Button
|
||||||
<Button
|
round
|
||||||
round
|
size="smaller"
|
||||||
size="smaller"
|
color="translucent-white"
|
||||||
color="translucent-white"
|
ariaLabel={lang('Forward')}
|
||||||
ariaLabel={lang('Forward')}
|
onClick={onForward}
|
||||||
onClick={onForward}
|
>
|
||||||
>
|
<i className="icon-forward" />
|
||||||
<i className="icon-forward" />
|
</Button>
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
{renderDownloadButton()}
|
{renderDownloadButton()}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -518,7 +518,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
<MediaViewerContent
|
<MediaViewerContent
|
||||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||||
{...rest}
|
{...rest}
|
||||||
messageId={previousMessageId} />
|
messageId={previousMessageId}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{activeMessageId && (
|
{activeMessageId && (
|
||||||
@ -543,7 +544,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
<MediaViewerContent
|
<MediaViewerContent
|
||||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||||
{...rest}
|
{...rest}
|
||||||
messageId={nextMessageId}/>
|
messageId={nextMessageId}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -571,4 +573,5 @@ function checkIfControlTarget(e: TouchEvent | MouseEvent) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
|
import React, {
|
||||||
|
FC, memo, useCallback, useEffect, useRef, useState,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import { ApiDimensions } from '../../api/types';
|
import { ApiDimensions } from '../../api/types';
|
||||||
|
|
||||||
import useBuffering from '../../hooks/useBuffering';
|
import useBuffering from '../../hooks/useBuffering';
|
||||||
import useFullscreenStatus from '../../hooks/useFullscreen';
|
import useFullscreenStatus from '../../hooks/useFullscreen';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
||||||
import React, { FC, memo, useCallback, useEffect, useRef, useState } from '../../lib/teact/teact';
|
|
||||||
|
|
||||||
import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment';
|
import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment';
|
||||||
import safePlay from '../../util/safePlay';
|
import safePlay from '../../util/safePlay';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import ProgressSpinner from '../ui/ProgressSpinner';
|
import ProgressSpinner from '../ui/ProgressSpinner';
|
||||||
|
|
||||||
|
|||||||
@ -55,12 +55,11 @@ const VideoPlayerControls: FC<IProps> = ({
|
|||||||
const isSeekingRef = useRef<boolean>(false);
|
const isSeekingRef = useRef<boolean>(false);
|
||||||
const isSeeking = isSeekingRef.current;
|
const isSeeking = isSeekingRef.current;
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timeout: number | undefined;
|
let timeout: number | undefined;
|
||||||
if (!isVisible || !isPlayed || isSeeking) {
|
if (!isVisible || !isPlayed || isSeeking) {
|
||||||
if (timeout) window.clearTimeout(timeout);
|
if (timeout) window.clearTimeout(timeout);
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
timeout = window.setTimeout(() => {
|
timeout = window.setTimeout(() => {
|
||||||
setVisibility(false);
|
setVisibility(false);
|
||||||
@ -113,7 +112,6 @@ const VideoPlayerControls: FC<IProps> = ({
|
|||||||
});
|
});
|
||||||
}, [isVisible, handleStartSeek, handleSeek, handleStopSeek]);
|
}, [isVisible, handleStartSeek, handleSeek, handleStopSeek]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName('VideoPlayerControls', isForceMobileVersion && 'mobile', isVisible && 'active')}
|
className={buildClassName('VideoPlayerControls', isForceMobileVersion && 'mobile', isVisible && 'active')}
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import useModuleLoader from '../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const EmojiInteractionAnimationAsync: FC<OwnProps> = (props) => {
|
const EmojiInteractionAnimationAsync: FC<OwnProps> = (props) => {
|
||||||
const { activeEmojiInteraction } = props;
|
const { activeEmojiInteraction } = props;
|
||||||
const EmojiInteractionAnimation = useModuleLoader(Bundles.Extra, 'EmojiInteractionAnimation',
|
const EmojiInteractionAnimation = useModuleLoader(
|
||||||
!activeEmojiInteraction);
|
Bundles.Extra, 'EmojiInteractionAnimation', !activeEmojiInteraction,
|
||||||
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return EmojiInteractionAnimation ? <EmojiInteractionAnimation {...props} /> : undefined;
|
return EmojiInteractionAnimation ? <EmojiInteractionAnimation {...props} /> : undefined;
|
||||||
|
|||||||
@ -354,7 +354,10 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
|
|
||||||
useHistoryBack(
|
useHistoryBack(
|
||||||
renderingChatId && renderingThreadId,
|
renderingChatId && renderingThreadId,
|
||||||
closeChat, undefined, undefined, undefined,
|
closeChat,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
messageLists?.map(createMessageHash) || [],
|
messageLists?.map(createMessageHash) || [],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import React, { FC, memo, useCallback, useState } from '../../lib/teact/teact';
|
import React, {
|
||||||
|
FC, memo, useCallback, useState,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
import { withGlobal, getDispatch } from '../../lib/teact/teactn';
|
import { withGlobal, getDispatch } from '../../lib/teact/teactn';
|
||||||
|
|
||||||
import { ApiUser } from '../../api/types';
|
import { ApiUser } from '../../api/types';
|
||||||
@ -61,10 +63,9 @@ const UserReportPanel: FC<OwnProps & StateProps> = ({ userId, user }) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="UserReportPanel">
|
<div className="UserReportPanel">
|
||||||
{canAddContact && (
|
{canAddContact && (
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const AttachMenu: FC<OwnProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
if (!isButtonVisible) {
|
if (!isButtonVisible) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -27,6 +27,7 @@ const StickerSetCover: FC<OwnProps> = ({ stickerSet, observeIntersection }) => {
|
|||||||
|
|
||||||
const firstLetters = useMemo(() => {
|
const firstLetters = useMemo(() => {
|
||||||
if ((isVideo && !IS_WEBM_SUPPORTED) || !mediaData) return getFirstLetters(stickerSet.title, 2);
|
if ((isVideo && !IS_WEBM_SUPPORTED) || !mediaData) return getFirstLetters(stickerSet.title, 2);
|
||||||
|
return undefined;
|
||||||
}, [isVideo, mediaData, stickerSet.title]);
|
}, [isVideo, mediaData, stickerSet.title]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -2,7 +2,7 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
|
|||||||
const files: File[] = [];
|
const files: File[] = [];
|
||||||
|
|
||||||
function traverseFileTreePromise(entry: FileSystemEntry | File, item: DataTransferItem) {
|
function traverseFileTreePromise(entry: FileSystemEntry | File, item: DataTransferItem) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
if (entry instanceof File) {
|
if (entry instanceof File) {
|
||||||
files.push(entry);
|
files.push(entry);
|
||||||
resolve(entry);
|
resolve(entry);
|
||||||
@ -20,11 +20,11 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
|
|||||||
resolve(itemFile);
|
resolve(itemFile);
|
||||||
});
|
});
|
||||||
} else if (entry.isDirectory) {
|
} else if (entry.isDirectory) {
|
||||||
let dirReader = (entry as FileSystemDirectoryEntry).createReader();
|
const dirReader = (entry as FileSystemDirectoryEntry).createReader();
|
||||||
dirReader.readEntries((entries) => {
|
dirReader.readEntries((entries) => {
|
||||||
let entriesPromises = [];
|
const entriesPromises = [];
|
||||||
for (let entr of entries) {
|
for (let i = 0; i < entries.length; i++) {
|
||||||
entriesPromises.push(traverseFileTreePromise(entr, item));
|
entriesPromises.push(traverseFileTreePromise(entries[i], item));
|
||||||
}
|
}
|
||||||
resolve(Promise.all(entriesPromises));
|
resolve(Promise.all(entriesPromises));
|
||||||
});
|
});
|
||||||
@ -32,8 +32,9 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let entriesPromises = [];
|
const entriesPromises = [];
|
||||||
for (let item of dataTransferItems) {
|
for (let i = 0; i < dataTransferItems.length; i++) {
|
||||||
|
const item = dataTransferItems[i];
|
||||||
if (item.kind === 'file') {
|
if (item.kind === 'file') {
|
||||||
const entry = item.webkitGetAsEntry() || item.getAsFile();
|
const entry = item.webkitGetAsEntry() || item.getAsFile();
|
||||||
if (entry) {
|
if (entry) {
|
||||||
@ -46,4 +47,3 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
|
|||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,6 @@ const useClipboardPaste = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const pastedText = e.clipboardData.getData('text').substring(0, MAX_MESSAGE_LENGTH);
|
const pastedText = e.clipboardData.getData('text').substring(0, MAX_MESSAGE_LENGTH);
|
||||||
const { items } = e.clipboardData;
|
const { items } = e.clipboardData;
|
||||||
let files: File[] = [];
|
let files: File[] = [];
|
||||||
|
|||||||
@ -36,10 +36,10 @@ const prepareLibraryMemo = memoized(prepareLibrary);
|
|||||||
const searchInLibraryMemo = memoized(searchInLibrary);
|
const searchInLibraryMemo = memoized(searchInLibrary);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RE_EMOJI_SEARCH = new RegExp('(^|\\s):[-+_:\\p{L}\\p{N}]*$', 'gui');
|
RE_EMOJI_SEARCH = /(^|\s):[-+_:\p{L}\p{N}]*$/gui;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Support for older versions of firefox
|
// Support for older versions of firefox
|
||||||
RE_EMOJI_SEARCH = new RegExp('(^|\\s):[-+_:\\d\\wа-яё]*$', 'gi');
|
RE_EMOJI_SEARCH = /(^|\s):[-+_:\d\wа-яё]*$/gi;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useEmojiTooltip(
|
export default function useEmojiTooltip(
|
||||||
|
|||||||
@ -16,10 +16,10 @@ const runThrottled = throttle((cb) => cb(), 500, true);
|
|||||||
let RE_USERNAME_SEARCH: RegExp;
|
let RE_USERNAME_SEARCH: RegExp;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RE_USERNAME_SEARCH = new RegExp('(^|\\s)@[-_\\p{L}\\p{M}\\p{N}]*$', 'gui');
|
RE_USERNAME_SEARCH = /(^|\s)@[-_\p{L}\p{M}\p{N}]*$/gui;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Support for older versions of firefox
|
// Support for older versions of firefox
|
||||||
RE_USERNAME_SEARCH = new RegExp('(^|\\s)@[-_\\d\\wа-яё]*$', 'gi');
|
RE_USERNAME_SEARCH = /(^|\s)@[-_\d\wа-яё]*$/gi;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useMentionTooltip(
|
export default function useMentionTooltip(
|
||||||
|
|||||||
@ -19,7 +19,9 @@ import { formatCountdownShort, formatLastUpdated } from '../../../util/dateForma
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||||
import useTimeout from '../../../hooks/useTimeout';
|
import useTimeout from '../../../hooks/useTimeout';
|
||||||
import { getMetersPerPixel, getVenueColor, getVenueIconUrl, prepareMapUrl } from '../../../util/map';
|
import {
|
||||||
|
getMetersPerPixel, getVenueColor, getVenueIconUrl, prepareMapUrl,
|
||||||
|
} from '../../../util/map';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import usePrevious from '../../../hooks/usePrevious';
|
import usePrevious from '../../../hooks/usePrevious';
|
||||||
import useInterval from '../../../hooks/useInterval';
|
import useInterval from '../../../hooks/useInterval';
|
||||||
@ -28,7 +30,7 @@ import { getServerTime } from '../../../util/serverTime';
|
|||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import Skeleton from '../../ui/Skeleton';
|
import Skeleton from '../../ui/Skeleton';
|
||||||
|
|
||||||
import mapPin from '/src/assets/map-pin.svg';
|
import mapPin from '../../../assets/map-pin.svg';
|
||||||
import './Location.scss';
|
import './Location.scss';
|
||||||
|
|
||||||
const MOVE_THRESHOLD = 0.0001; // ~11m
|
const MOVE_THRESHOLD = 0.0001; // ~11m
|
||||||
@ -79,7 +81,9 @@ const Location: FC<OwnProps> = ({
|
|||||||
const [point, setPoint] = useState(geo);
|
const [point, setPoint] = useState(geo);
|
||||||
|
|
||||||
const shouldRenderText = type === 'venue' || (type === 'geoLive' && !isExpired);
|
const shouldRenderText = type === 'venue' || (type === 'geoLive' && !isExpired);
|
||||||
const { width, height, zoom, scale } = DEFAULT_MAP_CONFIG;
|
const {
|
||||||
|
width, height, zoom, scale,
|
||||||
|
} = DEFAULT_MAP_CONFIG;
|
||||||
|
|
||||||
const mediaHash = Boolean(lastSyncTime) && buildStaticMapHash(point, width, height, zoom, scale);
|
const mediaHash = Boolean(lastSyncTime) && buildStaticMapHash(point, width, height, zoom, scale);
|
||||||
const mediaBlobUrl = useMedia(mediaHash);
|
const mediaBlobUrl = useMedia(mediaHash);
|
||||||
@ -206,13 +210,15 @@ const Location: FC<OwnProps> = ({
|
|||||||
|
|
||||||
function renderMap() {
|
function renderMap() {
|
||||||
if (!mapBlobUrl) return <Skeleton width={width} height={height} />;
|
if (!mapBlobUrl) return <Skeleton width={width} height={height} />;
|
||||||
return <img
|
return (
|
||||||
className="full-media map"
|
<img
|
||||||
src={mapBlobUrl}
|
className="full-media map"
|
||||||
alt="Location on a map"
|
src={mapBlobUrl}
|
||||||
width={DEFAULT_MAP_CONFIG.width}
|
alt="Location on a map"
|
||||||
height={DEFAULT_MAP_CONFIG.height}
|
width={DEFAULT_MAP_CONFIG.width}
|
||||||
/>;
|
height={DEFAULT_MAP_CONFIG.height}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPin() {
|
function renderPin() {
|
||||||
@ -279,4 +285,3 @@ const Location: FC<OwnProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo(Location);
|
export default memo(Location);
|
||||||
|
|
||||||
|
|||||||
@ -964,7 +964,9 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, ownProps): StateProps => {
|
(global, ownProps): StateProps => {
|
||||||
const { focusedMessage, forwardMessages, lastSyncTime, serverTimeOffset } = global;
|
const {
|
||||||
|
focusedMessage, forwardMessages, lastSyncTime, serverTimeOffset,
|
||||||
|
} = global;
|
||||||
const {
|
const {
|
||||||
message, album, withSenderName, withAvatar, threadId, messageListType, isLastInDocumentGroup,
|
message, album, withSenderName, withAvatar, threadId, messageListType, isLastInDocumentGroup,
|
||||||
} = ownProps;
|
} = ownProps;
|
||||||
|
|||||||
@ -25,7 +25,9 @@ type OwnProps = {
|
|||||||
|
|
||||||
const cn = createClassNameBuilder('ReactionSelectorReaction');
|
const cn = createClassNameBuilder('ReactionSelectorReaction');
|
||||||
|
|
||||||
const ReactionSelectorReaction: FC<OwnProps> = ({ reaction, previewIndex, onSendReaction, isReady }) => {
|
const ReactionSelectorReaction: FC<OwnProps> = ({
|
||||||
|
reaction, previewIndex, onSendReaction, isReady,
|
||||||
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
|||||||
@ -41,16 +41,17 @@ export default function withSelectControl(WrappedComponent: FC) {
|
|||||||
}, [toggleMessageSelection, message]);
|
}, [toggleMessageSelection, message]);
|
||||||
|
|
||||||
const newProps = useMemo(() => {
|
const newProps = useMemo(() => {
|
||||||
|
const { dimensions: dims, onClick } = props;
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
isInSelectMode,
|
isInSelectMode,
|
||||||
isSelected,
|
isSelected,
|
||||||
dimensions: {
|
dimensions: {
|
||||||
...props.dimensions,
|
...dims,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
},
|
},
|
||||||
onClick: isInSelectMode ? undefined : props.onClick,
|
onClick: isInSelectMode ? undefined : onClick,
|
||||||
};
|
};
|
||||||
}, [props, isInSelectMode, isSelected]);
|
}, [props, isInSelectMode, isSelected]);
|
||||||
|
|
||||||
|
|||||||
@ -167,8 +167,21 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
const tabType = tabs[activeTab].type as ProfileTabType;
|
const tabType = tabs[activeTab].type as ProfileTabType;
|
||||||
|
|
||||||
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds(
|
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds(
|
||||||
isRightColumnShown, loadMoreMembers, loadCommonChats, searchMediaMessagesLocal, tabType, mediaSearchType, members,
|
isRightColumnShown,
|
||||||
commonChatIds, usersById, userStatusesById, chatsById, chatMessages, foundIds, chatId, lastSyncTime,
|
loadMoreMembers,
|
||||||
|
loadCommonChats,
|
||||||
|
searchMediaMessagesLocal,
|
||||||
|
tabType,
|
||||||
|
mediaSearchType,
|
||||||
|
members,
|
||||||
|
commonChatIds,
|
||||||
|
usersById,
|
||||||
|
userStatusesById,
|
||||||
|
chatsById,
|
||||||
|
chatMessages,
|
||||||
|
foundIds,
|
||||||
|
chatId,
|
||||||
|
lastSyncTime,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
);
|
);
|
||||||
const activeKey = tabs.findIndex(({ type }) => type === resultType);
|
const activeKey = tabs.findIndex(({ type }) => type === resultType);
|
||||||
|
|||||||
@ -211,7 +211,7 @@ const RightColumn: FC<StateProps> = ({
|
|||||||
|
|
||||||
// We need to clear profile state and management screen state, when changing chats
|
// We need to clear profile state and management screen state, when changing chats
|
||||||
useLayoutEffectWithPrevDeps(([prevChatId]) => {
|
useLayoutEffectWithPrevDeps(([prevChatId]) => {
|
||||||
if (prevChatId !== chatId ) {
|
if (prevChatId !== chatId) {
|
||||||
setProfileState(ProfileState.Profile);
|
setProfileState(ProfileState.Profile);
|
||||||
setManagementScreen(ManagementScreens.Initial);
|
setManagementScreen(ManagementScreens.Initial);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -295,7 +295,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className='text-muted'>{isChannel ? lang('PrimaryLinkHelpChannel') : lang('PrimaryLinkHelp')}</p>
|
<p className="text-muted">{isChannel ? lang('PrimaryLinkHelpChannel') : lang('PrimaryLinkHelp')}</p>
|
||||||
</div>
|
</div>
|
||||||
{primaryInviteLink && (
|
{primaryInviteLink && (
|
||||||
<div className="section">
|
<div className="section">
|
||||||
|
|||||||
@ -148,7 +148,7 @@ const Statistics: FC<OwnProps & StateProps> = ({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{Boolean(statistics.recentTopMessages?.length) &&
|
{Boolean(statistics.recentTopMessages?.length) && (
|
||||||
<div className="Statistics--messages">
|
<div className="Statistics--messages">
|
||||||
<h2 className="Statistics--messages-title">{lang('ChannelStats.Recent.Header')}</h2>
|
<h2 className="Statistics--messages-title">{lang('ChannelStats.Recent.Header')}</h2>
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ const Statistics: FC<OwnProps & StateProps> = ({
|
|||||||
<StatisticsRecentMessage message={message as ApiMessage & StatisticsRecentMessageType} />
|
<StatisticsRecentMessage message={message as ApiMessage & StatisticsRecentMessageType} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -44,7 +44,7 @@ const StatisticsRecentMessage: FC<OwnProps> = ({ message }) => {
|
|||||||
{formatDateTimeToString(message.date * 1000, lang.code)}
|
{formatDateTimeToString(message.date * 1000, lang.code)}
|
||||||
</div>
|
</div>
|
||||||
<div className="StatisticsRecentMessage--meta">
|
<div className="StatisticsRecentMessage--meta">
|
||||||
{Boolean(message.forwards) ? lang('ChannelStats.SharesCount', message.forwards) : 'No shares'}
|
{message.forwards ? lang('ChannelStats.SharesCount', message.forwards) : 'No shares'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -137,7 +137,6 @@ const Button: FC<OwnProps> = ({
|
|||||||
dir={isRtl ? 'rtl' : undefined}
|
dir={isRtl ? 'rtl' : undefined}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
aria-controls={ariaControls}
|
aria-controls={ariaControls}
|
||||||
aria-haspopup={hasPopup}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{!disabled && ripple && (
|
{!disabled && ripple && (
|
||||||
@ -148,7 +147,6 @@ const Button: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line react/button-has-type
|
|
||||||
<button
|
<button
|
||||||
ref={elementRef as RefObject<HTMLButtonElement>}
|
ref={elementRef as RefObject<HTMLButtonElement>}
|
||||||
id={id}
|
id={id}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import React, { FC, memo, useCallback, useRef } from '../../lib/teact/teact';
|
import React, {
|
||||||
|
FC, memo, useCallback, useRef,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import { TextPart } from '../common/helpers/renderMessageText';
|
import { TextPart } from '../common/helpers/renderMessageText';
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { FC, useRef, useLayoutEffect, VirtualElement } from '../../lib/teact/teact';
|
import {
|
||||||
|
FC, useRef, useLayoutEffect, VirtualElement,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
import TeactDOM from '../../lib/teact/teact-dom';
|
import TeactDOM from '../../lib/teact/teact-dom';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import useShowTransition from '../../hooks/useShowTransition';
|
|||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
isCustom?: boolean;
|
isCustom?: boolean;
|
||||||
|
|||||||
@ -12,7 +12,9 @@ type OwnProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Skeleton: FC<OwnProps> = ({ variant = 'rectangular', animation = 'wave', width, height, className }) => {
|
const Skeleton: FC<OwnProps> = ({
|
||||||
|
variant = 'rectangular', animation = 'wave', width, height, className,
|
||||||
|
}) => {
|
||||||
const classNames = buildClassName('Skeleton', variant, animation, className);
|
const classNames = buildClassName('Skeleton', variant, animation, className);
|
||||||
const style = (width ? `width: ${width}px;` : '') + (height ? `height: ${height}px;` : '');
|
const style = (width ? `width: ${width}px;` : '') + (height ? `height: ${height}px;` : '');
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -260,7 +260,8 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
typeof render === 'function'
|
typeof render === 'function'
|
||||||
? render(key === activeKey, key === prevActiveKey, activeKey)
|
? render(key === activeKey, key === prevActiveKey, activeKey)
|
||||||
: render
|
: render
|
||||||
}</div>
|
}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export const useResize = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isActive) return;
|
if (!isActive) return undefined;
|
||||||
|
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
const handleMouseMove = (event: MouseEvent) => {
|
||||||
const newWidth = Math.ceil(initialElementWidth + event.clientX - initialMouseX);
|
const newWidth = Math.ceil(initialElementWidth + event.clientX - initialMouseX);
|
||||||
|
|||||||
@ -1,29 +1,34 @@
|
|||||||
{
|
{
|
||||||
"rules": {
|
"root": true,
|
||||||
"indent": [
|
"parser": "@typescript-eslint/parser",
|
||||||
"error",
|
"plugins": [
|
||||||
4,
|
"@typescript-eslint"
|
||||||
{
|
],
|
||||||
"SwitchCase": 1
|
"rules": {
|
||||||
}
|
"indent": [
|
||||||
],
|
"error",
|
||||||
"@typescript-eslint/indent": [
|
4,
|
||||||
"error",
|
{
|
||||||
4,
|
"SwitchCase": 1
|
||||||
{
|
}
|
||||||
"SwitchCase": 1
|
],
|
||||||
}
|
"@typescript-eslint/indent": [
|
||||||
],
|
"error",
|
||||||
"max-len": [
|
4,
|
||||||
"error",
|
{
|
||||||
120
|
"SwitchCase": 1
|
||||||
],
|
}
|
||||||
"no-bitwise": "off",
|
],
|
||||||
"no-underscore-dangle": "off",
|
"max-len": [
|
||||||
"no-continue": "off",
|
"error",
|
||||||
"no-restricted-syntax": "off",
|
120
|
||||||
"class-methods-use-this": "off",
|
],
|
||||||
"max-classes-per-file": "off",
|
"no-bitwise": "off",
|
||||||
"camelcase": "error"
|
"no-underscore-dangle": "off",
|
||||||
}
|
"no-continue": "off",
|
||||||
|
"no-restricted-syntax": "off",
|
||||||
|
"class-methods-use-this": "off",
|
||||||
|
"max-classes-per-file": "off",
|
||||||
|
"camelcase": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -186,7 +186,6 @@ class TelegramClient {
|
|||||||
}
|
}
|
||||||
// set defaults vars
|
// set defaults vars
|
||||||
this._sender.userDisconnected = false;
|
this._sender.userDisconnected = false;
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
this._sender._user_connected = false;
|
this._sender._user_connected = false;
|
||||||
this._sender.isReconnecting = false;
|
this._sender.isReconnecting = false;
|
||||||
this._sender._disconnected = true;
|
this._sender._disconnected = true;
|
||||||
|
|||||||
@ -31,11 +31,11 @@
|
|||||||
@keyframes lovely-chart--animation-fadeIn {
|
@keyframes lovely-chart--animation-fadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale3d(.5, .5, 1);
|
transform: scale3d(0.5, 0.5, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
40% {
|
40% {
|
||||||
transform: scale3d(.75, .75, 1);
|
transform: scale3d(0.75, 0.75, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
@ -51,12 +51,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
60% {
|
60% {
|
||||||
transform: scale3d(.75, .75, 1);
|
transform: scale3d(0.75, 0.75, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale3d(.5, .5, 1);
|
transform: scale3d(0.5, 0.5, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
.lovely-chart--button {
|
.lovely-chart--button {
|
||||||
border: 1px solid #E6ECF0;
|
border: 1px solid #E6ECF0;
|
||||||
background-color: transparent;
|
|
||||||
padding: 7px 7px;
|
padding: 7px 7px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
@ -11,7 +10,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
border-color: var(--text-color);
|
border-color: var(--text-color);
|
||||||
background: transparent;
|
background-color: transparent;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
|
||||||
&.lovely-chart--state-checked {
|
&.lovely-chart--state-checked {
|
||||||
|
|||||||
@ -169,7 +169,8 @@ addReducer('openTipsChat', (global, actions, payload) => {
|
|||||||
|
|
||||||
addReducer('loadAllChats', (global, actions, payload) => {
|
addReducer('loadAllChats', (global, actions, payload) => {
|
||||||
const listType = payload.listType as 'active' | 'archived';
|
const listType = payload.listType as 'active' | 'archived';
|
||||||
let { shouldReplace, onReplace } = payload;
|
const { onReplace } = payload;
|
||||||
|
let { shouldReplace } = payload;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|||||||
@ -34,7 +34,9 @@ addReducer('loadStatistics', (global, actions, payload) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addReducer('loadStatisticsAsyncGraph', (global, actions, payload) => {
|
addReducer('loadStatisticsAsyncGraph', (global, actions, payload) => {
|
||||||
const { chatId, token, name, isPercentage } = payload;
|
const {
|
||||||
|
chatId, token, name, isPercentage,
|
||||||
|
} = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat?.fullInfo) {
|
if (!chat?.fullInfo) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -43,8 +43,9 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
|||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
participants.forEach((participant) => {
|
participants.forEach((participant) => {
|
||||||
if (participant.id) {
|
if (participant.id) {
|
||||||
global = updateGroupCallParticipant(global, groupCallId, participant.id, participant,
|
global = updateGroupCallParticipant(
|
||||||
Boolean(nextOffset) || currentUserId === participant.id);
|
global, groupCallId, participant.id, participant, Boolean(nextOffset) || currentUserId === participant.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (nextOffset) {
|
if (nextOffset) {
|
||||||
|
|||||||
@ -687,7 +687,7 @@ addReducer('copySelectedMessages', (global) => {
|
|||||||
copyTextForMessages(global, chatId, messageIds);
|
copyTextForMessages(global, chatId, messageIds);
|
||||||
});
|
});
|
||||||
|
|
||||||
addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number[] } ) => {
|
addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number[] }) => {
|
||||||
const { messageIds } = payload;
|
const { messageIds } = payload;
|
||||||
const chat = selectCurrentChat(global);
|
const chat = selectCurrentChat(global);
|
||||||
if (!messageIds || messageIds.length === 0 || !chat) {
|
if (!messageIds || messageIds.length === 0 || !chat) {
|
||||||
@ -697,7 +697,6 @@ addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number
|
|||||||
copyTextForMessages(global, chat.id, messageIds);
|
copyTextForMessages(global, chat.id, messageIds);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
|
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
|
||||||
const { threadId } = selectCurrentMessageList(global) || {};
|
const { threadId } = selectCurrentMessageList(global) || {};
|
||||||
const lang = langProvider.getTranslation;
|
const lang = langProvider.getTranslation;
|
||||||
@ -712,7 +711,7 @@ function copyTextForMessages(global: GlobalState, chatId: string, messageIds: nu
|
|||||||
const result = messages.reduce((acc, message) => {
|
const result = messages.reduce((acc, message) => {
|
||||||
const sender = selectSender(global, message);
|
const sender = selectSender(global, message);
|
||||||
acc.push(`> ${sender ? getSenderTitle(lang, sender) : ''}:`);
|
acc.push(`> ${sender ? getSenderTitle(lang, sender) : ''}:`);
|
||||||
acc.push(getMessageSummaryText(lang, message, false, 0, undefined, true) + '\n');
|
acc.push(`${getMessageSummaryText(lang, message, false, 0, undefined, true)}\n`);
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as string[]);
|
}, [] as string[]);
|
||||||
|
|||||||
@ -134,7 +134,9 @@ export function buildStaticMapHash(
|
|||||||
zoom: number,
|
zoom: number,
|
||||||
scale: number,
|
scale: number,
|
||||||
) {
|
) {
|
||||||
const { long, lat, accessHash, accuracyRadius } = geo;
|
const {
|
||||||
|
long, lat, accessHash, accuracyRadius,
|
||||||
|
} = geo;
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
return `staticMap:${accessHash}?lat=${lat}&long=${long}&w=${width}&h=${height}&zoom=${zoom}&scale=${scale}&accuracyRadius=${accuracyRadius}`;
|
return `staticMap:${accessHash}?lat=${lat}&long=${long}&w=${width}&h=${height}&zoom=${zoom}&scale=${scale}&accuracyRadius=${accuracyRadius}`;
|
||||||
|
|||||||
@ -48,7 +48,6 @@ export function getMessageTextWithSpoilers(message: ApiMessage) {
|
|||||||
|
|
||||||
const spoiler = generateBrailleSpoiler(length);
|
const spoiler = generateBrailleSpoiler(length);
|
||||||
|
|
||||||
|
|
||||||
return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`;
|
return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`;
|
||||||
}, text);
|
}, text);
|
||||||
}
|
}
|
||||||
@ -160,12 +159,12 @@ export function getMessageSummaryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invoice) {
|
if (invoice) {
|
||||||
summary = lang('PaymentInvoice') + ': ' + invoice.text;
|
summary = `${lang('PaymentInvoice')}: ${invoice.text}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
if (isExtended && summary) {
|
if (isExtended && summary) {
|
||||||
summary += '\n' + truncatedText;
|
summary += `\n${truncatedText}`;
|
||||||
} else {
|
} else {
|
||||||
summary = truncatedText;
|
summary = truncatedText;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,10 @@ export function checkIfReactionAdded(oldReactions?: ApiReactions, newReactions?:
|
|||||||
if (!oldReactions || !oldReactions.recentReactions) return true;
|
if (!oldReactions || !oldReactions.recentReactions) return true;
|
||||||
if (!newReactions || !newReactions.recentReactions) return false;
|
if (!newReactions || !newReactions.recentReactions) return false;
|
||||||
// Skip reactions from yourself
|
// Skip reactions from yourself
|
||||||
if (newReactions.recentReactions.every(reaction => reaction.userId === currentUserId)) return false;
|
if (newReactions.recentReactions.every((reaction) => reaction.userId === currentUserId)) return false;
|
||||||
const oldReactionsMap = oldReactions.results.reduce<Record<string, number>>((acc, reaction) => {
|
const oldReactionsMap = oldReactions.results.reduce<Record<string, number>>((acc, reaction) => {
|
||||||
acc[reaction.reaction] = reaction.count;
|
acc[reaction.reaction] = reaction.count;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
return newReactions.results.some(r => !oldReactionsMap[r.reaction] || oldReactionsMap[r.reaction] < r.count);
|
return newReactions.results.some((r) => !oldReactionsMap[r.reaction] || oldReactionsMap[r.reaction] < r.count);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -219,7 +219,7 @@ export function leaveChat(global: GlobalState, leftChatId: string): GlobalState
|
|||||||
|
|
||||||
export function addChatMembers(global: GlobalState, chat: ApiChat, membersToAdd: ApiChatMember[]): GlobalState {
|
export function addChatMembers(global: GlobalState, chat: ApiChat, membersToAdd: ApiChatMember[]): GlobalState {
|
||||||
const currentMembers = chat.fullInfo?.members;
|
const currentMembers = chat.fullInfo?.members;
|
||||||
const newMemberIds = new Set(membersToAdd.map(m => m.userId));
|
const newMemberIds = new Set(membersToAdd.map((m) => m.userId));
|
||||||
const updatedMembers = [
|
const updatedMembers = [
|
||||||
...currentMembers?.filter((m) => !newMemberIds.has(m.userId)) || [],
|
...currentMembers?.filter((m) => !newMemberIds.has(m.userId)) || [],
|
||||||
...membersToAdd,
|
...membersToAdd,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { DEBUG } from '../config';
|
import { DEBUG } from '../config';
|
||||||
|
|
||||||
export const CLIPBOARD_ITEM_SUPPORTED = navigator.clipboard && window.ClipboardItem;
|
export const CLIPBOARD_ITEM_SUPPORTED = window.navigator.clipboard && window.ClipboardItem;
|
||||||
|
|
||||||
const textCopyEl = document.createElement('textarea');
|
const textCopyEl = document.createElement('textarea');
|
||||||
textCopyEl.setAttribute('readonly', '');
|
textCopyEl.setAttribute('readonly', '');
|
||||||
@ -51,7 +51,7 @@ async function copyBlobToClipboard(pngBlob: Blob | null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.write?.([
|
await window.navigator.clipboard.write?.([
|
||||||
new ClipboardItem({
|
new ClipboardItem({
|
||||||
[pngBlob.type]: pngBlob,
|
[pngBlob.type]: pngBlob,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -55,7 +55,7 @@ export const IS_TABLET_COLUMN_LAYOUT = !IS_SINGLE_COLUMN_LAYOUT && (
|
|||||||
window.innerWidth <= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN
|
window.innerWidth <= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN
|
||||||
);
|
);
|
||||||
export const IS_VOICE_RECORDING_SUPPORTED = Boolean(
|
export const IS_VOICE_RECORDING_SUPPORTED = Boolean(
|
||||||
navigator.mediaDevices && 'getUserMedia' in navigator.mediaDevices && (
|
window.navigator.mediaDevices && 'getUserMedia' in window.navigator.mediaDevices && (
|
||||||
window.AudioContext || (window as any).webkitAudioContext
|
window.AudioContext || (window as any).webkitAudioContext
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -203,9 +203,12 @@ function updateFolderManager(global: GlobalState) {
|
|||||||
updateFolders(global, isAllFolderChanged, isArchivedFolderChanged, areFoldersChanged);
|
updateFolders(global, isAllFolderChanged, isArchivedFolderChanged, areFoldersChanged);
|
||||||
|
|
||||||
affectedFolderIds = affectedFolderIds.concat(updateChats(
|
affectedFolderIds = affectedFolderIds.concat(updateChats(
|
||||||
global, areFoldersChanged || isAllFolderChanged || isArchivedFolderChanged,
|
global,
|
||||||
areNotifySettingsChanged, areNotifyExceptionsChanged,
|
areFoldersChanged || isAllFolderChanged || isArchivedFolderChanged,
|
||||||
prevAllFolderListIds, prevArchivedFolderListIds,
|
areNotifySettingsChanged,
|
||||||
|
areNotifyExceptionsChanged,
|
||||||
|
prevAllFolderListIds,
|
||||||
|
prevArchivedFolderListIds,
|
||||||
));
|
));
|
||||||
|
|
||||||
updateResults(unique(affectedFolderIds));
|
updateResults(unique(affectedFolderIds));
|
||||||
@ -315,7 +318,7 @@ function buildFolderSummaryFromMainList(
|
|||||||
return {
|
return {
|
||||||
id: folderId,
|
id: folderId,
|
||||||
listIds: new Set(listIds),
|
listIds: new Set(listIds),
|
||||||
orderedPinnedIds: orderedPinnedIds,
|
orderedPinnedIds,
|
||||||
pinnedChatIds: new Set(orderedPinnedIds),
|
pinnedChatIds: new Set(orderedPinnedIds),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ export default function getMessageIdsForSelectedText() {
|
|||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
let selectedFragments = selection && selection.rangeCount ? selection.getRangeAt(0).cloneContents() : undefined;
|
let selectedFragments = selection && selection.rangeCount ? selection.getRangeAt(0).cloneContents() : undefined;
|
||||||
if (!selectedFragments || selectedFragments.childElementCount === 0) {
|
if (!selectedFragments || selectedFragments.childElementCount === 0) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageIds = Array.from(selectedFragments.children)
|
const messageIds = Array.from(selectedFragments.children)
|
||||||
@ -19,7 +19,6 @@ export default function getMessageIdsForSelectedText() {
|
|||||||
return result;
|
return result;
|
||||||
}, [] as number[]);
|
}, [] as number[]);
|
||||||
|
|
||||||
|
|
||||||
// Cleanup a document fragment because it is playing media content in the background
|
// Cleanup a document fragment because it is playing media content in the background
|
||||||
while (selectedFragments.firstChild) {
|
while (selectedFragments.firstChild) {
|
||||||
selectedFragments.removeChild(selectedFragments.firstChild);
|
selectedFragments.removeChild(selectedFragments.firstChild);
|
||||||
|
|||||||
@ -5,23 +5,23 @@ const PROVIDER = 'http://maps.google.com/maps';
|
|||||||
const VENUE_COLORS = new Map(Object.entries({
|
const VENUE_COLORS = new Map(Object.entries({
|
||||||
'building/medical': '#43b3f4',
|
'building/medical': '#43b3f4',
|
||||||
'building/gym': '#43b3f4',
|
'building/gym': '#43b3f4',
|
||||||
'arts_entertainment': '#e56dd6',
|
'education/cafeteria': '#f7943f',
|
||||||
'travel/bedandbreakfast': '#9987ff',
|
'travel/bedandbreakfast': '#9987ff',
|
||||||
'travel/hotel': '#9987ff',
|
'travel/hotel': '#9987ff',
|
||||||
'travel/hostel': '#9987ff',
|
'travel/hostel': '#9987ff',
|
||||||
'travel/resort': '#9987ff',
|
'travel/resort': '#9987ff',
|
||||||
'building': '#6e81b2',
|
|
||||||
'education': '#a57348',
|
|
||||||
'event': '#959595',
|
|
||||||
'food': '#f7943f',
|
|
||||||
'education/cafeteria': '#f7943f',
|
|
||||||
'nightlife': '#e56dd6',
|
|
||||||
'travel/hotel_bar': '#e56dd6',
|
'travel/hotel_bar': '#e56dd6',
|
||||||
'parks_outdoors': '#6cc039',
|
arts_entertainment: '#e56dd6',
|
||||||
'shops': '#ffb300',
|
building: '#6e81b2',
|
||||||
'travel': '#1c9fff',
|
education: '#a57348',
|
||||||
'work': '#ad7854',
|
event: '#959595',
|
||||||
'home': '#00aeef',
|
food: '#f7943f',
|
||||||
|
home: '#00aeef',
|
||||||
|
nightlife: '#e56dd6',
|
||||||
|
parks_outdoors: '#6cc039',
|
||||||
|
shops: '#ffb300',
|
||||||
|
travel: '#1c9fff',
|
||||||
|
work: '#ad7854',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const RANDOM_COLORS = [
|
const RANDOM_COLORS = [
|
||||||
@ -34,7 +34,7 @@ export function prepareMapUrl(lat: number, long: number, zoom: number) {
|
|||||||
|
|
||||||
export function getMetersPerPixel(lat: number, zoom: number) {
|
export function getMetersPerPixel(lat: number, zoom: number) {
|
||||||
// https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM/m/osOYQYXg2oUJ
|
// https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM/m/osOYQYXg2oUJ
|
||||||
return 156543.03392 * Math.cos(lat * Math.PI / 180) / Math.pow(2, zoom);
|
return (156543.03392 * Math.cos(lat * (Math.PI / 180))) / 2 ** zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVenueIconUrl(type?: string) {
|
export function getVenueIconUrl(type?: string) {
|
||||||
@ -52,6 +52,7 @@ export function getVenueColor(type?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stringToNumber(str: string) {
|
function stringToNumber(str: string) {
|
||||||
return str.split('').reduce((prevHash, currVal) =>
|
return str.split('').reduce((prevHash, currVal) => (
|
||||||
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0, 0);
|
// eslint-disable-next-line no-bitwise
|
||||||
|
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0), 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { callApi } from '../api/gramjs';
|
import { callApi } from '../api/gramjs';
|
||||||
import { ApiChat, ApiMediaFormat, ApiMessage, ApiUser, ApiUserReaction } from '../api/types';
|
import {
|
||||||
|
ApiChat, ApiMediaFormat, ApiMessage, ApiUser, ApiUserReaction,
|
||||||
|
} from '../api/types';
|
||||||
import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText';
|
import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText';
|
||||||
import { DEBUG, IS_TEST } from '../config';
|
import { DEBUG, IS_TEST } from '../config';
|
||||||
import { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn';
|
import { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn';
|
||||||
@ -264,9 +266,11 @@ function checkIfShouldNotify(chat: ApiChat) {
|
|||||||
|
|
||||||
function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: ApiUserReaction) {
|
function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: ApiUserReaction) {
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
|
const {
|
||||||
|
replyToMessageId,
|
||||||
|
} = message;
|
||||||
let {
|
let {
|
||||||
senderId,
|
senderId,
|
||||||
replyToMessageId,
|
|
||||||
} = message;
|
} = message;
|
||||||
if (reaction) senderId = reaction.userId;
|
if (reaction) senderId = reaction.userId;
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
let RE_NOT_LETTER: RegExp;
|
let RE_NOT_LETTER: RegExp;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RE_NOT_LETTER = new RegExp('[^\\p{L}\\p{M}]+', 'ui');
|
RE_NOT_LETTER = /[^\p{L}\p{M}]+/ui;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Support for older versions of firefox
|
// Support for older versions of firefox
|
||||||
RE_NOT_LETTER = new RegExp('[^\\wа-яё]+', 'i');
|
RE_NOT_LETTER = /[^\wа-яё]+/i;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function searchWords(haystack: string, needle: string | string[]) {
|
export default function searchWords(haystack: string, needle: string | string[]) {
|
||||||
|
|||||||
@ -52,7 +52,7 @@ if (IS_SERVICE_WORKER_SUPPORTED) {
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('[SW] Hard reload detected, re-enabling Service Worker');
|
console.log('[SW] Hard reload detected, re-enabling Service Worker');
|
||||||
}
|
}
|
||||||
await Promise.all(registrations.map(r => r.unregister()));
|
await Promise.all(registrations.map((r) => r.unregister()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user