Main Menu: Add Beta changelog and discussion chat (#1810)

This commit is contained in:
Alexander Zinchuk 2022-04-19 15:11:48 +02:00
parent facb6eac5f
commit c3e2079b69
17 changed files with 834 additions and 68700 deletions

View File

@ -1,4 +1,3 @@
dev
src/lib/rlottie/rlottie-wasm.js src/lib/rlottie/rlottie-wasm.js
src/lib/webp/webp_wasm.js src/lib/webp/webp_wasm.js

View File

@ -1,6 +1,6 @@
/* eslint-env node */ /* eslint-env node */
module.exports = async ({ github, context, core, body }) => { module.exports = async ({ github, context, body }) => {
await github.rest.issues.createComment({ await github.rest.issues.createComment({
issue_number: context.issue.number, issue_number: context.issue.number,
owner: context.repo.owner, owner: context.repo.owner,

View File

@ -4,10 +4,10 @@ const fs = require('fs');
const createPRComment = require('./createPRComment'); const createPRComment = require('./createPRComment');
const template = require('../.github/workflows/statoscope-comment.js'); const template = require('../.github/workflows/statoscope-comment.js');
module.exports = async ({ github, context, core }) => { module.exports = async ({ github, context }) => {
const data = JSON.parse(fs.readFileSync('result.json', 'utf8')); const data = JSON.parse(fs.readFileSync('result.json', 'utf8'));
data.prNumber = context.issue.number; data.prNumber = context.issue.number;
const body = template(data); const body = template(data);
await createPRComment({ github, context, core, body }); await createPRComment({ github, context, body });
}; };

View File

@ -1,82 +0,0 @@
// @ts-nocheck
const replaceInFile = require('replace-in-file');
const langPackIOs = require('./en-ios.json');
const langPackExtra = require('./en-extra.json');
(async () => {
const pairs = preparePairs();
const pairs2 = preparePairs2();
await replaceInFile({
files: './src/components/**/*.tsx',
from: [
/(>[\r\n\s]*)([\w\-.?! ]+)([\r\n\s]*<)/gm,
/(placeholder=)"([\w\-.?! ]+)"()/gm,
/(label=)"([\w\-.?! ]+)"()/gm,
/(ariaLabel=)"([\w\-.?! ]+)"()/gm,
/(submitLabel=)"([\w\-.?! ]+)"()/gm,
],
to: (match: string, p1: string, p2: string, p3: string) => {
const trimmed = p2.trim().toLowerCase();
if (pairs2[trimmed]) {
return `${p1}{lang('${pairs2[trimmed]}')}${p3}`;
} else if (pairs[trimmed]) {
return `${p1}{lang('${pairs[trimmed]}')}${p3}`;
} else {
return match;
}
},
});
// eslint-disable-next-line no-console
console.log('Cool!');
})();
interface ApiLangString {
key: string;
value?: string;
zeroValue?: string;
oneValue?: string;
twoValue?: string;
fewValue?: string;
manyValue?: string;
otherValue?: string;
}
type ApiLangPack = Record<string, ApiLangString>;
type ByValue = Record<string, string[]>;
type Pairs = Record<string, string>;
function preparePairs() {
// Get key arrays by values (only for single-value strings)
const byValue = Object.values(langPackIOs as ApiLangPack).reduce((acc, { value, key }) => {
if (value) {
value = value.toLowerCase();
if (!acc[value]) {
acc[value] = [key];
} else {
acc[value].push(key);
}
}
return acc;
}, {} as ByValue);
// Select the shortest key for each value
return Object.keys(byValue).reduce((acc, value) => {
const keys = byValue[value];
const shortestKeyLength = Math.min(...keys.map((k) => k.length));
acc[value] = keys.find((k) => k.length === shortestKeyLength)!;
return acc;
}, {} as Pairs);
}
function preparePairs2() {
return Object.keys(langPackExtra).reduce((acc, key) => {
acc[langPackExtra[key].toLowerCase()] = key;
return acc;
}, {} as Pairs);
}

View File

@ -1,84 +0,0 @@
// @ts-nocheck
const replaceInFile = require('replace-in-file');
const langPackAndroid = require('./en-android.json');
const langPackIOs = require('./en-ios.json');
const langPackExtra = require('./en-extra.json');
interface ApiLangString {
key: string;
value?: string;
zeroValue?: string;
oneValue?: string;
twoValue?: string;
fewValue?: string;
manyValue?: string;
otherValue?: string;
}
type ApiLangPack = Record<string, ApiLangString>;
type ByValue = Record<string, string[]>;
type Pairs = Record<string, string>;
(async () => {
const androidByKey = prepareAndroidByKey();
const extraByKey = langPackExtra;
const newKeysByValue = prepareByValue(langPackIOs);
await replaceInFile({
files: ['./src/components/**/*.tsx', './src/modules/**/*.ts'],
from: [
/(?:lang|getTranslation)\('(\w+)'\)/g,
],
to: (match: string, key: string) => {
if (extraByKey[key]) {
return match;
}
const value = androidByKey[key];
const newKey = newKeysByValue[value.toLowerCase()];
if (!newKey) {
return match;
}
return match.replace(key, newKey);
},
});
// eslint-disable-next-line no-console
console.log('Cool!');
})();
function prepareAndroidByKey() {
// Select the shortest key for each value
return Object.keys(langPackAndroid).reduce((acc, key) => {
acc[key] = langPackAndroid[key].value;
return acc;
}, {} as Pairs);
}
function prepareByValue(langPack: ApiLangPack) {
// Get key arrays by values (only for single-value strings)
const byValue = Object.values(langPack).reduce((acc, { value, key }) => {
if (value) {
value = value.toLowerCase();
if (!acc[value]) {
acc[value] = [key];
} else {
acc[value].push(key);
}
}
return acc;
}, {} as ByValue);
// Select the shortest key for each value
return Object.keys(byValue).reduce((acc, value) => {
const keys = byValue[value];
const shortestKeyLength = Math.min(...keys.map((k) => k.length));
acc[value] = keys.find((k) => k.length === shortestKeyLength)!;
return acc;
}, {} as Pairs);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,102 +0,0 @@
{
"ActionSendTTLPhoto": "un1 sent a self-destructing photo. Please view it on your mobile",
"ActionSendTTLVideo": "un1 sent a self-destructing video. Please view it on your mobile",
"ActionYouSendTTLPhoto": "You sent a self-destructing photo. Please view it on your mobile",
"ActionYouSendTTLVideo": "You sent a self-destructing video. Please view it on your mobile",
"Activity": "Activity",
"AnimalsNature": "Animals & Nature",
"AppName": "Telegram",
"Archived": "Archived",
"BioAbout": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco.",
"ChannelInfo": "Channel Info",
"ChatInfo": "Chat Info",
"ChatsAndContacts": "Chats and contacts",
"ChooseDefaultSkinTone": "Choose your default skin tone",
"Connecting": "Connecting...",
"ConnectingToProxy": "Connecting to proxy...",
"ContactJoinedDisabled": "Disabled",
"ContactJoinedEnabled": "Enabled",
"ContinueOnThisLanguage": "Continue in English",
"CopyMessageLink": "Copy Message Link",
"Country": "Country",
"Custom": "Custom",
"DeletedMessage": "Deleted message",
"DragToReposition": "Drag to Reposition",
"EditProfile": "Edit Profile",
"Emoji": "EMOJI",
"EnterFullScreen": "Enter full screen",
"EnterPassword": "Enter a Password",
"ExitFullScreen": "Exit full screen",
"FilterChooseChats": "Please choose at least one chat for this folder.",
"FilterCreateError": "Sorry, you can't add more than 10 folders.",
"Flags": "Flags",
"FoodDrink": "Food & Drink",
"GeneralSettings": "General Settings",
"GoToMessage": "Go to message",
"InputDevice": "Input Device",
"InvalidPassword": "Invalid password. Please check the password and try again.",
"InvalidPhoneNumber": "Invalid phone number. Please check the number and try again.",
"KeepMeSignedIn": "Keep me signed in",
"Keyboard": "Keyboard",
"LeftChannel": "Left channel",
"LeftGroup": "Left group",
"Loading": "Loading...",
"LogInByPhone": "Or log in by using your phone number",
"LogInViaQR": "Quick log in using QR code",
"Microphone": "Microphone",
"More": "More",
"Next": "Next",
"NewLineByEnter": "New line by Enter",
"NewLineByShiftEnter": "New line by Shift + Enter",
"NewVersionText": "New and improved version of Telegram is ready for use.",
"NewVersionTitle": "Update available",
"NotEmojiFound": "No Emoji Found",
"NotificationsDisabled": "Disabled",
"NotificationsEnabled": "Enabled",
"Objects": "Objects",
"OutputDevice": "Output Device",
"PaymentsNotSupported": "Sorry, this Telegram client doesn't support payments yet. Please use one of our mobile apps to do this.",
"PhoneNumber": "Phone Number",
"PhotosTitle": "Photos",
"PollQuizOneRightAnswer": "Quiz has only one right answer.",
"PreviewDisabled": "Disabled",
"PreviewEnabled": "Enabled",
"QRHint": "1. Open Telegram on your phone\n2. Go to Settings > Devices > Scan QR\n3. Scan this image to Log in",
"Recent": "Frequently Used",
"RecordDeniedDescription": "You must allow your browser to access your microphone before being able to record voice notes. Click on the padlock icon next to the URL and then make sure you click Allow in the microphone settings to enable Telegram to access your microphone.",
"RecordDeniedTitle": "Permission Denied",
"Saved": "Saved",
"Search": "Search",
"SearchMessagesIn": "Search messages in",
"SearchResults": "Search Results",
"SelectChatToStartMessaging": "Please select a chat to start messaging",
"SendAsFile": "Send as a file",
"SendAsPhoto": "Send as a photo",
"SendByCommandEnter": "Send by Cmd + Enter",
"SendByControlEnter": "Send by Ctrl + Enter",
"SendByEnter": "Send by Enter",
"SendFileConfirmation": "Are you sure you want to send file?",
"SendFilesConfirmation": "Are you sure you want to send files?",
"SendMessage": "Send Message",
"SignInToTelegram": "Sign in to Telegram",
"SmileysPeople": "Smileys & People",
"StartScreenSharing": "Start screen sharing",
"StartText": "Please confirm your country code and enter your phone number.",
"Stickers": "STICKERS",
"StopScreenSharing": "Stop screen sharing",
"Symbols": "Symbols",
"Text": "Text",
"TravelPlaces": "Travel & Places",
"UnpinAllMessagesAlert": "Do you want to unpin all messages?",
"UpdateDraftConfirmation": "Are you sure you want to update draft?",
"Updating": "Updating...",
"VideosTitle": "Videos",
"ViewChannelInfo": "View channel info",
"ViewGroupInfo": "View group info",
"ViewProfile": "View profile",
"VoiceTitle": "Voice messages",
"WaitingForNetwork": "Waiting for network...",
"WriteChatCant": "Sorry, no way to write here :(",
"YourAccountProtectedWithPassword": "Your account is protected with an additional password.",
"YourPhone": "Your Phone"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

121
dev/telegraphChangelog.js Normal file
View File

@ -0,0 +1,121 @@
const Telegraph = require('telegraph-node');
const { JSDOM } = require('jsdom');
const { gitlogPromise } = require('gitlog');
// CONSTANTS
const AUTH_TOKEN = process.env.TELEGRAPH_TOKEN;
const version = require('../package.json').version;
const gitOptions = {
repo: '.',
branch: 'master',
number: 100,
fields: ['hash', 'subject', 'committerDate'],
};
const pageTemplate = `
<body>\
<h3>Commits since ${version}</h3>\
<blockquote>This list is automatically updated when a new commit pushed to the beta repo</blockquote>\
<ul id="list"></ul>\
</body>
`.trim();
// MAIN
function updateChangelog() {
preparePage().then((dom) => {
updateTelegraph(dom);
});
}
updateChangelog();
// UTIL
async function updateTelegraph(dom) {
const api = new Telegraph();
const content = domToNode(dom.window.document.body).children;
const result = await api.editPage(AUTH_TOKEN, 'WebZ-Beta-04-01', 'Telegram WebZ Beta Changelog', content, {
author_name: 'WebZ team',
author_url: 'https://t.me/webztalks',
});
// eslint-disable-next-line no-console
console.log(result);
}
async function preparePage() {
const dom = new JSDOM(pageTemplate);
const commits = await getCommitsSince(version);
commits.forEach((commit) => (
dom.window.document.getElementById('list').appendChild(renderCommit(dom, commit))
));
if (!commits?.length) {
const li = dom.window.document.createElement('li');
li.innerHTML = `<p>Nothing changed since ${version}</p>`;
dom.window.document.getElementById('list').appendChild(li);
}
return dom;
}
function renderCommit(dom, commit) {
const li = dom.window.document.createElement('li');
const subject = commit.subject.replaceAll(/`(.+)`/g, '<code>$1</code>');
li.innerHTML = `<p><code>${commit.hash.substring(0, 7)}</code> <b>${subject}</b></p>`;
return li;
}
function getCommits() {
return gitlogPromise(gitOptions).then((log) => {
return log.map((commit) => {
return {
hash: commit.hash,
subject: commit.subject,
date: new Date(commit.committerDate),
};
});
});
}
async function getCommitsSince(semver) {
const commits = await getCommits();
const versionCommit = commits.find((commit) => commit.subject === semver);
if (!versionCommit) {
return [];
}
return commits.filter(({ date }) => date > versionCommit.date);
}
function domToNode(domNode) {
if (domNode.nodeType === domNode.TEXT_NODE) {
return domNode.data;
}
if (domNode.nodeType !== domNode.ELEMENT_NODE) {
return false;
}
const nodeElement = {};
nodeElement.tag = domNode.tagName.toLowerCase();
for (let i = 0; i < domNode.attributes.length; i++) {
const attr = domNode.attributes[i];
if (attr.name === 'href' || attr.name === 'src') {
if (!nodeElement.attrs) {
nodeElement.attrs = {};
}
nodeElement.attrs[attr.name] = attr.value;
}
}
if (domNode.childNodes.length > 0) {
nodeElement.children = [];
for (let i = 0; i < domNode.childNodes.length; i++) {
const child = domNode.childNodes[i];
nodeElement.children.push(domToNode(child));
}
}
return nodeElement;
}

View File

@ -1,4 +1,5 @@
const stylelint = require('stylelint'); const stylelint = require('stylelint');
// eslint-disable-next-line import/no-unresolved -- TS BUG
const postcss = require('postcss'); const postcss = require('postcss');
const ruleName = 'plugin/whole-pixel'; const ruleName = 'plugin/whole-pixel';
@ -39,11 +40,13 @@ module.exports = stylelint.createPlugin(ruleName, (primaryOption, secondaryOptio
const isValid = (value, unit) => { const isValid = (value, unit) => {
if (unit === 'px') return Number.isInteger(value); if (unit === 'px') return Number.isInteger(value);
if (unit === 'rem') return Number.isInteger(value * pxPerRem); if (unit === 'rem') return Number.isInteger(value * pxPerRem);
return false;
}; };
const suggestFix = (value, unit) => { const suggestFix = (value, unit) => {
if (unit === 'px') return `${Math.round(value)}px`; if (unit === 'px') return `${Math.round(value)}px`;
if (unit === 'rem') return `${Math.round(value * pxPerRem) / pxPerRem}rem`; if (unit === 'rem') return `${Math.round(value * pxPerRem) / pxPerRem}rem`;
return undefined;
}; };
const handleValue = (decl, value) => { const handleValue = (decl, value) => {
@ -69,7 +72,7 @@ module.exports = stylelint.createPlugin(ruleName, (primaryOption, secondaryOptio
} }
}; };
root.walkDecls(decl => { root.walkDecls((decl) => {
if (!decl.value || ignoreList.includes(decl.prop)) return; if (!decl.value || ignoreList.includes(decl.prop)) return;
const values = postcss.list.space(decl.value); const values = postcss.list.space(decl.value);
if (!values?.length) return; if (!values?.length) return;

791
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"deploy:production": "npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push", "deploy:production": "npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push",
"print_version": "node -p -e \"require('./package.json').version\"", "print_version": "node -p -e \"require('./package.json').version\"",
"inc_version": "echo $((`cat .patch-version` + 1)) > .patch-version && echo \"$(node -p -e \"require('./package.json').version.match(/^\\d+\\.\\d+/)[0]\").$(cat .patch-version)\"", "inc_version": "echo $((`cat .patch-version` + 1)) > .patch-version && echo \"$(node -p -e \"require('./package.json').version.match(/^\\d+\\.\\d+/)[0]\").$(cat .patch-version)\"",
"telegraph:update_changelog": "node ./dev/telegraphChangelog.js",
"check": "tsc && stylelint \"**/*.{css,scss}\" && eslint . --ext .ts,.tsx --ignore-pattern src/lib/gramjs", "check": "tsc && stylelint \"**/*.{css,scss}\" && eslint . --ext .ts,.tsx --ignore-pattern src/lib/gramjs",
"check:fix": "npm run check -- --fix", "check:fix": "npm run check -- --fix",
"gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js", "gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js",
@ -77,10 +78,12 @@
"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",
"gitlog": "^4.0.4",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"jest": "^27.5.1", "jest": "^27.5.1",
"jest-raw-loader": "^1.0.1", "jest-raw-loader": "^1.0.1",
"jsdom": "^19.0.0",
"lint-staged": "^12.3.5", "lint-staged": "^12.3.5",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
"postcss-loader": "^6.2.1", "postcss-loader": "^6.2.1",
@ -95,6 +98,7 @@
"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",
"telegraph-node": "^1.0.4",
"typescript": "^4.6.2", "typescript": "^4.6.2",
"webpack": "^5.70.0", "webpack": "^5.70.0",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",

View File

@ -8,7 +8,7 @@ import { ApiChat } from '../../../api/types';
import { GlobalState } from '../../../global/types'; import { GlobalState } from '../../../global/types';
import { import {
ANIMATION_LEVEL_MAX, APP_NAME, APP_VERSION, DEBUG, FEEDBACK_URL, ANIMATION_LEVEL_MAX, APP_NAME, APP_VERSION, BETA_CHANGELOG_URL, BETA_DISCUSSION_CHAT, DEBUG, FEEDBACK_URL, IS_BETA,
} from '../../../config'; } from '../../../config';
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment'; import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
@ -93,6 +93,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
setGlobalSearchDate, setGlobalSearchDate,
setSettingOption, setSettingOption,
setGlobalSearchChatId, setGlobalSearchChatId,
openChatByUsername,
} = getActions(); } = getActions();
const lang = useLang(); const lang = useLang();
@ -169,6 +170,14 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
switchTheme(newTheme, animationLevel === ANIMATION_LEVEL_MAX); switchTheme(newTheme, animationLevel === ANIMATION_LEVEL_MAX);
}, [animationLevel, setSettingOption, theme]); }, [animationLevel, setSettingOption, theme]);
const handleChangelogClick = useCallback(() => {
window.open(BETA_CHANGELOG_URL, '_blank');
}, []);
const handleDiscussionClick = useCallback(() => {
openChatByUsername({ username: BETA_DISCUSSION_CHAT });
}, [openChatByUsername]);
const handleSwitchToWebK = useCallback(() => { const handleSwitchToWebK = useCallback(() => {
setPermanentWebVersion('K'); setPermanentWebVersion('K');
clearWebsync(); clearWebsync();
@ -189,12 +198,14 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
? lang('SearchFriends') ? lang('SearchFriends')
: lang('Search'); : lang('Search');
const versionString = IS_BETA ? `${APP_VERSION} Beta (${APP_REVISION})` : (DEBUG ? APP_REVISION : APP_VERSION);
return ( return (
<div className="LeftMainHeader"> <div className="LeftMainHeader">
<div id="LeftMainHeader" className="left-header"> <div id="LeftMainHeader" className="left-header">
<DropdownMenu <DropdownMenu
trigger={MainButton} trigger={MainButton}
footer={`${APP_NAME} ${DEBUG ? APP_REVISION : APP_VERSION}`} footer={`${APP_NAME} ${versionString}`}
> >
<MenuItem <MenuItem
icon="saved-messages" icon="saved-messages"
@ -247,6 +258,22 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
> >
Report Bug Report Bug
</MenuItem> </MenuItem>
{IS_BETA && (
<>
<MenuItem
icon="permissions"
onClick={handleChangelogClick}
>
Beta Changelog
</MenuItem>
<MenuItem
icon="comments"
onClick={handleDiscussionClick}
>
Beta Discussion (ru)
</MenuItem>
</>
)}
{withOtherVersions && ( {withOtherVersions && (
<> <>
<MenuItem <MenuItem

View File

@ -6,6 +6,10 @@ export const DEBUG_MORE = false;
export const IS_TEST = process.env.APP_ENV === 'test'; export const IS_TEST = process.env.APP_ENV === 'test';
export const IS_PERF = process.env.APP_ENV === 'perf'; export const IS_PERF = process.env.APP_ENV === 'perf';
export const IS_BETA = process.env.APP_ENV === 'staging';
export const BETA_CHANGELOG_URL = 'https://telegra.ph/WebZ-Beta-04-01';
export const BETA_DISCUSSION_CHAT = 'webzcommentsru';
export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.'; export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.';
export const DEBUG_GRAMJS = false; export const DEBUG_GRAMJS = false;

View File

@ -25,5 +25,6 @@
"src", "src",
"tests", "tests",
"plugins", "plugins",
"dev",
] ]
} }

View File

@ -137,7 +137,8 @@ module.exports = (env = {}, argv = {}) => {
new DefinePlugin({ new DefinePlugin({
APP_REVISION: DefinePlugin.runtimeValue(() => { APP_REVISION: DefinePlugin.runtimeValue(() => {
const { branch, commit } = getGitMetadata(); const { branch, commit } = getGitMetadata();
return JSON.stringify((!branch || branch === 'HEAD') ? commit : branch); const shouldDisplayCommit = process.env.APP_ENV === 'staging' || !branch || branch === 'HEAD';
return JSON.stringify(shouldDisplayCommit ? commit : branch);
}, argv.mode === 'development' ? true : []), }, argv.mode === 'development' ? true : []),
}), }),
new ProvidePlugin({ new ProvidePlugin({