Browse Source

Fix conflicting placeholder class name

master
Eduard Kuzmenko 3 years ago
parent
commit
c3fcca8af9
  1. 15
      src/components/chat/bubbles.ts
  2. 2
      src/config/app.ts
  3. 9
      src/helpers/dom/renderImageFromUrl.ts
  4. 7
      src/lang.ts
  5. 6
      src/lib/appManagers/appAvatarsManager.ts
  6. 90
      src/lib/appManagers/appDialogsManager.ts
  7. 6
      src/lib/appManagers/appPhotosManager.ts
  8. 35
      src/lib/langPack.ts
  9. 22
      src/scss/partials/_chatBubble.scss
  10. 21
      src/scss/partials/_leftSidebar.scss

15
src/components/chat/bubbles.ts

@ -3148,14 +3148,15 @@ export default class ChatBubbles { @@ -3148,14 +3148,15 @@ export default class ChatBubbles {
}
private renderEmptyPlaceholder(type: 'group' | 'saved' | 'noMessages' | 'noScheduledMessages' | 'greeting', bubble: HTMLElement, message: any, elements: (Node | string)[]) {
bubble.classList.add('empty-placeholder', 'empty-placeholder-' + type);
const BASE_CLASS = 'empty-bubble-placeholder';
bubble.classList.add(BASE_CLASS, BASE_CLASS + '-' + type);
let title: HTMLElement;
if(type === 'group') title = i18n('GroupEmptyTitle1');
else if(type === 'saved') title = i18n('ChatYourSelfTitle');
else if(type === 'noMessages' || type === 'greeting') title = i18n('NoMessages');
else if(type === 'noScheduledMessages') title = i18n('NoScheduledMessages');
title.classList.add('center', 'empty-placeholder-title');
title.classList.add('center', BASE_CLASS + '-title');
elements.push(title);
@ -3177,12 +3178,12 @@ export default class ChatBubbles { @@ -3177,12 +3178,12 @@ export default class ChatBubbles {
];
} else if(type === 'greeting') {
const subtitle = i18n('NoMessagesGreetingsDescription');
subtitle.classList.add('center', 'empty-placeholder-subtitle');
subtitle.classList.add('center', BASE_CLASS + '-subtitle');
this.messagesQueue.findAndSplice(q => q.bubble === bubble);
const stickerDiv = document.createElement('div');
stickerDiv.classList.add('empty-placeholder-sticker');
stickerDiv.classList.add(BASE_CLASS + '-sticker');
const middleware = this.getMiddleware();
@ -3221,7 +3222,7 @@ export default class ChatBubbles { @@ -3221,7 +3222,7 @@ export default class ChatBubbles {
elements.push(
...listElements.map(elem => {
const span = document.createElement('span');
span.classList.add('empty-placeholder-list-item');
span.classList.add(BASE_CLASS + '-list-item');
span.append(elem);
return span;
})
@ -3236,7 +3237,7 @@ export default class ChatBubbles { @@ -3236,7 +3237,7 @@ export default class ChatBubbles {
} else if(type === 'saved') {
listElements.forEach(elem => {
const i = document.createElement('span');
i.classList.add('empty-placeholder-list-bullet');
i.classList.add(BASE_CLASS + '-list-bullet');
i.innerText = '•';
elem.prepend(i);
});
@ -3247,7 +3248,7 @@ export default class ChatBubbles { @@ -3247,7 +3248,7 @@ export default class ChatBubbles {
bubble.classList.add('has-description');
}
elements.forEach((element: any) => element.classList.add('empty-placeholder-line'));
elements.forEach((element: any) => element.classList.add(BASE_CLASS + '-line'));
}
private processLocalMessageRender(message: Message.message | Message.messageService) {

2
src/config/app.ts

@ -17,7 +17,7 @@ const App = { @@ -17,7 +17,7 @@ const App = {
id: 1025907,
hash: '452b0359b988148995f22ff0f4229750',
version: '0.6.1',
langPackVersion: '0.3.1',
langPackVersion: '0.3.2',
langPack: 'macos',
langPackCode: 'en',
domains: [MAIN_DOMAIN] as string[],

9
src/helpers/dom/renderImageFromUrl.ts

@ -12,7 +12,8 @@ const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoE @@ -12,7 +12,8 @@ const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoE
};
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
export default async function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void, useCache = true) {
export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement,
url: string, callback?: (err?: Event) => void, useCache = true) {
if(!url) {
console.error('renderImageFromUrl: no url?', elem, url);
callback && callback();
@ -54,3 +55,9 @@ export default async function renderImageFromUrl(elem: HTMLElement | HTMLImageEl @@ -54,3 +55,9 @@ export default async function renderImageFromUrl(elem: HTMLElement | HTMLImageEl
}
}
}
export function renderImageFromUrlPromise(elem: Parameters<typeof renderImageFromUrl>[0], url: string, useCache?: boolean) {
return new Promise((resolve) => {
renderImageFromUrl(elem, url, resolve, useCache);
});
}

7
src/lang.ts

@ -43,6 +43,9 @@ const lang = { @@ -43,6 +43,9 @@ const lang = {
},
"Chat.Search.NoMessagesFound": "No messages found",
"Chat.Search.PrivateSearch": "Private Search",
"ChatList.Main.EmptyPlaceholder.Title": "Your chats will appear here",
"ChatList.Main.EmptyPlaceholder.Subtitle": "You have %s on Telegram",
"ChatList.Main.EmptyPlaceholder.SubtitleNoContacts": "Use Telegram app on your [Android](https://telegram.org/android) or [iOS](https://telegram.org/dl/ios) device to sync your contacts",
//"ChatList.Menu.Archived": "Archived",
"ChatList.Menu.SwitchTo.Webogram": "Switch to Old Version",
"ChatList.Menu.SwitchTo.Z": "Switch to Z version",
@ -54,6 +57,10 @@ const lang = { @@ -54,6 +57,10 @@ const lang = {
"ConnectionStatus.Reconnecting": "Reconnecting...",
"ConnectionStatus.TimedOut": "Request timed out, %s",
"ConnectionStatus.Waiting": "Waiting for network...",
"Contacts.Count": {
"one_value": "%d contact",
"other_value": "%d contacts",
},
"Deactivated.Title": "Too many tabs...",
"Deactivated.Subtitle": "Telegram supports only one active tab with the app.\nClick anywhere to continue using this tab.",
/* "Drafts": {

6
src/lib/appManagers/appAvatarsManager.ts

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
import renderImageFromUrl, { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import replaceContent from "../../helpers/dom/replaceContent";
import sequentialDom from "../../helpers/sequentialDom";
import { UserProfilePhoto, ChatPhoto, InputFileLocation } from "../../layer";
@ -103,7 +103,7 @@ export class AppAvatarsManager { @@ -103,7 +103,7 @@ export class AppAvatarsManager {
thumbImage.classList.add('avatar-photo', 'avatar-photo-thumbnail');
img.classList.add('avatar-photo');
const url = appPhotosManager.getPreviewURLFromBytes(photo.stripped_thumb);
renderThumbPromise = renderImageFromUrl(thumbImage, url).then(() => {
renderThumbPromise = renderImageFromUrlPromise(thumbImage, url).then(() => {
replaceContent(div, thumbImage);
});
}
@ -134,7 +134,7 @@ export class AppAvatarsManager { @@ -134,7 +134,7 @@ export class AppAvatarsManager {
}
const renderPromise = loadPromise
.then((url) => renderImageFromUrl(img, url/* , false */))
.then((url) => renderImageFromUrlPromise(img, url/* , false */))
.then(() => callback());
return {cached, loadPromise: renderThumbPromise || renderPromise};

90
src/lib/appManagers/appDialogsManager.ts

@ -29,7 +29,7 @@ import appDraftsManager, { MyDraftMessage } from "./appDraftsManager"; @@ -29,7 +29,7 @@ import appDraftsManager, { MyDraftMessage } from "./appDraftsManager";
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
import appNotificationsManager from "./appNotificationsManager";
import PeerTitle from "../../components/peerTitle";
import { i18n, LangPackKey, _i18n } from "../langPack";
import I18n, { FormatterArguments, i18n, LangPackKey, _i18n } from "../langPack";
import findUpTag from "../../helpers/dom/findUpTag";
import { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
import lottieLoader from "../lottieLoader";
@ -41,6 +41,8 @@ import positionElementByIndex from "../../helpers/dom/positionElementByIndex"; @@ -41,6 +41,8 @@ import positionElementByIndex from "../../helpers/dom/positionElementByIndex";
import replaceContent from "../../helpers/dom/replaceContent";
import ConnectionStatusComponent from "../../components/connectionStatus";
import appChatsManager from "./appChatsManager";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import { fastRafPromise } from "../../helpers/schedulers";
export type DialogDom = {
avatarEl: AvatarElement,
@ -754,12 +756,13 @@ export class AppDialogsManager { @@ -754,12 +756,13 @@ export class AppDialogsManager {
private generateEmptyPlaceholder(options: {
title: LangPackKey,
subtitle: LangPackKey,
subtitle?: LangPackKey,
subtitleArgs?: FormatterArguments,
classNameType: string
}) {
const BASE_CLASS = 'empty-placeholder';
const div = document.createElement('div');
div.classList.add(BASE_CLASS, BASE_CLASS + '-' + options.classNameType);
const container = document.createElement('div');
container.classList.add(BASE_CLASS, BASE_CLASS + '-' + options.classNameType);
const header = document.createElement('div');
header.classList.add(BASE_CLASS + '-header');
@ -767,31 +770,82 @@ export class AppDialogsManager { @@ -767,31 +770,82 @@ export class AppDialogsManager {
const subtitle = document.createElement('div');
subtitle.classList.add(BASE_CLASS + '-subtitle');
_i18n(subtitle, options.subtitle);
if(options.subtitle) {
_i18n(subtitle, options.subtitle, options.subtitleArgs);
}
div.append(header, subtitle);
container.append(header, subtitle);
return div;
return {container, header, subtitle};
}
private onListLengthChange = () => {
//return;
if(this.filterId === 1) {
return;
}
if(this.filterId < 2) return;
let placeholderContainer = (Array.from(this.chatList.parentElement.children) as HTMLElement[]).find(el => el.matches('.empty-placeholder'));
const needPlaceholder = this.scroll.loadedAll.bottom && !this.chatList.childElementCount/* || true */;
// this.chatList.style.display = 'none';
const emptyPlaceholder = this.chatList.parentElement.querySelector('.empty-placeholder');
if(this.scroll.loadedAll.bottom && !this.chatList.childElementCount) {
if(emptyPlaceholder) {
return;
if(needPlaceholder && placeholderContainer) {
return;
} else if(!needPlaceholder) {
if(placeholderContainer) {
placeholderContainer.remove();
}
const d = this.generateEmptyPlaceholder({
return;
}
let placeholder: ReturnType<AppDialogsManager['generateEmptyPlaceholder']>;
if(!this.filterId) {
placeholder = this.generateEmptyPlaceholder({
title: 'ChatList.Main.EmptyPlaceholder.Title',
classNameType: 'dialogs'
});
placeholderContainer = placeholder.container;
const img = document.createElement('img');
img.classList.add('empty-placeholder-dialogs-icon');
Promise.all([
appUsersManager.getContacts().then(users => {
let key: LangPackKey, args: FormatterArguments;
if(users.length) {
key = 'ChatList.Main.EmptyPlaceholder.Subtitle';
args = [i18n('Contacts.Count', [users.length])];
} else {
key = 'ChatList.Main.EmptyPlaceholder.SubtitleNoContacts';
args = [];
}
const subtitleEl = new I18n.IntlElement({
key,
args,
element: placeholder.subtitle
});
}),
renderImageFromUrlPromise(img, 'assets/img/EmptyChats.svg'),
fastRafPromise()
]).then(() => {
placeholderContainer.classList.add('visible');
});
placeholderContainer.prepend(img);
} else {
placeholder = this.generateEmptyPlaceholder({
title: 'FilterNoChatsToDisplay',
subtitle: 'FilterNoChatsToDisplayInfo',
classNameType: 'folder'
});
d.prepend(wrapLocalSticker({
placeholderContainer = placeholder.container;
placeholderContainer.prepend(wrapLocalSticker({
emoji: '📂',
width: 128,
height: 128
@ -806,12 +860,10 @@ export class AppDialogsManager { @@ -806,12 +860,10 @@ export class AppDialogsManager {
new AppEditFolderTab(appSidebarLeft).open(appMessagesManager.filtersStorage.filters[this.filterId]);
});
d.append(button);
this.chatList.parentElement.append(d);
} else if(emptyPlaceholder) {
emptyPlaceholder.remove();
placeholderContainer.append(button);
}
this.chatList.parentElement.append(placeholderContainer);
};
public onChatsRegularScroll = () => {

6
src/lib/appManagers/appPhotosManager.ts

@ -23,7 +23,7 @@ import appDownloadManager, { ThumbCache } from "./appDownloadManager"; @@ -23,7 +23,7 @@ import appDownloadManager, { ThumbCache } from "./appDownloadManager";
import appUsersManager from "./appUsersManager";
import blur from "../../helpers/blur";
import { MOUNT_CLASS_TO } from "../../config/debug";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import calcImageInBox from "../../helpers/calcImageInBox";
import { makeMediaSize, MediaSize } from "../../helpers/mediaSizes";
@ -208,9 +208,7 @@ export class AppPhotosManager { @@ -208,9 +208,7 @@ export class AppPhotosManager {
image.classList.add('thumbnail');
const loadPromise = (useBlur ? blur(url) : Promise.resolve(url)).then(url => {
return new Promise<any>((resolve) => {
renderImageFromUrl(image, url, resolve);
});
return renderImageFromUrlPromise(image, url);
});
return {image, loadPromise};

35
src/lib/langPack.ts

@ -14,6 +14,7 @@ import apiManager from "./mtproto/mtprotoworker"; @@ -14,6 +14,7 @@ import apiManager from "./mtproto/mtprotoworker";
import stateStorage from "./stateStorage";
import App from "../config/app";
import rootScope from "./rootScope";
import RichTextProcessor from "./richtextprocessor";
export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionChatCreate": "ActionCreateGroup",
@ -59,6 +60,9 @@ export const langPack: {[actionType: string]: LangPackKey} = { @@ -59,6 +60,9 @@ export const langPack: {[actionType: string]: LangPackKey} = {
export type LangPackKey = /* string | */keyof typeof lang | keyof typeof langSign;
export type FormatterArgument = string | Node;
export type FormatterArguments = FormatterArgument[];
namespace I18n {
export const strings: Map<LangPackKey, LangPackString> = new Map();
let pluralRules: Intl.PluralRules;
@ -230,12 +234,12 @@ namespace I18n { @@ -230,12 +234,12 @@ namespace I18n {
});
}
export function superFormatter(input: string, args?: any[], indexHolder = {i: 0}) {
let out: (string | HTMLElement)[] = [];
const regExp = /(\*\*)(.+?)\1|(\n)|un\d|%\d\$.|%./g;
export function superFormatter(input: string, args?: FormatterArguments, indexHolder = {i: 0}) {
let out: FormatterArguments = [];
const regExp = /(\*\*)(.+?)\1|(\n)|(\[.+?\]\(.*?\))|un\d|%\d\$.|%./g;
let lastIndex = 0;
input.replace(regExp, (match, p1: any, p2: any, p3: any, offset: number, string: string) => {
input.replace(regExp, (match, p1: any, p2: any, p3: any, p4: string, offset: number, string: string) => {
//console.table({match, p1, p2, offset, string});
out.push(string.slice(lastIndex, offset));
@ -252,6 +256,23 @@ namespace I18n { @@ -252,6 +256,23 @@ namespace I18n {
}
} else if(p3) {
out.push(document.createElement('br'));
} else if(p4) {
const a = document.createElement('a');
const idx = p4.lastIndexOf(']');
const text = p4.slice(1, idx);
a.append(...superFormatter(text, args, indexHolder));
const url = p4.slice(idx + 2, p4.length - 1);
if(url) {
const wrappedUrl = RichTextProcessor.wrapUrl(url);
a.href = wrappedUrl.url;
if(wrappedUrl.onclick) a.setAttribute('onclick', wrappedUrl.onclick);
a.target = '_blank';
}
out.push(a);
console.log('p4', p4);
} else if(args) {
out.push(args[indexHolder.i++]);
}
@ -268,8 +289,8 @@ namespace I18n { @@ -268,8 +289,8 @@ namespace I18n {
}
export function format(key: LangPackKey, plain: true, args?: any[]): string;
export function format(key: LangPackKey, plain?: false, args?: any[]): (string | HTMLElement)[];
export function format(key: LangPackKey, plain = false, args?: any[]): (string | HTMLElement)[] | string {
export function format(key: LangPackKey, plain?: false, args?: any[]): FormatterArguments;
export function format(key: LangPackKey, plain = false, args?: any[]): FormatterArguments | string {
const str = strings.get(key);
let input: string;
if(str) {
@ -329,7 +350,7 @@ namespace I18n { @@ -329,7 +350,7 @@ namespace I18n {
export type IntlElementOptions = IntlElementBaseOptions & {
key: LangPackKey,
args?: any[]
args?: FormatterArguments
};
export class IntlElement extends IntlElementBase<IntlElementOptions> {
public key: IntlElementOptions['key'];

22
src/scss/partials/_chatBubble.scss

@ -733,7 +733,7 @@ $bubble-margin: .25rem; @@ -733,7 +733,7 @@ $bubble-margin: .25rem;
max-width: 100%;
}
&.empty-placeholder {
&.empty-bubble-placeholder {
position: absolute;
top: 50%;
left: 50%;
@ -750,7 +750,7 @@ $bubble-margin: .25rem; @@ -750,7 +750,7 @@ $bubble-margin: .25rem;
align-self: center;
}
.empty-placeholder-title {
.empty-bubble-placeholder-title {
font-weight: 500;
font-size: 1rem !important;
}
@ -760,7 +760,11 @@ $bubble-margin: .25rem; @@ -760,7 +760,11 @@ $bubble-margin: .25rem;
}
}
.empty-placeholder-line + .empty-placeholder-line {
.empty-bubble-placeholder-line {
color: #fff;
}
.empty-bubble-placeholder-line + .empty-bubble-placeholder-line {
margin-top: .5rem;
}
@ -771,7 +775,7 @@ $bubble-margin: .25rem; @@ -771,7 +775,7 @@ $bubble-margin: .25rem;
margin-left: -.1875rem;
}
.empty-placeholder-list-bullet {
.empty-bubble-placeholder-list-bullet {
margin-right: .3125rem;
}
@ -784,23 +788,23 @@ $bubble-margin: .25rem; @@ -784,23 +788,23 @@ $bubble-margin: .25rem;
}
}
&.empty-placeholder-group {
.empty-placeholder-list-item {
&.empty-bubble-placeholder-group {
.empty-bubble-placeholder-list-item {
margin-top: .4375rem !important;
}
}
&.empty-placeholder-greeting {
&.empty-bubble-placeholder-greeting {
.service-msg {
max-width: 232px;
}
.empty-placeholder-subtitle {
.empty-bubble-placeholder-subtitle {
margin-top: .25rem !important;
}
}
.empty-placeholder-sticker {
.empty-bubble-placeholder-sticker {
margin-top: .75rem !important;
position: relative;
width: 200px;

21
src/scss/partials/_leftSidebar.scss

@ -1187,6 +1187,9 @@ @@ -1187,6 +1187,9 @@
text-align: center;
line-height: var(--line-height);
user-select: none;
width: 21rem !important;
margin: 0 auto;
padding: 0 1rem;
.media-sticker-wrapper {
width: 128px;
@ -1203,7 +1206,7 @@ @@ -1203,7 +1206,7 @@
&-subtitle {
color: var(--secondary-text-color);
font-size: .875rem;
margin-top: .3125rem;
margin-top: .375rem;
}
.btn-control {
@ -1214,4 +1217,20 @@ @@ -1214,4 +1217,20 @@
margin-right: .625rem;
}
}
&-dialogs {
opacity: 0;
@include animation-level(2) {
transition: opacity .2s ease-in-out;
}
&-icon {
margin-bottom: 1.0625rem;
}
&.visible {
opacity: 1;
}
}
}

Loading…
Cancel
Save